バイナリ検索が3項検索よりも高速なのはなぜですか?


49

バイナリ検索を使用して要素の配列を検索するには、最悪の場合、回の反復が必要です。これは、各ステップで検索スペースの半分をトリミングするためです。代わりに、「三分探索」を使用した場合、各反復で探索空間の3分の2を切り捨てるので、最悪の場合は反復が必要です...log 2 N log 3 N < log 2 NNlog2Nlog3N<log2N

三項検索の方が速いように思えますが、なぜ二項検索を使用するのですか?


3
クォータナリ検索について同じ理由を使用できませんか?または、10進検索でも...または2より大きいものです。
d'alar'cop 14

4
B + Trees
arunmoezhi 14

5
線形検索は、キャッシュコヒーレントであり、ほとんどすべての分岐が正しく予測されるため、最新のハードウェアでの小規模から中規模の問題のバイナリ検索よりも高速です。
仮名14

2
また、2 * log_3(N)= log_3(N ^ 2)が直観に基づいている場合。
PawelP 14

6
これを直感的な用語にしましょう。3ベースの検索を使用する方が反復ごとに検索スペースを削減するために高速になる場合、100万ベースの検索を使用する方が高速ではありませんか?ただし、ターゲットを含む100万番目のスライスを特定するには、各反復内で平均500,000のチェックを行う必要があることが簡単にわかります。明らかに、反復ごとに検索スペースを半分に削減し、それ以上ではなく、1ステップで確実に最も多くの情報を提供します。
エリック14

回答:


76

バイナリ検索を適用すると、多くの比較があります。三項検索を適用すると、多くの比較があります。各ステップで、検索スペースを3つの部分に分割するために2つの比較を実行する必要があります。計算すると、次のことがます 我々は知っているので、その、我々は実際に得るより 3分探索との比較を。

log2(n)+O(1)
2log3(n)+O(1)
2log3(n)+O(1)=2log(2)log(3)log2(n)+O(1)
2log(2)log(3)>1

ちなみに、検索は、比較に非常にコストがかかり、並列コンピュータを適用できる場合に並列化できる場合に非常に意味があります。n

引数は検索に簡単に一般化できることに注意してください。関数が整数値に対して厳密に単調増加することを示す必要があります。F K = K - 1 ログ2 n kf(k)=(k1)log(2)log(k)k


1
そして、LHSは線形であると、それはより多くのそれよりも、第四級か何かのために助けにはなりませんので、RHSは対数です....ニースの説明....おかげで
平均二乗

3
完全を期すために、要素比較の数のような抽象的な尺度が実際のランタイムを支配する場合としない場合があることに注意してください。特に、どちらの検索でも長い配列で発生する可能性のあるキャッシュミスの数を考慮する必要があります。(ここでは、彼らが一致OPは「なぜそれが速いのですか?」、と抽象的措置でいくつかのアルゴリズムのために誤解を招くことができることを答えて、尋ねるので、私はちょうどこれを注目しています。。)
ラファエル

10
三項検索では、1/3の時間で1回の比較が必要になります(下位比較を行う:下位3分の1であれば、2回目の比較は不要です)。これにより、3進法が25%ではなく、約5%遅くなります(この世界では、比較カウントのみが重要です)。これをn-aryに一般化する方法はわかりませんが、バイナリよりも速くなることはないと思います。
アーロンデュフォー14

2
@AaronDufour:最初に中間の項目と比較してから他の比較の結果を無視することで四次検索を行うことができるため、四次検索を高速化できる唯一の方法は、2つの比較よりも安価に3つの比較を並行して実行できる場合です連続して実行できます。
supercat 14

1
@AaronDufourしかし、あなたは検索する要素について償却しているので、それがなぜ大丈夫なのかは私には明らかではありません。最悪の場合、すべてのステップで両方の比較が実行される場合があります。
サショニコロフ2014

26

DCTLibは正しいですが、数学をちょっと忘れてください。

あなたのロジックでは、n- aryが最も速いはずです。ただし、考えてみると、n -aryは通常の反復検索とまったく同じです(リストを1つずつ反復しますが、逆順です)。まず、リスト内の最後の(または最後から2番目の)アイテムを選択し、その値を比較値と比較します。次に、そのアイテムをリストから削除し、新しいリストの最後のアイテムを選択します。これは、配列の最後の値の直後にあります。毎回、値が見つかるまで一度に1つの値のみを削除します。

代わりに、このように考える必要があります-反復ごとにリストからほとんどの値を削除する方法は?バイナリ検索では、常にリストの半分を削除します。3項検索では、リストの2/3を削除できる可能性(実際には33.33%の可能性)がありますが、リストの1/3のみを削除する可能性はさらに大きくなります(66.66%)。O(n)を計算するには、最悪のシナリオ、つまり1 / 3、1 / 2未満を調べる必要があります。nに近づくにつれて、さらに悪化します。

バイナリ検索で最悪のシナリオが改善されるだけでなく、平均時間も改善されます。期待値(リストのどの部分を平均して削除できるか)を見て、次の式を使用します。

(P_lower)x(低い場合は削除できる部分)+(P_higher)x(高い場合は削除できる部分)= E

バイナリ検索の場合、これは.5x.5 + .5x.5 = .5です(リストの半分は常に削除されます)。三項検索の場合、この値は.666x.333 + .333x.666 = 0.44であるか、各ステップでリストの44%のみを削除する可能性が高く、平均して二分検索よりも効率が低下します。この値は1/2(リストの半分)でピークに達し、n(逆反復)と0(通常反復)に近づくほど減少します。

わかりましたので、私は嘘をついた..少し数学が関与しているが、私はそれが役立つことを願っています!


1
これは素晴らしい答えです。
The_Sympathizer

Ya境界分析は、難しい数学を理解するのに役立ちます!n項順次検索は、線形検索O(n)と同じコストです。
-shuva

-2

log(N)対2 log(N)比較引数は、アルゴリズムの単純な解釈に基づいていることに注意してください。実際に座ってx86アセンブリでこれを記述すると、結果は逆になります。問題は、冗長な比較を削除できない不十分なスマートコンパイラと組み合わされたテストケースの整数の使用です。文字列と適切な文字列比較関数を使用して再試行し、ループごとに1回比較関数を呼び出すようにコーディングすると、3項検索が再び高速になります。


2
もちろん、3回の検索は、反復ごとに1回の比較のみで実行できる場合は高速になります。しかし、文字列でも整数でも、できません。
FrankW 14

比較は冗長ではなく、問題はコンパイラとは関係ありません。サーチスペースを3つの部分に分割するには、2つの比較が必要です。バイナリ検索では、中央の要素と比較するだけで、結果が検索スペースのどの半分にあるかを知ることができます。3進検索では、リストおよびリストの2/3のリスト。比較するデータの種類や使用している言語は関係ありません。確かに、アイテムが1番目から3番目にある場合は、1回の比較後に停止できます。
reirab

2
一部のプラットフォームでは、CPUが比較のためにオペランドを必要とする前にRAMからオペランドをフェッチするためにより多くの時間を許可するため、3項検索が高速になる場合があります。しかし、それは使用されるプラットフォームとそのレイテンシーとキャッシュに完全に依存します。
jpa

1
くそー-三分探索の間違った定義。
ジョシュア14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.