ソートされていない配列の中央値を見つける方法はありますか:1-ソートせずに。 2-選択アルゴリズムも中央値の中央値も使用しない
私と同じような質問が他にもたくさん見つかりました。しかし、ソリューションは、すべてではないにしても、ほとんどがSelectProblemとMedianOfMediansについて議論しました。
配列を並べ替えなくても、配列の中央値を確実に見つけることができます。簡単ではないのは、それを効率的に行うことです。
たとえば、配列の要素を反復処理するだけで済みます。各要素について、正しいカウントの値が見つかるまで、それ以下の要素の数をカウントします。それはO(n2)時間ですが、O(1)スペースのみです。
または、サイズが配列のちょうど半分のサイズの最小ヒープを使用することもできます。配列要素の前半を使用してヒープを構築し、残りの各要素x
について、x
がヒープの最小値より大きい場合は、min要素をx
に置き換えます。 。最後に、ヒープの最小要素は中央値です。それはO(n log n)時間とO(n)スペースです。
O(n)
ステップ(平均的なケースのシナリオ)でこのタスクを実行できるランダム化されたアルゴリズムがありますが、配列のいくつかのサブセットをソートする必要があります。そして、そのランダムな性質のために、それが実際に終了するという保証はありません(ただし、この不幸なイベントは、消滅する確率で発生するはずです)。
主なアイデアはここに残しておきます。より詳細な説明と、このアルゴリズムが機能する理由の証明については、 ここ を確認してください。
A
を配列とし、_n=|A|
_とします。 A
のすべての要素が異なると仮定しましょう。アルゴリズムは次のようになります。
A
からt = n^(3/4)
要素をランダムに選択します。T
を選択した要素の「セット」とします。T
を並べ替えます。pl = T[t/2-sqrt(n)]
とpr = T[t/2+sqrt(n)]
を設定します。A
の要素を繰り返し処理し、pl
よりも小さい要素(l
で示される)とpr
よりも大きい要素(r
で示される)の数を決定します。 _l > n/2
_または_r > n/2
_の場合は、手順1に戻ります。M
をA
とpl
の間のpr
の要素のセットとします。 M
は、ステップ5に到達した場合に備えて、ステップ4で決定できます。M
のサイズが_4t
_以下の場合は、M
を並べ替えます。それ以外の場合は、手順1に戻ります。m = M[n/2-l]
_を返します。アルゴリズムの背後にある主なアイデアは、中央の要素(つまり、pl
<pr
<pl
)を囲む2つの要素(m
とpr
)を取得して、これら2つが配列の順序付けられたバージョンで互いに非常に接近するようにすることです(これは実際に配列をソートせずに)。高い確率で、6つのステップすべてを1回実行するだけで済みます(つまり、最初からこれらの「適切な」プロパティを持つpl
とpr
を取得し、ステップ1〜5のみを通過するため、ステップ1に戻る必要はありません)。そのような要素が2つ見つかったら、それらの間にある要素を並べ替えて、A
の中央値要素を見つけることができます。
ステップ2とステップ5には、いくつかの並べ替えが含まれます(これは、不思議なことに確立した「ルール」に反する可能性があります:p)。サブ配列の並べ替えがテーブル上にある場合は、O(slogs)
ステップでこれを行う並べ替え方法を使用する必要があります。ここで、s
は並べ替える配列のサイズです。 T
とM
はA
よりも大幅に小さいため、並べ替えの手順はO(n)
の手順よりも「少ない」です。サブ配列をソートすることもルールに違反している場合は、どちらの場合もソートは実際には必要ないことを考慮してください。 pl
、pr
、およびm
を決定する方法を見つける必要があるだけです。これは、(それぞれのインデックスを使用した)別の選択問題です。 T
とM
を並べ替えるとこれが実現しますが、他の選択方法を使用することもできます(おそらく、前に提案した rici )。
非破壊ルーチンselip()については、 http://www.aip.de/groups/soe/local/numres/bookfpdf/f8-5.pdf で説明されています。データを複数回通過し、各段階で現在の値の範囲内のアイテムをランダムに選択し、アイテムの数をカウントしてランダム選択のランクを確立します。