ABC100の解説で触れられていた「中央値の中央値 (median of medians)」のことを調べたメモ。
〜
厳密なmedian値でなくていいのでそれっぽい値をO(n)で求める方法。少なくとも上下に個ずつ、それより小さい(or 大きい)値が存在することが示せる。
真のmedianの近似として、適当なpivotの選択に使える。
(配列の中でk番目に小さい値を選ぶときに使える。k=n/2とすれば当然真のmedianも求まる。quicksortのpivotにも使えるけど乱数でやったほうが速いとか。最悪計算量は減らせるけど)
原理は割と簡単
・[Round 1] 5個ずつのブロックに分けて、それぞれのmedianを代表として選ぶ
・[Round 2] 代表(n/5個)のmedianを1個選ぶ(この値をMとしよう)
n/5個の代表の半数(即ちn/10個)はM以下の値である。そして、それらを代表として選出したブロックにはその代表より小さい値が2つずつ存在するので、少なくとも全体の個の値はM以下である。上についても同様に、少なくとも全体の
個の値はM以上である。
配列の中でk番目に小さい値を選ぶ方法 (quickselect)
quickselectは、quicksortと同様に適当なpivotを選んで、partitionして、k番目に小さいの要素があるはずの区間に潜って(ないほうの区間については放置しつつ)k番目に小さい要素を得るアルゴリズムである。
(詳しくは作図の得意なフレンズのブログの図解を見ていただこう)
ここでmedian of mediansを利用することでmedianに近いpivotを高速に選び、最悪計算量)を減らすことができる。
実装
Wikipediaに書かれているアルゴリズムはそのままだと相互参照の終了条件がなくて止まらない
- Understanding "median of medians" algorithm - stackoverflow
- Deterministic selection - ICS 161 Design and Analysis of Algorithms Lecture notes for January 30, 1996
この辺りを見ながら実装。
partitionの所はquicksortの時の要領で。(苦手なので上記実装ではstd::partition()
でお茶を濁しているがそれくらい自分でさくっと書きたいところ)
実験
0〜N-1の値をシャッフルした配列で適当なkについてk番目の値を選ばせてみた
N=100: 0.008msec N=1000: 0.064msec N=10000: 0.579msec N=100000: 5.781msec N=1000000: 57.816msec
線形っぽい