部分ソート(3)

 実際の応用では、上記の(1),(2)の方法で十分な事が多いと思われる。しかしまぁ、他の方法も見てみよう。
 3つ目の方法は、quickselectを使う方法である。quickselectは、配列のk番目の要素を少ないコストで見付けるアルゴリズムだ。配列のk番目を見付けたければ、配列をquicksortしてからk番めにアクセスしても良い訳だが、全部をソートするのは明らかに無駄である。
 quickselectは、quicksortで不要な部分のソートをサボりながらk番目の要素を見付ける、と表現すると、直感的にはわかりやすいかもしれない。
 quicksortはpivotを選択して、そのpivotの値よりも大きいか小さいかで要素を分ける(この操作をpartitionと呼んでおこう)という操作を再帰的に繰り返すことによってソートを行う。しかし、k番目の要素が見付けたいだけであるなら、以下のようにすると計算をサボれる。

  if(length(left_partition) == k ) {
    return last(left_partition);
  } else if(length(left_partition) > k ) {
    return quickselect(left_partition, k);
  } else {
    return quickselect(right_patition, k - length(left_partition));
  }

 quickselectを行うと、k番目の要素と、それよりも小さなk-1個の要素が手に入る。このk-1個の要素はソートされていないので、quicksortとかでソートしてやれば、部分ソートが行える。段々眠くなってきたので計算は省くけれど、quickselectの平均計算量はnであるらしい。(ついでに、最悪はn^2。)
 最後のソートは明らかにm log mであるので、この場合の計算量はn + m log mになる。n log nと比べると、それなりに魅力的になったのではないだろうか。
 実はこの話には元ネタとなるファイルがあって、しかもさらに続きがある(が理解できなかったので省略した)ので、興味のある人は「On Partial Sorting」とか「chunksort」とかでぐぐるといいかもしれない。