Frederickson and Johnson アルゴリズムは、おおよそ次のように機能します。(いつも思い出すのはQuickselectです。) これは、別の実装をテンプレートとして使用した実装からのものであるため、詳細は彼らの論文とは少し異なる場合がありますが、同じ時間の複雑さと同じ考え方です。k
run from 0
toの正当な値を想定していますM * N - 1
。
(0 から数えて)番目のi
行とj
番目の列の要素にアクセスします。A[i, j]
さらに、A[i, -1]
どちらが負の無限大で、A[i, N]
どちらが正の無限大であるかにアクセスできると仮定します。次のプロパティを持つインデックスleft[0 .. M)
との 2 つの配列right[0..M)
、および 2 つの変数lessthanleft
とを維持します。greaterthanright
- 行列要素が存在する
A[i0, j0]
ため、すべての行i
で が得られA[i, left[i] - 1] < A[i0, j0] <= A[i, left[i]]
ます。つまり、left[i]
おおよそ、A[i0, j0]
それが row にある場合の位置i
です。
- からまでの値の合計
left[i]
はです。は、 で示される位置の左側の位置にある行列要素の数です。つまり、厳密に 未満の行列要素です。i
0
M - 1
lessthanleft
lessthanleft
left
A[i0, j0]
lessthanleft <= k
. これは、探している要素がi
の右側の行にあることを示していますleft[i]
。
- 行列要素が存在する
A[i1, j1]
ため、すべての行i
で が得られA[i, right[i] - 1] < A[i1, j1] <= A[i, right[i]]
ます。つまり、right[i]
おおよそ、A[i1, j1]
それが row にある場合の位置i
です。
- からまでの値の合計
N - right[i]
はです。は、 で示される位置の右側の位置にある行列要素の数です。つまり、厳密に よりも大きい行列要素です。i
0
M - 1
greaterthanright
greaterthanright
right
A[i1, j1]
greaterthanright <= M * N - k
. これは、探している要素がi
の左側の行にあることを示していますright[i]
。
これらのプロパティは、 to のすべての要素、left
to0
のすべての要素、およびright
toN
の両方を設定することによって初期化できます (この場合は を返します) または(この場合は を返します)。ちなみに、これは 、 、 、 に対応します。lessthanleft
greaterthanright
0
k = 0
A[0, 0]
k = M*N - 1
A[M-1, N-1]
i0=0
j0=0
i1=M-1
j1=N-1
ここで、すべての反復で、ピボット マトリックス要素をA[i2, j2]
で選択しleft[i2] <= j2 < right[i2]
ます。(いくつかの戦略があります。これについては後で説明します。) 配列less[0..M)
とを使用lessequal[0..M)
して、次の内部ループに入力し、変数nless
およびnequal
をngreater
に設定します0
。内側のループでは、すべての行を反復処理しi
、 and を設定less[i]
しlessequal[i]
て、 A[i, less[i] - 1] < A[i2, j2] <= A[i, less[i]]
andを設定しA[i, lessequal[i] - 1] <= A[i2, j2] < A[i, lessequal[i]]
ます。(とless[i-1] >= less[i]
の値を使用して、そこから左に線形探索を開始すると、全体的に時間がかかります。) このような各ステップの後、 に追加し、 に追加し、 に追加します。less[i-1]
lessequal[i-1]
O(N)
less[i] - left[i]
nless
lessequal[i] - less[i]
nequal
right[i] - lessequal[i]
ngreater
この内側のループの後、 かどうかを確認しますlessthanleft + nless >= k
。この場合は、 beに設定して(配列のループでの割り当てを防止する場合はポインターのフリッピングを行うか、 から に値をコピーして)、beA[i2, j2]
に設定するよりも少ないエントリを続行し、次の処理に進みます。反復。の場合、 beおよびbeに設定して次の反復を続行することにより、より大きいエントリを続行します。それ以外の場合、探しているエントリは に等しいエントリの中にあるため、 を返します。right
less
less
right
greaterthanright
greaterthanright + ngreater + nequal
lessthanleft + nless + nequal < k
A[i2, j2]
left
lessequal
lessthanleft
lessthanleft + nless + nequal
A[i2, j2]
A[i2, j2]
次に、ピボットの選択についてです。c
1 つの方法は、間隔で乱数を選択すること[0 .. N * M - lessthanleft - greaterthanright)
です。次に、とc
の間の 番目の行列要素を含む行を、から開始して負になるまで から減算することによって見つけます。負になる場所を選択して. または、各行の残りのエントリの中央値に対して中央値スタイルの計算を行うこともできます。left
right
left[i] - right[i]
c
i
0
i
j = round((left[i] + right[i])/2)
したがって、一般的に、各行の間にある行列要素のセットは分割され、できれば反復ごとに約半分になりleft[i]
ます。複雑さの分析は、予想される反復回数が初期プール内の値の数で「ほぼ確実に」(数学的な意味で) 対数になるQuickselectright[i]
の分析に似ています。中央値の中央値スタイルのピボット選択を使用することで、実際の実行時に支払いを行いながら確実にこれを達成できると思いますが、ここでは「ほぼ確実」で十分であると仮定します。(これは、Quicksort が O(N lg(N)) であるのと同じ条件です。) 個々の反復では、ピボットを選択するのに時間がかかり、次に内側のループで時間がかかります。候補者の初期プールはO(M)
O(N)
M*N
O(lg(M * N)) = O(lg(M) + lg(M))
メンバーなので、合計の複雑さに対して、反復回数はほぼ確実に ですO(N * (lg(M) + lg(N)))
。すべての行のエントリでヒープを使用するアルゴリズムは、によってのみ制限されるO(k * lg(M))
ため、さらに悪い結果になります。k
N*M
簡単な例M=4
を次に示します。N=5
k=11
A
6 12 17 17 25
9 15 17 19 30
16 17 23 29 32
23 29 35 35 39
、、および両方をに初期化left
します。[0,0,0,0]
right
[5,5,5,5]
lessthanleft
greaterthanright
0
最初の反復をA[1, 2]=17
ピボットとして選択するとします。内側のループ中に、最初の行のエントリright[0]-1=4
から左への調査を開始し、最大 である数値の最初の出現を見つけ17
ます。これは列3
にあるので、設定しlessequal[0]=3+1=4
ます。厳密に より小さい数値の最初の出現を検索し続け17
ます。これは列で発生する1
ため、 を設定しless[0]=1+1=2
ます。17
次の行では、最大で columnにある値を探し始めることができ、lessequal[0]
それを column 2
so setで見つけますlessequal[1]=2+1=3
。17
から始まる値より厳密に小さい値を探してless[0]
、 を設定しless[1] = 2
ます。続けて と を取得less = [2,2,1,0]
しlessequal = [4,3,2,0]
ます。したがってnless = 5
、、、nequal = 4
_ngreater = 11
. を持っているlessthanleft + nless + nequal = 9 < 11
ので、 より大きい17
と と を設定left = lessequal = [4,3,2,0]
したエントリを続けますlessthanleft = 9
。
次の反復では、とi
の間の中央にある行のピボットを選択する必要がleft[i]
ありright[i]
ます。行 を選択2
すると、 があることになりますA[2,3] = 29
。内側のループ中に、less = [5,4,3,1]
andとlessequal = [5,4,4,2]
でnless = 4
andを取得します。では、以下のエントリと setおよびのエントリを続けます。nequal = 2
ngreater = 5
lessthanleft + nless = 13 > 11
29
right = less = [5,4,3,1]
greaterthanright = 7
3 回目の反復では、すべての行に 1 つのエントリしか残っていません。行をランダムに選択します - おそらく行3
です。ピボットはA[3,0] = 23
. 内側のループ中に、 と を取得less = [4,4,2,0]
しlessequal = [4,4,3,1]
ます。したがってnless = 1
、nequal = 2
、およびngreater = 1
。を持っているlessthanleft + nless = 10 < k = 11 <= lessthanleft + nless + nequal = 12
ので、 を返し23
ます。実際、厳密に 10 個の行列エントリ23
(A[i, less[i]]
最後の反復で の左側にあるもの) と最大で 12 個の行列エントリ23
(最後の反復で の左側にあるものA[i, lessequal[i]]
) があります。