SlideShare a Scribd company logo
BFPRT algorithm
  Blum-Floyd-Pratt-Rivest-Tarjan, 1973




  MATSUURA Satoshi
  matsuura@is.naist.jp




                   1
中央値ソートを
復習しましょう


   2
中央値ソート
       7   2   8   0   6   3   5
                               中央値を探し、中央の要素とスワップ



       7   2   8   5   6   3   0
                               中央値より大きい左側の要素を、中央
                                 値以下の右側の要素とスワップ



      3    2   0   5   6   7   8
                                   中央値の左右で配列を分割し、
                                   再帰的に部分配列を整列させる



      0    2   3   5   6   7   8
                               2つ、または3つの部分配列で上記処理
                                 が終わると、ソートが完了する。
                               (1つの部分配列の整列には意味が無い)



再帰的に整列させる部分配列のサイズがほぼ等しくなる(半分に
  なっていく)ため、O(n log n)のコストと考えられる。
                   3
そもそも中央値を
 求める方法は?


   4
partition関数
                           0        1       2        3           4   5   6

中央値(3番目に小さい数)を
   探すことが目的                7        2       8        0        6       3   5

                 int partition(array, left, right, pivotIndex)


   ・配列の特定範囲(left, right)をpivot要素を基準に2分割する。
   ・前半はpivot要素以下の値に、後半はpivot要素より大きな値に分割する。
   ・pivot要素 = array[pivotIndex]であり、left pivotIndex rightである。
   ・返値は分割後のpivot要素の位置を表す。




? partition関数があったとしてどのように中央値が算出できるか
? partition関数の実装はどのようなものが考えられるか




                                   5
partition関数の
中身を見ていこう


     6
0   1   2   3   4   5   6

partition(0,6,5)開始
 5番目の要素を選ぶ           7   2   8   0   6   3   5

一番右側とスワップ
 (一時的な待避)            7   2   8   0   6   5   3

   3以下の数を
   左側から探す            7   2   8   0   6   5   3

  発見できたら
 左側からスワップ            ②   7   8   0   6   5   3

   3以下の数を
   継続して探す            ②   7   8   0   6   5   3

  発見できたら
 左側からスワップ            ②   ⓪   8   7   6   5   3

 3以下の数を探し
  終端まで到達             ②   ⓪   8   7   6   5   3

  発見できたら
 左側からスワップ            ②   ⓪   ③   7   6   5   8
                     0   1   2   3   4   5   6




                         7
0       1       2       3       4       5       6

                                                                            partition(0,6,5)
     初期状態      7        2       8       0       6       3       5       pivot位置を5番目と選ぶ


                                                                        partition(0,6,5)の結果
partition実行後   ②    ⓪       ③           7       6       5       8        pivotは2番目の位置




               pivot要素以下の数、ここでは             pivot要素より大きい数、ここでは
                 3以下の要素で構成される                 3より大きな要素で構成される


                    ②       ⓪       ③       7       6       5       8
                    0       1       2       3       4       5       6



        欲しい数  :3番目に小さな値(中央値)
        得られた情報:選択したpivot要素(3)は2番目の位置




   pivot要素が3番目の位置であったなら、それが中央値であったのに残念、、、
 次は右側の配列の中で0番目に大きな数を探そう。つまりpartition(3,6,0)を実行しよう。
             再帰的に追っていけば、いつかは見つかるはず。


                                            8
k: 求めるべき中央値の位置(ここではk=3)
                             p: 得られたpivot位置


                                                0       1   2   3   4   5   6

                                                                                    partition(0,6,6)
                                      初期状態      7       2   8   0   6   3   5   pivot位置を6番目と選ぶ
     k = pの場合:
  得られたpivot要素が中央値
                                                                                partition(0,6,6)の結果
                                 partition実行後   ②       ⓪   ③   ⑤   6   8   7    pivotは3番目の位置




                                                0       1   2   3   4   5   6

         k > pの場合:                                                                  partition(0,6,5)
                                      初期状態      7       2   8   0   6   3   5   pivot位置を5番目と選ぶ
  [p+1, right]からk-p-1番目に
   小さな値が中央値となる。
                                                                                partition(0,6,5)の結果
[p+1, right]にpartitionを実行。       partition実行後   ②       ⓪   ③   7   6   5   8    pivotは2番目の位置




                                                0       1   2   3   4   5   6

         k < pの場合:                                                                  partition(0,6,0)
                                      初期状態      7       2   8   0   6   3   5   pivot位置を0番目と選ぶ
    [left, p-1]からk番目に
   小さな値が中央値となる。
                                                                                partition(0,6,0)の結果
[left, p-1]にpartitionを実行。        partition実行後   ⑤       ②   ⓪   ⑥   ③   7   8    pivotは5番目の位置




                                                    9
*中央値ソートの手順


    1.partition関数を再帰的に実行
    2.中央値の発見
    3.発見したときにはすでに左側の部分配列は中央値以下に、
      右側の部分配列は中央値より大きくなっている




            すぐに下位の部分配列の整列に取りかかれる


    *partition関数動作例(再掲)
                    0   1   2    3       4        5   6

                                                              partition(0,6,5)
          初期状態      7   2   8    0       6        3   5   pivot位置を5番目と選ぶ



                                                          partition(0,6,5)の結果
     partition実行後   ②   ⓪   ③    7       6        5   8    pivotは2番目の位置




                                pivot要素以下の数、ここでは          pivot要素より大きい数、ここでは
partition実行後はpivotを基準に            3以下の要素で構成される              3より大きな要素で構成される
左側に小さな部分配列が、
右側に大きな部分配列が                          ②        ⓪       ③   7          6           5   8
出来た事を思いだそう。
                                     0        1       2    3         4           5   6


                                         10
中央値ソートも
中央値の発見方法も
  わかった :)


    11
でも、partition実行時の
pivot選択はどうするのか?


        12
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)となる。

                         なんだか面倒な事になっている。クイックソート使った方が。。。




                         13
pivot選択の失敗に
     引きずられて、
O(n^2)になるのは嫌だ。。


それ、BFPRTで出来るよ

       14
BFPRTアルゴリズム

・目的
 真の中央値に近似した値をO(n)のコストで探す。


・概要
 1.対象の配列を短い部分配列(オリジナルアルゴリズムでは5)に分割し、
   それぞれの部分配列の中央値からなる配列を作る。
 2.中央値からなる配列に1の手順を再帰的に適応し、1つになるまで
   絞り込む。
 3.最終的に得られた(中央値に近似した)1つの要素をpivotとして利用する。




                    15
BFPRTアルゴリズムの手順
   ソート対象の配列         23   7        9    81    17       11   34   39   44    28        ・・・    3       72   54



                    23       11                  12                   23        11          12

                    7        34                  98                   7         39          98
  5個の配列に分割し、
                    9        39       ・・・        3                    17        34    ・・・   54
 それぞれで中央値を求める
                    81       44                  72                   81        44          72

                    17       28                  54                   9         28              3



  部分配列の中央値を集め、
上記の手順を再帰的に繰り返す。
 つまり、5個の配列に分割し、
                    17   34       ・・・        54                           34
  それぞれで中央値を求め、
 中央値の集合で配列を作る。
 またその配列を...と繰り返す




 最後に残った要素は真の中
 央値に近似した値となり、      partition(array, left, right, 34)
 これをpivotして利用する




                                            16
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




                       17
BRPRTで中央値の近似値が
O(n)で求まることが分かった。


で、本当に中央値の近似値なの?


       18
中央値と部分配列の中央値群
     ソート対象の配列           23          7           9    81   17   11   34       39       44



 ソート済みの状態と真の中央値             7           9       11   17   23   34   39       44       81


ソート済みの配列をA<B<Cとなる                                                                                ここでは簡単のため
                        7           9       11       17   23   34        39       44       81
 ように3等分にグループ化する                                                                                 部分配列長を 3 とする

                                A群                        B群                      C群




                    B           A           A        C    B    A         B        C        C
   ソート対象の配列を
                    23          7           9        81   17   11    34       39       44
    部分配列に分割

                                A                         B                       C

  部分配列の中央値を求める                  9                         17                  39


                                                          B

 中央値群から中央値を求める                                            17




       B群以外から最終的な要素が選択される事はあるのだろうか?

                                                          19
中央値と部分配列の中央値群
・B群が最終的な要素に選択されない
 → 部分配列の中央値群にBを選択しない
 → できる限りBを含む部分配列を作りかつBが中央値にならない組み合わせを考える
 → AAB, BCCの組み合わせが一番適している
 → 残りの組み合わせがABCとなり中央値群にBが選択されてしまう
 → 最終的にB群の要素が選択される
 → 命題に矛盾する

    *中央値群にBが選択されない例

      B   A   A   B    C    C    A   A    A    C    C    C

     23   7   9   34   39   44   9   7    11   81   39   44


          A            C             A              C

          9            39            9              39



     上記二つの例の様にBを選択しないためにはAAB、BCCの組み合わせが最適。AAB->A, BCC->Cが選択される。
     しかし残りの組み合わせ(ABC)で必ずBが選択され、結果的にBを選択せざる得ない。



    9個の要素からは必ずB群(中央値近辺1/3)の要素が選択される


                                     20
中央値と部分配列の中央値群
    ・9個の要素から中央値群を取り出すと最低でも1つB群の要素を含む
     →9個の要素からは必ずB群(中央値近辺1/3)の要素が選択される。


     (一般化して長い配列を考えてみよう)
    ・3つの部分配列を選択し、その9個の要素においてA, B, Cの数が等しい場合、
     中央値群を選択すると最低でも1つのB群の要素を含む。
    ・また数列全体において、A, B, Cの数は等しいため、全体の中央値群を選択すると
     最低でも中央値群の1/3はB群の要素を含む。

           帰納的に、元の配列のB群(中央値近辺1/3)の要素が選択される


                            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
  ABC, ABC, ABCの様な部分配列の組が
    出来ると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群が選択されにくいようにしたケース。
 中央値群の1/3は常にB群が選択されている。
                                   B                  A                 C

                                                      B

                                    21
部分配列の長さが「3」の時、
中央値周辺の1/3の値が近似値
として選ばれる事が分かった*。

            *
            BFPRTオリジナルは「5」の部分配列長を持ちます。
            より精度の高い近似値が得られます。同じ要領で出来
            るので確認してみてください。
       22
つまり、最悪時でも配列を1/3と
2/3の大きさで分割でき、1/2で
 分割する時と比べて最悪時で
 定数倍(2倍)*コストがかかる。
   →O(n log n)のコストが
       保証される
              *等比級数の和で分割の深さを考える
               Σ(1/2)^n = 1 (n→ )
               Σ(2/3)^n = 2 (n→ )

         23
BFPRTまとめ


・中央値選択のコストをかけすぎてO(n^2)になるのを避けたい(最悪時)
・元の配列を固定長の部分配列に分割し、各々の配列から中央値を取り出して、
 中央値群を作成し、同様の操作を再帰的に繰り返して中央値の近似値を得る
・近似値を得る過程はO(n)で求まる
・部分配列長が3の時(オリジナルは5)、近似値は全配列をソートした場合の
 中央1/3の要素から選択される。つまり最悪時でも1/3, 2/3分割が出来る。
・分割過程は中央1/2で分割する場合の定数倍コストがかかる。
・BFPRTを使ってソートを行う場合のコストはO(n log n)である。




                   24
参考文献

アルゴリズム クイックリファレンス,
George T. Heineman, Gary Pollice, Stanley Selkow (著),
黒川 利明, 黒川 洋(訳).
4.3 中央値ソート, pp.74-86, オライリージャパン, 2010.




                            25

More Related Content

What's hot

続わかりやすいパターン認識11章(11.1 - 11.4)
続わかりやすいパターン認識11章(11.1 - 11.4)続わかりやすいパターン認識11章(11.1 - 11.4)
続わかりやすいパターン認識11章(11.1 - 11.4)
Nagi Teramo
 
Tokyor35 人工データの発生
Tokyor35 人工データの発生Tokyor35 人工データの発生
Tokyor35 人工データの発生
Yohei Sato
 
はじめてのパターン認識 第8章 サポートベクトルマシン
はじめてのパターン認識 第8章 サポートベクトルマシンはじめてのパターン認識 第8章 サポートベクトルマシン
はじめてのパターン認識 第8章 サポートベクトルマシン
Motoya Wakiyama
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造
Takuya Akiba
 

What's hot (20)

代数トポロジー入門
代数トポロジー入門代数トポロジー入門
代数トポロジー入門
 
劣モジュラ最適化と機械学習 2.4節
劣モジュラ最適化と機械学習 2.4節劣モジュラ最適化と機械学習 2.4節
劣モジュラ最適化と機械学習 2.4節
 
最近の自然言語処理
最近の自然言語処理最近の自然言語処理
最近の自然言語処理
 
決定木学習
決定木学習決定木学習
決定木学習
 
Rolling hash
Rolling hashRolling hash
Rolling hash
 
続わかりやすいパターン認識11章(11.1 - 11.4)
続わかりやすいパターン認識11章(11.1 - 11.4)続わかりやすいパターン認識11章(11.1 - 11.4)
続わかりやすいパターン認識11章(11.1 - 11.4)
 
実用Brainf*ckプログラミング
実用Brainf*ckプログラミング実用Brainf*ckプログラミング
実用Brainf*ckプログラミング
 
Pythonで時系列のデータを分析してみよう
Pythonで時系列のデータを分析してみようPythonで時系列のデータを分析してみよう
Pythonで時系列のデータを分析してみよう
 
未来を予測せよ 〜あるエンジニアが挑んだKaggleコンペの軌跡〜
未来を予測せよ 〜あるエンジニアが挑んだKaggleコンペの軌跡〜未来を予測せよ 〜あるエンジニアが挑んだKaggleコンペの軌跡〜
未来を予測せよ 〜あるエンジニアが挑んだKaggleコンペの軌跡〜
 
Za atsu-20170328
Za atsu-20170328Za atsu-20170328
Za atsu-20170328
 
Anomaly detection survey
Anomaly detection surveyAnomaly detection survey
Anomaly detection survey
 
(DL輪読)Matching Networks for One Shot Learning
(DL輪読)Matching Networks for One Shot Learning(DL輪読)Matching Networks for One Shot Learning
(DL輪読)Matching Networks for One Shot Learning
 
2部グラフの最小点被覆の求め方
2部グラフの最小点被覆の求め方2部グラフの最小点被覆の求め方
2部グラフの最小点被覆の求め方
 
Tokyor35 人工データの発生
Tokyor35 人工データの発生Tokyor35 人工データの発生
Tokyor35 人工データの発生
 
異常検知と変化検知の1~3章をまとめてみた
異常検知と変化検知の1~3章をまとめてみた異常検知と変化検知の1~3章をまとめてみた
異常検知と変化検知の1~3章をまとめてみた
 
PRML 8.2 条件付き独立性
PRML 8.2 条件付き独立性PRML 8.2 条件付き独立性
PRML 8.2 条件付き独立性
 
はじめてのパターン認識 第8章 サポートベクトルマシン
はじめてのパターン認識 第8章 サポートベクトルマシンはじめてのパターン認識 第8章 サポートベクトルマシン
はじめてのパターン認識 第8章 サポートベクトルマシン
 
Jubatus Casual Talks #2 異常検知入門
Jubatus Casual Talks #2 異常検知入門Jubatus Casual Talks #2 異常検知入門
Jubatus Casual Talks #2 異常検知入門
 
プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造プログラミングコンテストでのデータ構造
プログラミングコンテストでのデータ構造
 
MCMC法
MCMC法MCMC法
MCMC法
 

BFPRT algorithm