SlideShare une entreprise Scribd logo
1  sur  41
Télécharger pour lire hors ligne
PFDS #11



               10.2.1
               Lists With Efficient Catenation



               @yuga
               2012-11-03

Copyright © 2012 yuga                            1
目次

   Structural Abstractionを用いた1つめの実装例として、
   Catenable List を取り上げます。

        定義: 何を作る
        実装: どう作る
        解析: どんなものなのか




Copyright © 2012 yuga                      2
定義




                        何を作る


Copyright © 2012 yuga          3
実現する操作

   以下の定義を実装したリストを作ります。

   定義(CATENABLELISTシグネチャ):
                module type CATENABLELIST = sig
                  type ‘a t
                  val empty : ‘a t
                  val isEmpty : ‘a t         ->   bool
                  val cons    : ‘a * ‘a t ->      ‘a t
                  val snoc    : ‘a t * ‘a    ->   ‘a t
                  val (++)    : ‘a t -> ‘a t ->   ‘a t
                  val head    : ‘a t         ->   ‘a
                  val tail    : ‘a t         ->   ‘a t
                end


            = Output Restricted Queues + (++)


Copyright © 2012 yuga                                    4
実行時間はO(1)

   CatenableListはすべての操作をO(1)時間で実現します。

        通常のリスト同士の連結操作(++)はO(n)時間かかるのに対し、
         Catenable ListsではO(1)時間で可能になるのが特徴です。

        Persistentな使い方をしても問題ないものにします。




Copyright © 2012 yuga                           5
実行時間はAmortized time

   でも、ごめんなさい。
   Worst-Case timeではなくて、Amortized timeです。

        Worst-CaseなCatenable Listsは11章に出てくるRecursive
         Slow-Downというテクニックを使って実現できます。
               – Persistent Lists with Catenation via Recursive Slow-Down
               – こっちの方が先行して世に登場


        今回扱うCatenable Listsは後発ですが、Worst-Caseなリスト
         より実装が簡単になっています。




Copyright © 2012 yuga                                                       6
実装




                        どう作る


Copyright © 2012 yuga          7
実装のアイデア

   効率の良い連結関数を作るため、リスト内部にqueueを設け
   て、その中に相手リストを格納します。

                    1                    6
                                連結

               2        4
                                ++   7       8
                                                                 ○はリストの
                                                                   要素
               3                             9
                                                                         青枠は
                                                                         queue


                                 1                   6           1


                            2    4               7       8   2   4       6


                            3                            9   3       7       8


     註: 空のqueueを省略しています                                                      9

Copyright © 2012 yuga                                                            8
実装のアイデア

   Queueを新たに実装するのは本節の対象外です。以下の要件
   を満たしていれば何でも良いので、既存のものを利用します。

        QUEUEシグネチャを満たしている
                module type QUEUE = sig
                  type ‘a t
                  val empty : ‘a t
                  val isEmpty : ‘a t        ->   bool
                  val snoc    : ‘a t * ‘a   ->   ‘a t
                  val head    : ‘a t        ->   ‘a
                  val tail    : ‘a t        ->   ‘a t
                end


        すべての操作が Worst-Case / Amortized 関係なくO(1)時間で実行可能
        Persistentな使い方をしても問題ない



Copyright © 2012 yuga                                     9
PFDSに出てきたqueue

   これまでに登場したqueueには以下のものがありました。
                    Section   Name                Cost                   Persistent
                    5.2       BatchedQueue        O(n) worst-case time   NG
                    6.3.2,    BankersQueue        O(1) amortized time    OK
                    8.3
                    6.4.2     PhysicistsQueue     O(1) amortized time    OK
                    7.2       RealTimeQueue       O(1) worst-case time   OK
                    8.3       HoodMelvilleQueue   O(1) worst-case time   OK
                    10.1.3    BootStrappedQueue   O(1) amortized time    OK




   ⇒ BatchedQueue以外なら良さそうです。



Copyright © 2012 yuga                                                                 10
Structural Abstractionテンプレートを使って実装開始

   10.2節に登場したテンプレートを参考に進めます。

       ‘a c : Primitive type                               Queue
       ’a b : Bootstrapped type                            CatenableList
                type ‘a b = E
                          | B of ‘a * ‘a b c

                let unit_b x = B (x, empty_b)

                let insert_b = function
                  | (x, E) -> B (x, empty_c)
                  | (x, B (y, c)) -> B (x, insert_c (unit_b y, c))

                let     join_b   = function
                  |     (b, E)   -> b
                  |     (E, b)   -> b                               _c は ‘a c
                  |     (B (x,   c), b) -> B (x, insert_c (b, c))   _b は ‘a b の関数



Copyright © 2012 yuga                                                               11
実装: empty / isEmpty

   最初に、データ構造として空リストだけ定義します。
   ここではCatenableListの型を t とします。

                module CatenableList : CATENABLELIST = struct
                  type ‘a t = E
                            | …
                  …
                end

   emptyとisEmptyの実装はデータコンストラクタを見るだけです。
               let empty = E

               let isEmpty = function
                 | E -> true
                 | _ -> false



Copyright © 2012 yuga                                           12
実装: ++ rev.1

   QUEUEシグネチャを実装したモジュールをQという名前で受け取
   ります。ここではqueueの型を t として、リストのデータ構造を定
   義します。
                module CatenableList (Q : QUEUE) : CATENABLELIST = struct
                  type ‘a t = E
                            | C of ‘a * ‘a t Q.t
                  …
                end


   (++)は、1つめのリストのqueueに2つめのリストを格納します。

                let     (++) xs ys = match (xs, ys) with
                  |     (E, ys)        -> ys
                  |     (xs, E)        -> xs
                  |     (C (x, q), ys) -> C (x, Q.snoc (q, ys))


Copyright © 2012 yuga                                                       13
実装: ++ rev.2

   4行目は、あとで他の関数からも利用するので、ヘルパー関数link
   にくくりだします。

                module CatenableList (Q : QUEUE) : CATENABLELIST = struct
                  type ‘a t = E
                            | C of ‘a * ‘a t Q.t

                    let link (C (x, q), ys) -> C (x, Q.snoc (q, ys))

                    let   (++) xs ys =   match (xs, ys) with
                      |   (E, ys)   ->   ys
                      |   (xs, E)   ->   xs
                      |   (xs, ys) ->    link (xs, ys)

                  …
                end




Copyright © 2012 yuga                                                       14
実装: cons / snoc

   consとsnocは、さきほど実装した(++)を使えば簡単です。

                let cons (x, xs) = C (x, Q.empty) ++ xs

                let snoc (xs, x) = xs ++ C (x, Q.empty)




Copyright © 2012 yuga                                     15
図解: cons / snoc

   consとsnocをそれぞれ3回繰り返した結果です。
                                                                     3
                        cons               cons           2   cons
                                       1
                                                                     2
                  E                                       1
                                                                     1



             1                     2                  3



                        snoc               snoc               snoc
                               1
                                                  1                      1
       E
                                                  2                  2       3




Copyright © 2012 yuga                                                            16
実装: head

   headは先頭を取り出すだけです。

                exception Empty

                let head = function
                  | E        -> raise Empty
                  | C (x, _) -> x




Copyright © 2012 yuga                         17
実装: tail rev.1

   tailは先頭をすてて、queueの中身をリスト状につなぎなおします。

                exception Empty

                let link (C (x, q), ys) -> C (x, Q.snoc (q, ys))

                let linkAll q =                                        linkは再掲
                  let x = Q.head q in
                  let q’ = Q.tail q in
                  if Q. isEmpty q’ then x else link (t, linkAll q’)

                let tail = function
                  | E        -> raise Empty
                  | C (_, q) -> if Q.isEmpty q then q else linkAll q




Copyright © 2012 yuga                                                            18
図解: tail

   tailによりqueueの中身がつなぎなおされる過程です。
   最初にheadが取り除かれます。


                                    1


                            2       5       6           2       5       6


                        3       4       7       8   3       4       7       8


                                                9                           9




Copyright © 2012 yuga                                                           19
図解: tail

   続いてqueueをほどいていきます。




                            2       5       6           2       5       6


                        3       4       7       8   3       4       7       8


                                                9                           9




Copyright © 2012 yuga                                                           20
図解: tail

   ほどき終わったらリスト状につなぎなおします。



                                                                    5


                            2       5       6           2           6


                        3       4       7       8   3       4   7       8


                                                9                       9




Copyright © 2012 yuga                                                       21
実装: tail rev.2

   tailの完了です。
                            2


                        3   4       5


                                    6


                                7       8


                                        9




Copyright © 2012 yuga                       22
実装: 同じデータを何度もtailしたとき

   しかし、今の実装では、tailするたびに最悪O(n)回linkを実行
   することになり 、CatenableListをpersistentなデータ構造と
   して使うことができません。
                                                               2

                            1                         tail
                                                       O(n)    3


                2       3   4   …   99
                                                               …


                                                               99
                                                 2                     2



                                                 3                     3


                                                 …                     …
      ならし解析が意味をなさない                      tail                 tail
      (参考: 5.6節 The Bad News)             O(n)                  O(n)
                                                 99                    99




Copyright © 2012 yuga                                                       23
実装: tail rev.2

   そこで、linkAllの再帰実行を遅延データに包んで、先頭要素から
   queueをほどく処理を、順次必要になるまで遅延させます。
   これによりtailがincremental関数になります。

                exception Empty

                let link (C (x, q), ys) -> C (x, Q.snoc (q, ys))

                let linkAll q =
                  let lazy t = Q.head q in
                  let q’ = Q.tail q in
                  if Q. isEmpty q’ then t else link (t, lazy (linkAll q’))

                let tail = function
                  | E        -> raise Empty
                  | C (_, q) -> if Q.isEmpty q then q else linkAll q
                                                                         型: ‘a t

                (参考: 6章)                                           型: ‘a t Lazy.t

Copyright © 2012 yuga                                                               24
実装: ++ rev.3

   その結果、queueに格納するデータ型が変化するので修正します。

                module CatenableList (Q : QUEUE) : CATENABLELIST = struct
                  type ‘a t = E
                            | C of ‘a * ‘a t Lazy.t Q.t

                    let link (C (x, q), ys) -> C (x, Q.snoc (q, ys))

                    let   (++) xs ys =   match (xs, ys) with
                      |   (E, ys) ->     ys
                      |   (xs, E)   ->   xs
                      |   (xs, ys) ->    link (xs, lazy ys)

                  …
                end




Copyright © 2012 yuga                                                       25
実装: 完成
           module CatenableList (Q : QUEUE) : CATENABLELIST = struct
             type ‘a t = E
                       | C of ‘a * ‘a t Lazy.t Q.t

             exception Empty

             let empty = E

             let isEmpty = function
               | E -> true
               | _ -> false

             let link (C (x, q), ys) -> C (x, Q.snoc (q, ys))

             let   (++) xs ys =   match (xs, ys) with
               |   (E, ys) ->     ys
               |   (xs, E)   ->   xs
               |   (xs, ys) ->    link (xs, lazy ys)

             let cons (x, xs) = C (x, Q.empty) ++ xs

             let snoc (xs, x) = xs ++ C (x, Q.empty)

             let head = function
               | E        -> raise Empty
               | C (x, _) -> x

             let linkAll q =
               let lazy t = Q.head q in
               let q’ = Q.tail q in
               if Q. isEmpty q’ then t else link (t, lazy (linkAll q’))

                   let tail = function
                      | E        -> raise Empty
                      | C (_, q) -> if Q.isEmpty q then q else linkAll q
                end
Copyright © 2012 yuga              Ocamlで実際に動かしてみたやつ: https://github.com/yuga/readpfds/blob/master/OCaml/catenableList.ml   26
解析




                        どんなものなのか


Copyright © 2012 yuga              27
解析: ならし解析

   実行コストの考え方:

    ++ / cons / snoc / head 関数の実行コストは、実装からあき
     らかにO(1) worst-case timeです。

    tail関数のO(n) worst-case timeな実行コストを、他の関数と
     の間でならして、CatenableListのすべての操作がO(1)
     amortized timeであることを証明します。

    CatenableListのデータ構造に影響を与えるのは ++ / cons /
     snoc / tail 関数ですが、cons / snoc 関数は ++ 関数に依存し
     ています。ならし解析は++ 関数と tail 関数の2つに注目して
     行います。

Copyright © 2012 yuga                              28
解析: Banker’s method

   ならし解析にあたり、Banker’s methodを採用します。

    tail 関数の実行コストはサスペンションとして負債(debits)にし
     linkAll 関数が link 関数を呼ぶときの1番目の引数のノードに割り当
     てます。

    ++ 関数の実行コストもサスペンションとして負債(debits)にし、
     ++ 関数が link 関数を呼ぶときの1番目の引数のノードに割り当て
     ます。

    各Nodeが tail 関数によって取り除かれるとき、そのノードに
     割り当てられたdebitsがすべて支払い済み(残サスペンション
     数=0)であるようにします。


Copyright © 2012 yuga                          29
解析: 定義(ツリー)

   CatenableListのデータ構造を、ノードによって構成され
   たツリーが、階層状になっているものと考えます。

                                                  0




                                      1       2       7        8

                                 𝑡0                       𝑡2       𝑡3

                                  3       4       5       6
                        𝑡

                            𝑡1

Copyright © 2012 yuga                                                   30
解析: 定義(ノード識別子, degree, depth)

   このツリーにラベルと関数を定義します。
                                                                 0
                            root (0 th) node of t

                                                                                                 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 0 = 4
                                                                                                        4th node of t
                                                       1     2        3         4                      𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 4 = 4
                                                       1st 2nd       3 rd                               𝑑𝑒𝑝𝑡ℎ 𝑡 4 = 1
                                                    node of t                                         0 th node of 𝑡1
                                                                                                      𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 𝑗 0 = 4
                                                                 5          6       7        8
                                                              1st                                                  𝑑𝑒𝑝𝑡ℎ 𝑡 8 = 2
                                                           node of 𝑡 𝑗


                                                                                9       10       11   12

                        𝑡                     𝑡𝑗



Copyright © 2012 yuga                                                                                                              31
解析: 各ノードに割り当てるdebitsの考え方

   各ノードに割り当てるdebitsは以下のようになります。

    ならし解析の目的はlinkAllのコストを配分すること




    queueの中の子ノード数 (linkAllのコスト)
       = queueに含まれるサスペンション数
       = そのノードに割り当てるdebits数

    デビット数を表す関数を定義します。
              𝑑 𝑡 𝑖 = ツリー 𝑡 の 𝑖 𝑡ℎ ノード上の𝑑𝑒𝑏𝑖𝑡𝑠数
                        𝑖
              𝐷𝑡 𝑖 =    𝑗=0   𝑑 𝑡 (𝑗) = ツリー 𝑡 のルートノードから 𝑖 𝑡ℎ ノードまでの合計𝑑𝑒𝑏𝑖𝑡𝑠数

Copyright © 2012 yuga                                                          32
解析: Debit Invariant #1

   ある1つのノード上に割り当てられるdebits数の上限を、
   以下の不変式で表します。

    あるノードのqueueに含まれるサスペンション数の上限は、
     そのノードの出次数(out degree)

            ⇒ 𝑑 𝑡 𝑖 ≤ 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 (𝑖) … (1)
                                                                          0


            ツリーの全ノードの出次数の合計は
            ノード数よりも1小さいので、
                                           0th    node   <=   4   1   2       7
                                           1st    node   <=   0
            ⇒ 𝐷 𝑡 ≤ |𝑡|                    2nd
                                           7th
                                                  node
                                                  node
                                                         <=
                                                         <=
                                                              3
                                                              2
                                           8th    node   <=   2
                                           12th   node   <=   0                   8   12


Copyright © 2012 yuga                                                                      33
解析: Debit Invariant #2

   あるノードが、全体のツリー t のルートノードとなり
   tail 関数によって取り除かれるまでに返済しなければなら
   ないdebits数の上限を、以下の不変式であらわします。

    そのノードへのルートノードからのpath数 (= depth)
      + そのノードより先に tail 関数で取り除かれるノード数
             ⇒ 𝐷 𝑡 𝑖 ≤ 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑖                                        0

               … (2) Left linear debit invariant

                                         0 + 0 =   0   if i =   0   1   2       7
                                         1 + 1 =   2   if i =   1
                        ルートノードは返済が
                                         2 + 1 =   3   if i =   2
                        済んだ状態になる
                                             …
                                         7 + 1 =   8 if i = 7
                                         8 + 2 =   10 if i = 8                      8   12
                                        12 + 2 =   14 if i = 12

Copyright © 2012 yuga                                                                        34
解析: 定理10.1

   定理10.1
   ++ 関数と tail 関数は、それぞれ 1 debit、3 debits ず
   つ返済することでDebit Invariantを維持する。




Copyright © 2012 yuga                        35
解析: ++ 関数は定理10.1を満たしているか

   ++ 関数が定理10.1を満たすことを証明します。

   ++ 関数は、2つのツリー𝑡1 と𝑡2 を連結することで新たなツリー𝑡を作るものとします。ツリー𝑡の
   ノード数を|𝑡| 、 𝑡1 を|𝑡1 | とします。当然ながら𝑡1 とt2はそれぞれ不変式(1)と(2)を満たしています。
   ++ 関数の実行の結果、𝑡1 のルートノードの子としてt2のルートノードが加わり新しいツリーtがう
   まれます。

   新規に発生するdebitとしては、t2のルートノードを格納するサスペンションが作られた結果、𝑡の
   ルートノード(元𝑡1 のルートノード)に割り当てられるdebitが1増加します。

   debit数の上限に影響するデータ構造の変化としては、𝑡1 のルートノードの出次数が1増え、𝑡2 の各
   ノードのインデックスが|𝑡1 |増加しdepthも1増加します。




Copyright © 2012 yuga                                          36
解析: ++ 関数は定理10.1を満たしているか

   まず新規debitについて考えます。不変式(1)によると、tの総出次数=𝑡1 の総出次数+𝑡2 の総出次数 +
   1であるので、このdebitの返済は必要ありません。しかし不変式(2)よれば、ルートノードはdebitを
   持てないため、すぐに1 debit返済する必要があります。

   次にデータ構造の変化によるdebit数の上限の変化です。ルートノードの出次数増加は、不変式(1)に
   よればdebitの許容数を増やすものなので、既存のdebitに対する影響はありません。不変式(2)につ
   いては、𝑡1 に含まれていた任意のノードiは連結による影響を受けないため、i < |𝑡1 | に対し、
   𝐷 𝑡 𝑖 = 𝐷 𝑡1 𝑖 ≤ 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡1 𝑖 = 𝑑𝑒𝑝𝑡ℎ 𝑡 (𝑖)です。ツリーt2に含まれていた任意のノード𝑖は𝑡の中でイ
   ンデックスが|𝑡1 |増加し、またdepthが1増加するので、

     𝐷 𝑡 𝑡1 + 𝑖 =       𝐷 𝑡1 + 𝐷 𝑡2 𝑖
                ≤       𝑡1 + 𝐷 𝑡2 𝑖
                ≤       𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡2 𝑖
                =       𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑡1 + 𝑖 − 1
                <         𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑡1 + 𝑖

   となりこちらも不変式を維持しています。

   以上から「++関数は1 debitの返済でDebit Invariantを維持し定理10.1を満たす」ことを証明でき
   ました。


Copyright © 2012 yuga                                                          37
解析: tail 関数は定理10.1を満たしているか

   tail 関数が定理10.1を満たすことを証明します。

   ツリーtに tail 関数を適用してツリーt’を作るものとします(let t’ = tail t)。tのルートノードはm
   個の子ノードを持っています。tail 関数はtのルートノードを取り除いた後、その子ノードとして
   queueに格納されていたツリーt0からtm-1を右から左へリスト状につなぎます。

                                                                   𝑡′       1

                                𝑡
                                    0
                                                                   2    3       4       5

             𝑡0                         𝑡𝑗               𝑡   𝑚−1
                   1                5        …       x                                          …
                                                                            6       7       8
                                                                                                    x

           2      3     4   6       7    8       …   …       …


                                                                                                …   …   …
Copyright © 2012 yuga                                                                                       38
解析: tail 関数は定理10.1を満たしているか

   新規に発生するdebit
   ツリーt’jを、ツリーtjからtm-1までをリンクした部分結果とします。したがってツリーt’=t’0となり
   ます。一番外側を除いたすべてのリンクはサスペンションを作ります。一番外側が除かれるのは、
   tail 関数からの linkAll 関数の呼び出しは遅延されてないからです。link関数の実行だけに注目して大
   雑把に式にすると、

                    let tail = link (tj, lazy (link (tj+1, lazy (link (tm-2, lazy (tm-1))))))

   となっています。このようにサスペンションの作成によってもたらされるdebitsを、ツリーtj ただし
   0 < j <= m-1 の各ルートノードに割り当てます。
                                                                                𝑡′0               𝑡′ 𝑗
   debit数の上限に影響するデータ構造の変化
                                                                            1                 5
   ツリーt0からツリーtm-2はそれぞれ1ずつ出次数が増
   加します。また、ツリーt1からtm-1は、それぞれ1か
   らm-1だけdepthが増加します。
                                                                                                      …
                                                                   2    3       4     6   7       8
                                                                                                          x
                                                                                                              𝑡′   𝑚−1




                                                                                                      …   …   …

Copyright © 2012 yuga                                                                                                    39
解析: tail 関数は定理10.1を満たしているか

   まず新規debitについて考えます。ツリーt1からtm-2までは、それぞれリンクによって出次数が1増
   加しているので、新規debitを1割り当てても不変式(1)を維持していますが、tm-1についてはリンク
   を行わないので出次数が変化していません。したがって、tm-1に割り当てられる予定だった1 debit
   はすぐに返済する必要があります。

   次にデータ構造の変化によるdebit数の上限の変化です。ツリーt0からtm-2までの出次数増加は、不
   変式(1)によればdebitの許容数を増やすものなので、既存のdebitに対する影響はありません。不変
   式(2)については、tjの中に含まれるtのi番目のノードをとりあげます。不変式(2)から
   Dt(i)<=i+deptht(i)であることがわかっています。これがtailによって、どのように各項の値がどの
   ように変化するかを見ます。tのルートノードが取り除かれるので、iは1減少します。tjの各ノードの
   depthはj-1増加します。一方でtjの各ノードのDt(i)は新規debitにより累積debitがj増加します。し
   たがって、

   Dt’(i-1)=Dt(i)+j<=i+deptht(i)+j=i+(deptht’(i-1)-(j-1))+j=(i-1)+deptht’(i-1)+2

   となり、2 debitsを返済すれば不変式(2)を維持できます。よって tail 関数が返済すべきdebitは合計
   3となります。

   以上から「tail 関数は3 debitの返済でDebit Invariantを維持し定理10.1を満たす」ことを証明でき
   ました。


Copyright © 2012 yuga                                                              40
参考文献

   •     Chris Okasaki, “10.2.1 Lists With Efficient Catenation”, Purely
         Functional Data Structures, Cambridge University Press (1999)

   •     Chris Okasaki, “Amortization, lazy evaluation, and persistence: Lists
         with catenation via lazy linking”, In IEEE Symposium on Foundations
         of Computer Science, pages 646-654, October 1995

   •     Haim Kaplan and Robert E. Tarjan, “Persistent lists with catenation via
         recursive slow-down”, In ACM Symposium on Theory of Computing,
         pages 93-102, May 1995.




Copyright © 2012 yuga                                                              41

Contenu connexe

Tendances

C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングKohsuke Yuasa
 
Crfと素性テンプレート
Crfと素性テンプレートCrfと素性テンプレート
Crfと素性テンプレートKei Uchiumi
 
Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~Kazuya Wada
 
Scala の関数型プログラミングを支える技術
Scala の関数型プログラミングを支える技術Scala の関数型プログラミングを支える技術
Scala の関数型プログラミングを支える技術Naoki Aoyama
 
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会Shotaro Sano
 
クラシックな機械学習の入門 3. 線形回帰および識別
クラシックな機械学習の入門 3. 線形回帰および識別クラシックな機械学習の入門 3. 線形回帰および識別
クラシックな機械学習の入門 3. 線形回帰および識別Hiroshi Nakagawa
 
第12回計算機構成
第12回計算機構成第12回計算機構成
第12回計算機構成眞樹 冨澤
 
現実世界のJRuby(ショートバージョン)
現実世界のJRuby(ショートバージョン)現実世界のJRuby(ショートバージョン)
現実世界のJRuby(ショートバージョン)Hiroshi Nakamura
 
今さら始めるCoffeeScript
今さら始めるCoffeeScript今さら始めるCoffeeScript
今さら始めるCoffeeScriptAshitaba YOSHIOKA
 
第10回 計算機構成
第10回 計算機構成第10回 計算機構成
第10回 計算機構成眞樹 冨澤
 
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜Takeshi Arabiki
 
Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」Nagi Teramo
 
Objective-C が好きになる Tips & Hack
Objective-C が好きになる Tips & HackObjective-C が好きになる Tips & Hack
Objective-C が好きになる Tips & HackTaketo Sano
 

Tendances (20)

C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
Crfと素性テンプレート
Crfと素性テンプレートCrfと素性テンプレート
Crfと素性テンプレート
 
Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~Rで触れる日本経済~RでVAR編~
Rで触れる日本経済~RでVAR編~
 
現実世界のJRuby
現実世界のJRuby現実世界のJRuby
現実世界のJRuby
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
Scala の関数型プログラミングを支える技術
Scala の関数型プログラミングを支える技術Scala の関数型プログラミングを支える技術
Scala の関数型プログラミングを支える技術
 
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会
サポートベクトルデータ記述法による異常検知 in 機械学習プロフェッショナルシリーズ輪読会
 
講座Java入門
講座Java入門講座Java入門
講座Java入門
 
クラシックな機械学習の入門 3. 線形回帰および識別
クラシックな機械学習の入門 3. 線形回帰および識別クラシックな機械学習の入門 3. 線形回帰および識別
クラシックな機械学習の入門 3. 線形回帰および識別
 
第12回計算機構成
第12回計算機構成第12回計算機構成
第12回計算機構成
 
現実世界のJRuby(ショートバージョン)
現実世界のJRuby(ショートバージョン)現実世界のJRuby(ショートバージョン)
現実世界のJRuby(ショートバージョン)
 
初めてのSTL
初めてのSTL初めてのSTL
初めてのSTL
 
今さら始めるCoffeeScript
今さら始めるCoffeeScript今さら始めるCoffeeScript
今さら始めるCoffeeScript
 
第10回 計算機構成
第10回 計算機構成第10回 計算機構成
第10回 計算機構成
 
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
文字列カーネルによる辞書なしツイート分類 〜文字列カーネル入門〜
 
Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」Rによるデータサイエンス:12章「時系列」
Rによるデータサイエンス:12章「時系列」
 
Actor&stm
Actor&stmActor&stm
Actor&stm
 
Scala with DDD
Scala with DDDScala with DDD
Scala with DDD
 
Objective-C が好きになる Tips & Hack
Objective-C が好きになる Tips & HackObjective-C が好きになる Tips & Hack
Objective-C が好きになる Tips & Hack
 
機械学習
機械学習機械学習
機械学習
 

En vedette

Caja metafisica
Caja metafisicaCaja metafisica
Caja metafisicamek97
 
Jorge Oteiza
Jorge OteizaJorge Oteiza
Jorge Oteizahirune
 
Jorge Oteiza
Jorge OteizaJorge Oteiza
Jorge Oteizahirune
 
Eguberriak
EguberriakEguberriak
Eguberriakhirune
 
PFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time DequesPFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time Deques昌平 村山
 
Oteiza
OteizaOteiza
Oteizajessi
 

En vedette (9)

Caja metafisica
Caja metafisicaCaja metafisica
Caja metafisica
 
Clojure
ClojureClojure
Clojure
 
Jorge Oteiza
Jorge OteizaJorge Oteiza
Jorge Oteiza
 
Jorge Oteiza
Jorge OteizaJorge Oteiza
Jorge Oteiza
 
Eguberriak
EguberriakEguberriak
Eguberriak
 
Jorge oteiza
Jorge oteizaJorge oteiza
Jorge oteiza
 
Jorge Oteiza
Jorge OteizaJorge Oteiza
Jorge Oteiza
 
PFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time DequesPFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time Deques
 
Oteiza
OteizaOteiza
Oteiza
 

Similaire à PFDS 10.2.1 lists with efficient catenation

10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!bitter_fox
 
Chainerチュートリアル -v1.5向け- ViEW2015
Chainerチュートリアル -v1.5向け- ViEW2015Chainerチュートリアル -v1.5向け- ViEW2015
Chainerチュートリアル -v1.5向け- ViEW2015Ryosuke Okuta
 
PFDS 11.2 catenable double ended queue
PFDS 11.2 catenable double ended queuePFDS 11.2 catenable double ended queue
PFDS 11.2 catenable double ended queue昌平 村山
 
What Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットWhat Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットTaisuke Oe
 
20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部NVIDIA Japan
 
Clojure programming-chapter-2
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2Masao Kato
 
Metaprogramming in JuliaLang
Metaprogramming in JuliaLangMetaprogramming in JuliaLang
Metaprogramming in JuliaLangYuichi Motoyama
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Springanyakichi
 
K010 appstat201201
K010 appstat201201K010 appstat201201
K010 appstat201201t2tarumi
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
Scalaで型クラス入門
Scalaで型クラス入門Scalaで型クラス入門
Scalaで型クラス入門Makoto Fukuhara
 
おもにEXcelだけで出来る自動化技術
おもにEXcelだけで出来る自動化技術おもにEXcelだけで出来る自動化技術
おもにEXcelだけで出来る自動化技術Takanobu Mizuta
 
解説#74 連結リスト
解説#74 連結リスト解説#74 連結リスト
解説#74 連結リストRuo Ando
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミングOuka Yuka
 
Casual learning machine learning with_excel_no5
Casual learning machine learning with_excel_no5Casual learning machine learning with_excel_no5
Casual learning machine learning with_excel_no5KazuhiroSato8
 

Similaire à PFDS 10.2.1 lists with efficient catenation (20)

10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!
 
kibayos_ov_090922
kibayos_ov_090922kibayos_ov_090922
kibayos_ov_090922
 
R新機能抄出
R新機能抄出R新機能抄出
R新機能抄出
 
Chainerチュートリアル -v1.5向け- ViEW2015
Chainerチュートリアル -v1.5向け- ViEW2015Chainerチュートリアル -v1.5向け- ViEW2015
Chainerチュートリアル -v1.5向け- ViEW2015
 
PFDS 11.2 catenable double ended queue
PFDS 11.2 catenable double ended queuePFDS 11.2 catenable double ended queue
PFDS 11.2 catenable double ended queue
 
What Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミットWhat Dotty fixes @ Scala関西サミット
What Dotty fixes @ Scala関西サミット
 
20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部20190625 OpenACC 講習会 第3部
20190625 OpenACC 講習会 第3部
 
Clojure programming-chapter-2
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2
 
Metaprogramming in JuliaLang
Metaprogramming in JuliaLangMetaprogramming in JuliaLang
Metaprogramming in JuliaLang
 
More C++11
More C++11More C++11
More C++11
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Spring
 
CuPy解説
CuPy解説CuPy解説
CuPy解説
 
K010 appstat201201
K010 appstat201201K010 appstat201201
K010 appstat201201
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
Trait in scala
Trait in scalaTrait in scala
Trait in scala
 
Scalaで型クラス入門
Scalaで型クラス入門Scalaで型クラス入門
Scalaで型クラス入門
 
おもにEXcelだけで出来る自動化技術
おもにEXcelだけで出来る自動化技術おもにEXcelだけで出来る自動化技術
おもにEXcelだけで出来る自動化技術
 
解説#74 連結リスト
解説#74 連結リスト解説#74 連結リスト
解説#74 連結リスト
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
 
Casual learning machine learning with_excel_no5
Casual learning machine learning with_excel_no5Casual learning machine learning with_excel_no5
Casual learning machine learning with_excel_no5
 

Dernier

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案sugiuralab
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 

Dernier (9)

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 

PFDS 10.2.1 lists with efficient catenation

  • 1. PFDS #11 10.2.1 Lists With Efficient Catenation @yuga 2012-11-03 Copyright © 2012 yuga 1
  • 2. 目次 Structural Abstractionを用いた1つめの実装例として、 Catenable List を取り上げます。  定義: 何を作る  実装: どう作る  解析: どんなものなのか Copyright © 2012 yuga 2
  • 3. 定義 何を作る Copyright © 2012 yuga 3
  • 4. 実現する操作 以下の定義を実装したリストを作ります。 定義(CATENABLELISTシグネチャ): module type CATENABLELIST = sig type ‘a t val empty : ‘a t val isEmpty : ‘a t -> bool val cons : ‘a * ‘a t -> ‘a t val snoc : ‘a t * ‘a -> ‘a t val (++) : ‘a t -> ‘a t -> ‘a t val head : ‘a t -> ‘a val tail : ‘a t -> ‘a t end = Output Restricted Queues + (++) Copyright © 2012 yuga 4
  • 5. 実行時間はO(1) CatenableListはすべての操作をO(1)時間で実現します。  通常のリスト同士の連結操作(++)はO(n)時間かかるのに対し、 Catenable ListsではO(1)時間で可能になるのが特徴です。  Persistentな使い方をしても問題ないものにします。 Copyright © 2012 yuga 5
  • 6. 実行時間はAmortized time でも、ごめんなさい。 Worst-Case timeではなくて、Amortized timeです。  Worst-CaseなCatenable Listsは11章に出てくるRecursive Slow-Downというテクニックを使って実現できます。 – Persistent Lists with Catenation via Recursive Slow-Down – こっちの方が先行して世に登場  今回扱うCatenable Listsは後発ですが、Worst-Caseなリスト より実装が簡単になっています。 Copyright © 2012 yuga 6
  • 7. 実装 どう作る Copyright © 2012 yuga 7
  • 8. 実装のアイデア 効率の良い連結関数を作るため、リスト内部にqueueを設け て、その中に相手リストを格納します。 1 6 連結 2 4 ++ 7 8 ○はリストの 要素 3 9 青枠は queue 1 6 1 2 4 7 8 2 4 6 3 9 3 7 8 註: 空のqueueを省略しています 9 Copyright © 2012 yuga 8
  • 9. 実装のアイデア Queueを新たに実装するのは本節の対象外です。以下の要件 を満たしていれば何でも良いので、既存のものを利用します。  QUEUEシグネチャを満たしている module type QUEUE = sig type ‘a t val empty : ‘a t val isEmpty : ‘a t -> bool val snoc : ‘a t * ‘a -> ‘a t val head : ‘a t -> ‘a val tail : ‘a t -> ‘a t end  すべての操作が Worst-Case / Amortized 関係なくO(1)時間で実行可能  Persistentな使い方をしても問題ない Copyright © 2012 yuga 9
  • 10. PFDSに出てきたqueue これまでに登場したqueueには以下のものがありました。 Section Name Cost Persistent 5.2 BatchedQueue O(n) worst-case time NG 6.3.2, BankersQueue O(1) amortized time OK 8.3 6.4.2 PhysicistsQueue O(1) amortized time OK 7.2 RealTimeQueue O(1) worst-case time OK 8.3 HoodMelvilleQueue O(1) worst-case time OK 10.1.3 BootStrappedQueue O(1) amortized time OK ⇒ BatchedQueue以外なら良さそうです。 Copyright © 2012 yuga 10
  • 11. Structural Abstractionテンプレートを使って実装開始 10.2節に登場したテンプレートを参考に進めます。 ‘a c : Primitive type Queue ’a b : Bootstrapped type CatenableList type ‘a b = E | B of ‘a * ‘a b c let unit_b x = B (x, empty_b) let insert_b = function | (x, E) -> B (x, empty_c) | (x, B (y, c)) -> B (x, insert_c (unit_b y, c)) let join_b = function | (b, E) -> b | (E, b) -> b _c は ‘a c | (B (x, c), b) -> B (x, insert_c (b, c)) _b は ‘a b の関数 Copyright © 2012 yuga 11
  • 12. 実装: empty / isEmpty 最初に、データ構造として空リストだけ定義します。 ここではCatenableListの型を t とします。 module CatenableList : CATENABLELIST = struct type ‘a t = E | … … end emptyとisEmptyの実装はデータコンストラクタを見るだけです。 let empty = E let isEmpty = function | E -> true | _ -> false Copyright © 2012 yuga 12
  • 13. 実装: ++ rev.1 QUEUEシグネチャを実装したモジュールをQという名前で受け取 ります。ここではqueueの型を t として、リストのデータ構造を定 義します。 module CatenableList (Q : QUEUE) : CATENABLELIST = struct type ‘a t = E | C of ‘a * ‘a t Q.t … end (++)は、1つめのリストのqueueに2つめのリストを格納します。 let (++) xs ys = match (xs, ys) with | (E, ys) -> ys | (xs, E) -> xs | (C (x, q), ys) -> C (x, Q.snoc (q, ys)) Copyright © 2012 yuga 13
  • 14. 実装: ++ rev.2 4行目は、あとで他の関数からも利用するので、ヘルパー関数link にくくりだします。 module CatenableList (Q : QUEUE) : CATENABLELIST = struct type ‘a t = E | C of ‘a * ‘a t Q.t let link (C (x, q), ys) -> C (x, Q.snoc (q, ys)) let (++) xs ys = match (xs, ys) with | (E, ys) -> ys | (xs, E) -> xs | (xs, ys) -> link (xs, ys) … end Copyright © 2012 yuga 14
  • 15. 実装: cons / snoc consとsnocは、さきほど実装した(++)を使えば簡単です。 let cons (x, xs) = C (x, Q.empty) ++ xs let snoc (xs, x) = xs ++ C (x, Q.empty) Copyright © 2012 yuga 15
  • 16. 図解: cons / snoc consとsnocをそれぞれ3回繰り返した結果です。 3 cons cons 2 cons 1 2 E 1 1 1 2 3 snoc snoc snoc 1 1 1 E 2 2 3 Copyright © 2012 yuga 16
  • 17. 実装: head headは先頭を取り出すだけです。 exception Empty let head = function | E -> raise Empty | C (x, _) -> x Copyright © 2012 yuga 17
  • 18. 実装: tail rev.1 tailは先頭をすてて、queueの中身をリスト状につなぎなおします。 exception Empty let link (C (x, q), ys) -> C (x, Q.snoc (q, ys)) let linkAll q = linkは再掲 let x = Q.head q in let q’ = Q.tail q in if Q. isEmpty q’ then x else link (t, linkAll q’) let tail = function | E -> raise Empty | C (_, q) -> if Q.isEmpty q then q else linkAll q Copyright © 2012 yuga 18
  • 19. 図解: tail tailによりqueueの中身がつなぎなおされる過程です。 最初にheadが取り除かれます。 1 2 5 6 2 5 6 3 4 7 8 3 4 7 8 9 9 Copyright © 2012 yuga 19
  • 20. 図解: tail 続いてqueueをほどいていきます。 2 5 6 2 5 6 3 4 7 8 3 4 7 8 9 9 Copyright © 2012 yuga 20
  • 21. 図解: tail ほどき終わったらリスト状につなぎなおします。 5 2 5 6 2 6 3 4 7 8 3 4 7 8 9 9 Copyright © 2012 yuga 21
  • 22. 実装: tail rev.2 tailの完了です。 2 3 4 5 6 7 8 9 Copyright © 2012 yuga 22
  • 23. 実装: 同じデータを何度もtailしたとき しかし、今の実装では、tailするたびに最悪O(n)回linkを実行 することになり 、CatenableListをpersistentなデータ構造と して使うことができません。 2 1 tail O(n) 3 2 3 4 … 99 … 99 2 2 3 3 … … ならし解析が意味をなさない tail tail (参考: 5.6節 The Bad News) O(n) O(n) 99 99 Copyright © 2012 yuga 23
  • 24. 実装: tail rev.2 そこで、linkAllの再帰実行を遅延データに包んで、先頭要素から queueをほどく処理を、順次必要になるまで遅延させます。 これによりtailがincremental関数になります。 exception Empty let link (C (x, q), ys) -> C (x, Q.snoc (q, ys)) let linkAll q = let lazy t = Q.head q in let q’ = Q.tail q in if Q. isEmpty q’ then t else link (t, lazy (linkAll q’)) let tail = function | E -> raise Empty | C (_, q) -> if Q.isEmpty q then q else linkAll q 型: ‘a t (参考: 6章) 型: ‘a t Lazy.t Copyright © 2012 yuga 24
  • 25. 実装: ++ rev.3 その結果、queueに格納するデータ型が変化するので修正します。 module CatenableList (Q : QUEUE) : CATENABLELIST = struct type ‘a t = E | C of ‘a * ‘a t Lazy.t Q.t let link (C (x, q), ys) -> C (x, Q.snoc (q, ys)) let (++) xs ys = match (xs, ys) with | (E, ys) -> ys | (xs, E) -> xs | (xs, ys) -> link (xs, lazy ys) … end Copyright © 2012 yuga 25
  • 26. 実装: 完成 module CatenableList (Q : QUEUE) : CATENABLELIST = struct type ‘a t = E | C of ‘a * ‘a t Lazy.t Q.t exception Empty let empty = E let isEmpty = function | E -> true | _ -> false let link (C (x, q), ys) -> C (x, Q.snoc (q, ys)) let (++) xs ys = match (xs, ys) with | (E, ys) -> ys | (xs, E) -> xs | (xs, ys) -> link (xs, lazy ys) let cons (x, xs) = C (x, Q.empty) ++ xs let snoc (xs, x) = xs ++ C (x, Q.empty) let head = function | E -> raise Empty | C (x, _) -> x let linkAll q = let lazy t = Q.head q in let q’ = Q.tail q in if Q. isEmpty q’ then t else link (t, lazy (linkAll q’)) let tail = function | E -> raise Empty | C (_, q) -> if Q.isEmpty q then q else linkAll q end Copyright © 2012 yuga Ocamlで実際に動かしてみたやつ: https://github.com/yuga/readpfds/blob/master/OCaml/catenableList.ml 26
  • 27. 解析 どんなものなのか Copyright © 2012 yuga 27
  • 28. 解析: ならし解析 実行コストの考え方:  ++ / cons / snoc / head 関数の実行コストは、実装からあき らかにO(1) worst-case timeです。  tail関数のO(n) worst-case timeな実行コストを、他の関数と の間でならして、CatenableListのすべての操作がO(1) amortized timeであることを証明します。  CatenableListのデータ構造に影響を与えるのは ++ / cons / snoc / tail 関数ですが、cons / snoc 関数は ++ 関数に依存し ています。ならし解析は++ 関数と tail 関数の2つに注目して 行います。 Copyright © 2012 yuga 28
  • 29. 解析: Banker’s method ならし解析にあたり、Banker’s methodを採用します。  tail 関数の実行コストはサスペンションとして負債(debits)にし linkAll 関数が link 関数を呼ぶときの1番目の引数のノードに割り当 てます。  ++ 関数の実行コストもサスペンションとして負債(debits)にし、 ++ 関数が link 関数を呼ぶときの1番目の引数のノードに割り当て ます。  各Nodeが tail 関数によって取り除かれるとき、そのノードに 割り当てられたdebitsがすべて支払い済み(残サスペンション 数=0)であるようにします。 Copyright © 2012 yuga 29
  • 30. 解析: 定義(ツリー) CatenableListのデータ構造を、ノードによって構成され たツリーが、階層状になっているものと考えます。 0 1 2 7 8 𝑡0 𝑡2 𝑡3 3 4 5 6 𝑡 𝑡1 Copyright © 2012 yuga 30
  • 31. 解析: 定義(ノード識別子, degree, depth) このツリーにラベルと関数を定義します。 0 root (0 th) node of t 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 0 = 4 4th node of t 1 2 3 4 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 4 = 4 1st 2nd 3 rd 𝑑𝑒𝑝𝑡ℎ 𝑡 4 = 1 node of t 0 th node of 𝑡1 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 𝑗 0 = 4 5 6 7 8 1st 𝑑𝑒𝑝𝑡ℎ 𝑡 8 = 2 node of 𝑡 𝑗 9 10 11 12 𝑡 𝑡𝑗 Copyright © 2012 yuga 31
  • 32. 解析: 各ノードに割り当てるdebitsの考え方 各ノードに割り当てるdebitsは以下のようになります。  ならし解析の目的はlinkAllのコストを配分すること  queueの中の子ノード数 (linkAllのコスト) = queueに含まれるサスペンション数 = そのノードに割り当てるdebits数  デビット数を表す関数を定義します。 𝑑 𝑡 𝑖 = ツリー 𝑡 の 𝑖 𝑡ℎ ノード上の𝑑𝑒𝑏𝑖𝑡𝑠数 𝑖 𝐷𝑡 𝑖 = 𝑗=0 𝑑 𝑡 (𝑗) = ツリー 𝑡 のルートノードから 𝑖 𝑡ℎ ノードまでの合計𝑑𝑒𝑏𝑖𝑡𝑠数 Copyright © 2012 yuga 32
  • 33. 解析: Debit Invariant #1 ある1つのノード上に割り当てられるdebits数の上限を、 以下の不変式で表します。  あるノードのqueueに含まれるサスペンション数の上限は、 そのノードの出次数(out degree) ⇒ 𝑑 𝑡 𝑖 ≤ 𝑑𝑒𝑔𝑟𝑒𝑒 𝑡 (𝑖) … (1) 0 ツリーの全ノードの出次数の合計は ノード数よりも1小さいので、 0th node <= 4 1 2 7 1st node <= 0 ⇒ 𝐷 𝑡 ≤ |𝑡| 2nd 7th node node <= <= 3 2 8th node <= 2 12th node <= 0 8 12 Copyright © 2012 yuga 33
  • 34. 解析: Debit Invariant #2 あるノードが、全体のツリー t のルートノードとなり tail 関数によって取り除かれるまでに返済しなければなら ないdebits数の上限を、以下の不変式であらわします。  そのノードへのルートノードからのpath数 (= depth) + そのノードより先に tail 関数で取り除かれるノード数 ⇒ 𝐷 𝑡 𝑖 ≤ 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑖 0 … (2) Left linear debit invariant 0 + 0 = 0 if i = 0 1 2 7 1 + 1 = 2 if i = 1 ルートノードは返済が 2 + 1 = 3 if i = 2 済んだ状態になる … 7 + 1 = 8 if i = 7 8 + 2 = 10 if i = 8 8 12 12 + 2 = 14 if i = 12 Copyright © 2012 yuga 34
  • 35. 解析: 定理10.1 定理10.1 ++ 関数と tail 関数は、それぞれ 1 debit、3 debits ず つ返済することでDebit Invariantを維持する。 Copyright © 2012 yuga 35
  • 36. 解析: ++ 関数は定理10.1を満たしているか ++ 関数が定理10.1を満たすことを証明します。 ++ 関数は、2つのツリー𝑡1 と𝑡2 を連結することで新たなツリー𝑡を作るものとします。ツリー𝑡の ノード数を|𝑡| 、 𝑡1 を|𝑡1 | とします。当然ながら𝑡1 とt2はそれぞれ不変式(1)と(2)を満たしています。 ++ 関数の実行の結果、𝑡1 のルートノードの子としてt2のルートノードが加わり新しいツリーtがう まれます。 新規に発生するdebitとしては、t2のルートノードを格納するサスペンションが作られた結果、𝑡の ルートノード(元𝑡1 のルートノード)に割り当てられるdebitが1増加します。 debit数の上限に影響するデータ構造の変化としては、𝑡1 のルートノードの出次数が1増え、𝑡2 の各 ノードのインデックスが|𝑡1 |増加しdepthも1増加します。 Copyright © 2012 yuga 36
  • 37. 解析: ++ 関数は定理10.1を満たしているか まず新規debitについて考えます。不変式(1)によると、tの総出次数=𝑡1 の総出次数+𝑡2 の総出次数 + 1であるので、このdebitの返済は必要ありません。しかし不変式(2)よれば、ルートノードはdebitを 持てないため、すぐに1 debit返済する必要があります。 次にデータ構造の変化によるdebit数の上限の変化です。ルートノードの出次数増加は、不変式(1)に よればdebitの許容数を増やすものなので、既存のdebitに対する影響はありません。不変式(2)につ いては、𝑡1 に含まれていた任意のノードiは連結による影響を受けないため、i < |𝑡1 | に対し、 𝐷 𝑡 𝑖 = 𝐷 𝑡1 𝑖 ≤ 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡1 𝑖 = 𝑑𝑒𝑝𝑡ℎ 𝑡 (𝑖)です。ツリーt2に含まれていた任意のノード𝑖は𝑡の中でイ ンデックスが|𝑡1 |増加し、またdepthが1増加するので、 𝐷 𝑡 𝑡1 + 𝑖 = 𝐷 𝑡1 + 𝐷 𝑡2 𝑖 ≤ 𝑡1 + 𝐷 𝑡2 𝑖 ≤ 𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡2 𝑖 = 𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑡1 + 𝑖 − 1 < 𝑡1 + 𝑖 + 𝑑𝑒𝑝𝑡ℎ 𝑡 𝑡1 + 𝑖 となりこちらも不変式を維持しています。 以上から「++関数は1 debitの返済でDebit Invariantを維持し定理10.1を満たす」ことを証明でき ました。 Copyright © 2012 yuga 37
  • 38. 解析: tail 関数は定理10.1を満たしているか tail 関数が定理10.1を満たすことを証明します。 ツリーtに tail 関数を適用してツリーt’を作るものとします(let t’ = tail t)。tのルートノードはm 個の子ノードを持っています。tail 関数はtのルートノードを取り除いた後、その子ノードとして queueに格納されていたツリーt0からtm-1を右から左へリスト状につなぎます。 𝑡′ 1 𝑡 0 2 3 4 5 𝑡0 𝑡𝑗 𝑡 𝑚−1 1 5 … x … 6 7 8 x 2 3 4 6 7 8 … … … … … … Copyright © 2012 yuga 38
  • 39. 解析: tail 関数は定理10.1を満たしているか 新規に発生するdebit ツリーt’jを、ツリーtjからtm-1までをリンクした部分結果とします。したがってツリーt’=t’0となり ます。一番外側を除いたすべてのリンクはサスペンションを作ります。一番外側が除かれるのは、 tail 関数からの linkAll 関数の呼び出しは遅延されてないからです。link関数の実行だけに注目して大 雑把に式にすると、 let tail = link (tj, lazy (link (tj+1, lazy (link (tm-2, lazy (tm-1)))))) となっています。このようにサスペンションの作成によってもたらされるdebitsを、ツリーtj ただし 0 < j <= m-1 の各ルートノードに割り当てます。 𝑡′0 𝑡′ 𝑗 debit数の上限に影響するデータ構造の変化 1 5 ツリーt0からツリーtm-2はそれぞれ1ずつ出次数が増 加します。また、ツリーt1からtm-1は、それぞれ1か らm-1だけdepthが増加します。 … 2 3 4 6 7 8 x 𝑡′ 𝑚−1 … … … Copyright © 2012 yuga 39
  • 40. 解析: tail 関数は定理10.1を満たしているか まず新規debitについて考えます。ツリーt1からtm-2までは、それぞれリンクによって出次数が1増 加しているので、新規debitを1割り当てても不変式(1)を維持していますが、tm-1についてはリンク を行わないので出次数が変化していません。したがって、tm-1に割り当てられる予定だった1 debit はすぐに返済する必要があります。 次にデータ構造の変化によるdebit数の上限の変化です。ツリーt0からtm-2までの出次数増加は、不 変式(1)によればdebitの許容数を増やすものなので、既存のdebitに対する影響はありません。不変 式(2)については、tjの中に含まれるtのi番目のノードをとりあげます。不変式(2)から Dt(i)<=i+deptht(i)であることがわかっています。これがtailによって、どのように各項の値がどの ように変化するかを見ます。tのルートノードが取り除かれるので、iは1減少します。tjの各ノードの depthはj-1増加します。一方でtjの各ノードのDt(i)は新規debitにより累積debitがj増加します。し たがって、 Dt’(i-1)=Dt(i)+j<=i+deptht(i)+j=i+(deptht’(i-1)-(j-1))+j=(i-1)+deptht’(i-1)+2 となり、2 debitsを返済すれば不変式(2)を維持できます。よって tail 関数が返済すべきdebitは合計 3となります。 以上から「tail 関数は3 debitの返済でDebit Invariantを維持し定理10.1を満たす」ことを証明でき ました。 Copyright © 2012 yuga 40
  • 41. 参考文献 • Chris Okasaki, “10.2.1 Lists With Efficient Catenation”, Purely Functional Data Structures, Cambridge University Press (1999) • Chris Okasaki, “Amortization, lazy evaluation, and persistence: Lists with catenation via lazy linking”, In IEEE Symposium on Foundations of Computer Science, pages 646-654, October 1995 • Haim Kaplan and Robert E. Tarjan, “Persistent lists with catenation via recursive slow-down”, In ACM Symposium on Theory of Computing, pages 93-102, May 1995. Copyright © 2012 yuga 41