0
8.4.3 Real-Time Deques      2012-08-04 (初版)      2012-08-14 (2版)         @yuga
目次•   狙い•   実装•   解析•   Exercise 8.7•   おまけ
狙い• すべての操作が最悪実行時間O(1)であるDequeを作ります。• 8.4.2で登場したBankersDequeを拡張して実現します。
実装• BankersDqeueはAmortized Boundなデータ構造であるので、  7章で学習した手順でWorst-Case Boundなデータ構造へと変  換していきます。  1.   Monolithic関数をIncremental...
BankersDequeで使用したmonolithic関数BankersDqueでは、フロントとリアの2つのStreamを、以下のように操作してローテーションを行っていました。         1.      長い方のStreamを全体の半分の...
Streamの実装(参考)P36 の Figure 4.1 より                     サスペンションを1つ潰すが、                                        新しいStreamのために構築...
monolithic関数からincremental関数へBankersDequeでのStream操作を分類します。  – Incremental関数     • take     • ++  – monolithic関数     • rever...
rotateDrop / rotateRevの戦略•   Incremenetal関数への変換    – Streamに対する処理を分割して実行し、毎回一定数(𝑐 > 1)ずつ処理を行う      ようにします。      ⇒ 一定数ずつ処理す...
rotateDrop1実行ごとに c ずつdropします。rotateDropの各回の呼び出しは、短い方のStreamを再作成する$CONSの内側に配置します。    fun rotateDrop (f, j, r)=          if ...
rotateRev1実行ごとに c ずつreverseします。rotateDropの各回の呼び出しは、短い方のStreamを再作成する$CONSの内側に配置します。    fun rotateRev ($NIL, r, a) = reverse...
ローテーション後のフロントとリア新たに作成されたStreamに内包されて処理が遅延しています。サスペンションはフロントとリアの両方に作成されます。                      フロント                    リア ...
スケジュールの導入ローテーション実行中にrotateDrop関数と rotateRev関数が作成したサスペンションを、次回のローテーション開始までに全て実行しなければなりません。このためにスケジュールを導入します。  type ‘a queue...
解析短い方のStreamもとに作成する$CONSで rotateDrop と rotateRev を遅延させることができるように、Balance Invariantを設定します。•   Balance Invariant:           ...
解析定数 c の範囲を求めます。•   定数 c の範囲    – 定数 j を長い方から短い方へ移動する長さとする。    – rotateRev 開始時に以下が成立する。         長い方 ≥ 𝑐 × 短い方 + 1 + 𝑘 − 𝑗 ...
解析•   ローテーション後のフロントとリアの長さ:                                           𝑓 + |𝑟|               ローテーション前に短かった方 =               ...
Exercise 8.7•   ローテーションで作成されるサスペンション数は、フロントとリアともにStream    の長さと一致します。•   Debits Invariantを以下のように設定します。    – ローテーション開始時の 𝑐 ...
おまけ: RealTimeDequeの動作例例) Dequeの操作とローテーション   1. c = 3, |f| = 3, |r| = 9 の RealTimeDeque q1 に対して       •   snoc q1 x        ...
おまけ: Global Rebuildingとの違いだいたいこんな感じだと思います。•   Global Rebuildingには必要だったローテーション用のコピーが、Lazy Rebuildingで    は不要。•   Global Reb...
参考文献•   Chris Okasaki, “8.4.3 Real-Time Deque”, Purely Functional Data Structures,    Cambridge University Press (1999)•  ...
Upcoming SlideShare
Loading in...5
×

PFDS 8.4.3 Real-Time Deques

311

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
311
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "PFDS 8.4.3 Real-Time Deques"

  1. 1. 8.4.3 Real-Time Deques 2012-08-04 (初版) 2012-08-14 (2版) @yuga
  2. 2. 目次• 狙い• 実装• 解析• Exercise 8.7• おまけ
  3. 3. 狙い• すべての操作が最悪実行時間O(1)であるDequeを作ります。• 8.4.2で登場したBankersDequeを拡張して実現します。
  4. 4. 実装• BankersDqeueはAmortized Boundなデータ構造であるので、 7章で学習した手順でWorst-Case Boundなデータ構造へと変 換していきます。 1. Monolithic関数をIncremental関数に変換します 2. Dequeの各操作ごとにサスペンションを少しずつ実行するスケジュー ルを導入します
  5. 5. BankersDequeで使用したmonolithic関数BankersDqueでは、フロントとリアの2つのStreamを、以下のように操作してローテーションを行っていました。 1. 長い方のStreamを全体の半分の長さだけ残す – 残す方をtakeで取得 – 半分を超える部分をdropで作成 2. 半分を超える部分をreverseして短い方のstreamと結合 (これで2つのstreamが同じ長さになる) – 半分を超える部分をreverse – 短い方のリストを前にしてreverseした結果と++ Queueの場合はリアを空にしてフロントにすべて寄せていましたが、Dequeではフロントとリアの長さを同じ にします。|f| < |r| の場合: val r’ = take (j, r) val f’ = f ++ reverse (drop (j, r))ここに登場したtake/drop/reverseの各関数は、4章で定義されたものです。
  6. 6. Streamの実装(参考)P36 の Figure 4.1 より サスペンションを1つ潰すが、 新しいStreamのために構築したサスペンショ ンで、後続の処理を遅延させる fun lazy ($NIL) ++ t = t | ($CONS (x, s)) ++ t = $CONS (x, s ++ t) incremental関数 fun lazy take (0, s) = $NIL | take (n, $NIL) = $NIL | take (n, $CONS (x, s)) = $CONS (x, take (n-1, s)) fun lazy drop (n, s) = let fun drop’ (0, s) = s | drop’ (n, $NIL) = $NIL | drop’ (n, $CONS (x, s)) = drop’ (n-1, s) in drop’ (n, s) end fun lazy reverse s = monolithic関数 let fun reverse’ ($NIL, r) = r | reverse’ ($CONS (x, s), r) = reverse’ (s, $CONS (x, r)) in reverse’ (s, $NIL) end 既存のサスペンションをすべて潰しながら、 新たなStreamを終端から構築する
  7. 7. monolithic関数からincremental関数へBankersDequeでのStream操作を分類します。 – Incremental関数 • take • ++ – monolithic関数 • reverse • drop以上から、RealTimeDeque専用のdropとreverseを用意します。 – rotateDrop – rotateRev
  8. 8. rotateDrop / rotateRevの戦略• Incremenetal関数への変換 – Streamに対する処理を分割して実行し、毎回一定数(𝑐 > 1)ずつ処理を行う ようにします。 ⇒ 一定数ずつ処理することで、1回の処理時間がO(1)になる。• スケジュールの導入 – ローテーションの結果として新規作成されるStreamのデータ構築子$CONSを サスペンションとして、その内側に関数の呼び出しを配置して実行を遅延しま す。 – Sectio7.2のRealTimeQueueと同様に、 Streamを先頭から順に開いていくこと でサスペンションを潰していく、スケジュール機構を構築します。
  9. 9. rotateDrop1実行ごとに c ずつdropします。rotateDropの各回の呼び出しは、短い方のStreamを再作成する$CONSの内側に配置します。 fun rotateDrop (f, j, r)= if j < c then rotateRev (f, drop (j, r), $NIL) ① else let val ($CONS (x, f’)) = f in $CONS (x, rotateDrop (f’, j – c, drop (c, r))) end ② ② ① c=3 の場合
  10. 10. rotateRev1実行ごとに c ずつreverseします。rotateDropの各回の呼び出しは、短い方のStreamを再作成する$CONSの内側に配置します。 fun rotateRev ($NIL, r, a) = reverse r ++ a | rotateRev ($CONS (x, f), r, a) = $CONS (x, rotateRev (f, drop (c, r), reverse (take (c, r)) ++ a)) c=3 の場合
  11. 11. ローテーション後のフロントとリア新たに作成されたStreamに内包されて処理が遅延しています。サスペンションはフロントとリアの両方に作成されます。 フロント リア rotateRev が作成 rotateDrop が作成 ++ が作成 take が作成 drop drop ++ take rotateDrop take reverse Streamの$CONSで ++ 遅延している処理 rotateRev ++ や take 自身が再帰して作成す るので、他の処理を内包できない (専用のを作れば別だろうけど)
  12. 12. スケジュールの導入ローテーション実行中にrotateDrop関数と rotateRev関数が作成したサスペンションを、次回のローテーション開始までに全て実行しなければなりません。このためにスケジュールを導入します。 type ‘a queue = int * ‘a Stream * ‘a Stream (* front *) * int * ‘a Stream * ‘a Stream (* rear *) データサイズ データ スケジュールローテーションで新しいフロントとリアができるので、そのままスケジュールとします。 let j = (lenf + lenr) / 2; let i = lenf + lenr –j; let r = S.take (j, r); let f = rotateDrop (f, j, r) in (i, f, f, j, r, r)Streamを順に開いていくことでサスペンションを実行します。 fun exec1 ($CONS (x, s)) = s | exec1 s = s
  13. 13. 解析短い方のStreamもとに作成する$CONSで rotateDrop と rotateRev を遅延させることができるように、Balance Invariantを設定します。• Balance Invariant: 𝑓 ≤ 𝑐× 𝑟 +1 𝑟 ≤ 𝑐 × 𝑓 + 1 (BankersDequeと同じ)• 上記から、ローテーション開始時のフロントとリアの長さ: 短い方 = 𝑚 長い方 = 𝑐 × 𝑚 + 𝑘 + 1 𝑤ℎ𝑒𝑟𝑒 1 ≤ 𝑘 ≤ 𝑐 例) c=3の場合、以下のような状態と操作でローテーションが開始する k=1: |f|=5, |r|=16 のときの snoc、|f|=6, |r|=17 のときの tail k=2: |f|=6, |r|=18 のときの tail k=3: |f|=6, |r|=19 のときの tail
  14. 14. 解析定数 c の範囲を求めます。• 定数 c の範囲 – 定数 j を長い方から短い方へ移動する長さとする。 – rotateRev 開始時に以下が成立する。 長い方 ≥ 𝑐 × 短い方 + 1 + 𝑘 − 𝑗 𝑚𝑜𝑑 𝑐 – 長い方 ≥ 𝑐 × 短い方 , 1 ≤ 𝑘 ≤ 𝑐, 𝑐 > 1 なので、 1 + 𝑘 − 𝑗 𝑚𝑜𝑑 𝑐 ≥ 0 1 + 1 − 𝑐 − 1 ≥ 0 (k の最小値と j mod c の最大値を代入) 𝑐≤3 – 𝑐 = 2 または 𝑐 = 3
  15. 15. 解析• ローテーション後のフロントとリアの長さ: 𝑓 + |𝑟| ローテーション前に短かった方 = 2 𝑓 + |𝑟| ローテーション前に長かった方 = 2• 次回ローテション実行までの最短操作回数 1 – Init / tail のどちらかを連続実行します(cons / snoc よりも 倍の回数ですむ)。 𝑐 – ローテーション後のフロント、リアの長さをともに n とすると 𝑐 × 𝑛 − 最短操作回数 + 2 ≤ 𝑛 𝑛−2 𝑛 − 最短操作回数 ≤ 𝑐 𝑛−2 𝑛+2 𝑛 最短操作回数 ≥ 𝑛 − = > 𝑐 𝑐 𝑐 𝑛+2 𝑛 の項が最大になるのは𝑐 = 2のときで、init / tail を最短で約 回実行すると、次のロー 𝑐 2 テーションが発生します。
  16. 16. Exercise 8.7• ローテーションで作成されるサスペンション数は、フロントとリアともにStream の長さと一致します。• Debits Invariantを以下のように設定します。 – ローテーション開始時の 𝑐 × 短い方 + 1 + 𝑘 ≥ 長い方 𝑤ℎ𝑒𝑟𝑒 1 ≤ 𝑘 ≤ 𝑐 から、 一方の𝑆𝑡𝑟𝑒𝑎𝑚の未評価サスペンション数 ≤ 𝑐 × 短い方 + 2 − 長い方 さらに次回ローテション実行までの最短操作回数の考察から c = 2 を代入して、 一方のStreamの未評価サスペンション数 ≤ 2 × 短い方 + 2 − 長い方• 以上から – ローテーション実行直後、短い方と長い方の長さは高々1違うだけなので、Debits Invariantを 満たしている。 – Dequeから削除するとき、短い方が1短くなっても、2つのStreamのいずれも、サスペンション を2減らせばDebits Invariantを維持できる。 – Dequeに追加するとき、長い方が1短くなっても、2つのStreamのいずれも、サスペンションを1 減らせばDebits Invariantを維持できる。
  17. 17. おまけ: RealTimeDequeの動作例例) Dequeの操作とローテーション 1. c = 3, |f| = 3, |r| = 9 の RealTimeDeque q1 に対して • snoc q1 x ⇒ |f| = 3, |r| = 10 • snoc (snoc q1 x) y ⇒ |f| = 3, |r| = 11 ⇒ ローテーション ⇒ |f| = 7, |r| = 7 2. c=3, |f| = 7, |r| = 18 の RealTimeDeque q2 に対して • tail q2 ⇒ |f| = 6, |r| = 18 • tail (tail q2) ⇒ |f| = 5, |r| = 18 ⇒ ローテーション ⇒ |f| = 12, |r| = 11OCamlによるサンプルコード: https://gist.github.com/3271021
  18. 18. おまけ: Global Rebuildingとの違いだいたいこんな感じだと思います。• Global Rebuildingには必要だったローテーション用のコピーが、Lazy Rebuildingで は不要。• Global Rebuildingを使用したHood MelvilleQueueでは、ローテーションに必要な処 理が比較的均等に分散していたが、Lazy Rebuildingを使用したRealTimeDequeで は、短い方のStreamを再作成する$CONSだけを使ってdropやreverse遅延させて いるので、サスペンションの実行コストに偏りがある。
  19. 19. 参考文献• Chris Okasaki, “8.4.3 Real-Time Deque”, Purely Functional Data Structures, Cambridge University Press (1999)• Chris Okasaki, “Simple and efficient purely functional queues and deques”, Journal of Functional Programming, 5(4):583-592, October 1995
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×