BFPRT algorithm
-
Upload
satoshi-matsuura -
Category
Documents
-
view
2.086 -
download
0
Transcript of BFPRT algorithm
中央値ソートを復習しましょう
2
3
中央値ソート7 2 8 0 6 3 5
中央値を探し、中央の要素とスワップ
6 7 83 2 0 5
7 2 8 5 6 3 0中央値より大きい左側の要素を、中央値以下の右側の要素とスワップ
中央値の左右で配列を分割し、再帰的に部分配列を整列させる
6 7 80 2 3 52つ、または3つの部分配列で上記処理が終わると、ソートが完了する。
(1つの部分配列の整列には意味が無い)
再帰的に整列させる部分配列のサイズがほぼ等しくなる(半分になっていく)ため、O(n log n)のコストと考えられる。
そもそも中央値を求める方法は?
4
5
7 2 8 0 6 3 50 1 2 3 4 5 6
中央値(3番目に小さい数)を探すことが目的
partition関数
int partition(array, left, right, pivotIndex)
・配列の特定範囲(left, right)をpivot要素を基準に2分割する。・前半はpivot要素以下の値に、後半はpivot要素より大きな値に分割する。・pivot要素 = array[pivotIndex]であり、left ≦ pivotIndex ≦ rightである。・返値は分割後のpivot要素の位置を表す。
? partition関数があったとしてどのように中央値が算出できるか? partition関数の実装はどのようなものが考えられるか
partition関数の中身を見ていこう
6
7
7 2 8 0 6 3 5
7 2 8 0 6 5 3
0 1 2 3 4 5 6
7 2 8 0 6 5 3
② 7 8 0 6 5 3
② 7 8 0 6 5 3
partition(0,6,5)開始5番目の要素を選ぶ
一番右側とスワップ(一時的な待避)
3以下の数を左側から探す
発見できたら左側からスワップ
3以下の数を継続して探す
② ⓪ 8 7 6 5 3発見できたら左側からスワップ
② ⓪ 8 7 6 5 33以下の数を探し終端まで到達
② ⓪ ③ 7 6 5 8発見できたら左側からスワップ
0 1 2 3 4 5 6
8
7 2 8 0 6 3 50 1 2 3 4 5 6
② ⓪ ③ 7 6 5 8
初期状態
partition実行後
欲しい数 :3番目に小さな値(中央値)得られた情報:選択したpivot要素(3)は2番目の位置
② ⓪ ③ 7 6 5 8
pivot要素以下の数、ここでは3以下の要素で構成される
pivot要素より大きい数、ここでは3より大きな要素で構成される
0 1 2 3 4 5 6
partition(0,6,5)pivot位置を5番目と選ぶ
partition(0,6,5)の結果pivotは2番目の位置
pivot要素が3番目の位置であったなら、それが中央値であったのに残念、、、次は右側の配列の中で0番目に大きな数を探そう。つまりpartition(3,6,0)を実行しよう。
再帰的に追っていけば、いつかは見つかるはず。
9
7 2 8 0 6 3 50 1 2 3 4 5 6
② ⓪ ③ ⑤ 6 8 7
初期状態
partition実行後
partition(0,6,6)pivot位置を6番目と選ぶ
partition(0,6,6)の結果pivotは3番目の位置
7 2 8 0 6 3 50 1 2 3 4 5 6
② ⓪ ③ 7 6 5 8
初期状態
partition実行後
partition(0,6,5)pivot位置を5番目と選ぶ
partition(0,6,5)の結果pivotは2番目の位置
7 2 8 0 6 3 50 1 2 3 4 5 6
⑤ ② ⓪ ⑥ ③ 7 8
初期状態
partition実行後
partition(0,6,0)pivot位置を0番目と選ぶ
partition(0,6,0)の結果pivotは5番目の位置
k = pの場合:得られたpivot要素が中央値
k > pの場合:[p+1, right]からk-p-1番目に小さな値が中央値となる。
[p+1, right]にpartitionを実行。
k < pの場合:[left, p-1]からk番目に小さな値が中央値となる。[left, p-1]にpartitionを実行。
k: 求めるべき中央値の位置(ここではk=3)p: 得られたpivot位置
10
② ⓪ ③ 7 6 5 8
pivot要素以下の数、ここでは3以下の要素で構成される
pivot要素より大きい数、ここでは3より大きな要素で構成される
0 1 2 3 4 5 6
7 2 8 0 6 3 50 1 2 3 4 5 6
② ⓪ ③ 7 6 5 8
初期状態
partition実行後
partition(0,6,5)pivot位置を5番目と選ぶ
partition(0,6,5)の結果pivotは2番目の位置
partition実行後はpivotを基準に左側に小さな部分配列が、右側に大きな部分配列が出来た事を思いだそう。
*partition関数動作例(再掲)
1.partition関数を再帰的に実行2.中央値の発見3.発見したときにはすでに左側の部分配列は中央値以下に、 右側の部分配列は中央値より大きくなっている
すぐに下位の部分配列の整列に取りかかれる
*中央値ソートの手順
中央値ソートも中央値の発見方法もわかった :)
11
でも、partition実行時のpivot選択はどうするのか?
12
13
partition関数のpivot選択pivotの選択は両端のいずれかから順に選択する方法、ランダムに選択する方法など多くの方法が考えられ、pivot選択方法が全体のパフォーマンスを決定する。
・pivotの選択を誤ると中央値を探すためにpartition関数を多く実行する必要がある・partition関数を1回実行した時のコストはO(n)・最悪時はpartition1回につき一要素しか絞り込めない時であり、n-1回partitionを実行する必要がある。つまり一回の中央値を得るコストがおよそn^2となる。一段下位の部分配列に関してもおよそ2 * (n / 2)^2のコストがかかる。コストの総和は2*n^2を越えないので、partition全体のコストはO(n^2)。このコストが支配的になり全体でもO(n^2)となる。
*最悪時のコスト
なんだか面倒な事になっている。クイックソート使った方が。。。
pivot選択の失敗に引きずられて、
O(n^2)になるのは嫌だ。。
14
それ、BFPRTで出来るよ
15
BFPRTアルゴリズム
・目的 真の中央値に近似した値をO(n)のコストで探す。
・概要 1.対象の配列を短い部分配列(オリジナルアルゴリズムでは5)に分割し、 それぞれの部分配列の中央値からなる配列を作る。 2.中央値からなる配列に1の手順を再帰的に適応し、1つになるまで 絞り込む。 3.最終的に得られた(中央値に近似した)1つの要素をpivotとして利用する。
16
BFPRTアルゴリズムの手順23 7 9 81 17 11 34 39 44 28 3 72 54・・・ソート対象の配列
23
7
9
81
17
11
34
39
44
28
12
98
3
72
54
・・・5個の配列に分割し、
それぞれで中央値を求める
23
7
17
81
9
11
39
34
44
28
12
98
54
72
3
・・・
5417 34 ・・・
部分配列の中央値を集め、上記の手順を再帰的に繰り返す。つまり、5個の配列に分割し、それぞれで中央値を求め、中央値の集合で配列を作る。またその配列を...と繰り返す
34
最後に残った要素は真の中央値に近似した値となり、これをpivotして利用する
partition(array, left, right, 34)
17
BFPRTのコスト
・5個の配列の中央値を得るために必要な数値比較の回数は8以下 (条件分岐のツリーを書いて確認してみてください)
・配列をnとすると部分配列の中央値の集合を得るためには 最悪時で(n/5) * 8 = 1.4nの数値比較が必要。
・中央値の集合は元の配列の1/5の長さになっているので、 BFPRT全体のコストは下記に示すようにO(n)となる。 8n/5 + 8n/(5^2) + 8n/(5^3) ・・・ < 8n/4 = 2n
18
BRPRTで中央値の近似値がO(n)で求まることが分かった。
で、本当に中央値の近似値なの?
19
中央値と部分配列の中央値群ソート対象の配列 23 7 9 81 17 11 34 39 44
7 9 11 17 23 34 39 44 81ソート済みの状態と真の中央値
7 9 11 17 23 34 39 44 81
A群 B群 C群
ソート済みの配列をA<B<Cとなるように3等分にグループ化する
ソート対象の配列を部分配列に分割
23 7 9 81 17 11 34 39 44
B A A C B A B C C
9
A
17
B
39
C
17
B
部分配列の中央値を求める
中央値群から中央値を求める
B群以外から最終的な要素が選択される事はあるのだろうか?
ここでは簡単のため部分配列長を”3”とする
20
中央値と部分配列の中央値群
*中央値群にBが選択されない例
9個の要素からは必ずB群(中央値近辺1/3)の要素が選択される
・B群が最終的な要素に選択されない → 部分配列の中央値群にBを選択しない → できる限りBを含む部分配列を作りかつBが中央値にならない組み合わせを考える → AAB, BCCの組み合わせが一番適している → 残りの組み合わせがABCとなり中央値群にBが選択されてしまう → 最終的にB群の要素が選択される → 命題に矛盾する
23 7 9 81 39 4434 39 44
B A A C C CB C C
9
A
39
C
39
C
9 7 11
A A A
9
A
上記二つの例の様にBを選択しないためにはAAB、BCCの組み合わせが最適。AAB->A, BCC->Cが選択される。しかし残りの組み合わせ(ABC)で必ずBが選択され、結果的にBを選択せざる得ない。
21
中央値と部分配列の中央値群・9個の要素から中央値群を取り出すと最低でも1つB群の要素を含む →9個の要素からは必ずB群(中央値近辺1/3)の要素が選択される。
(一般化して長い配列を考えてみよう)・3つの部分配列を選択し、その9個の要素においてA, B, Cの数が等しい場合、 中央値群を選択すると最低でも1つのB群の要素を含む。・また数列全体において、A, B, Cの数は等しいため、全体の中央値群を選択すると 最低でも中央値群の1/3はB群の要素を含む。
A C C B A C A B C A A C B B A B C C A B A B B C A B C
C B B A B C A B B
B B B
B
A C C B A A A B C A A C B B A B A A C B C B B C C B C
C A B A B A C B C
B A C
B
ABC, ABC, ABCの様な部分配列の組が出来るとB群が選択されやすくなる
意図的にB群が選択されにくいようにしたケース。中央値群の1/3は常にB群が選択されている。
帰納的に、元の配列のB群(中央値近辺1/3)の要素が選択される
22
部分配列の長さが「3」の時、中央値周辺の1/3の値が近似値として選ばれる事が分かった*。
*BFPRTオリジナルは「5」の部分配列長を持ちます。より精度の高い近似値が得られます。同じ要領で出来るので確認してみてください。
23
つまり、最悪時でも配列を1/3と2/3の大きさで分割でき、1/2で分割する時と比べて最悪時で定数倍(2倍)*コストがかかる。→O(n log n)のコストが
保証される*等比級数の和で分割の深さを考える Σ(1/2)^n = 1 (n→∞) Σ(2/3)^n = 2 (n→∞)
24
BFPRTまとめ
・中央値選択のコストをかけすぎてO(n^2)になるのを避けたい(最悪時)・元の配列を固定長の部分配列に分割し、各々の配列から中央値を取り出して、 中央値群を作成し、同様の操作を再帰的に繰り返して中央値の近似値を得る・近似値を得る過程はO(n)で求まる・部分配列長が3の時(オリジナルは5)、近似値は全配列をソートした場合の 中央1/3の要素から選択される。つまり最悪時でも1/3, 2/3分割が出来る。・分割過程は中央1/2で分割する場合の定数倍コストがかかる。・BFPRTを使ってソートを行う場合のコストはO(n log n)である。
25
参考文献
アルゴリズム クイックリファレンス, George T. Heineman, Gary Pollice, Stanley Selkow (著), 黒川 利明, 黒川 洋(訳).4.3 中央値ソート, pp.74-86, オライリージャパン, 2010.