コンピュータサイエンスでは、バイナリサーチは、ハーフインターバルサーチ[ 1 ] 、対数サーチ[ 2 ]、バイナリチョップ[ 3 ]とも呼ばれ、ソートされた配列内のターゲット値の位置を見つける検索アルゴリズムです。[ 4 ] [ 5 ]バイナリサーチは、ターゲット値を配列の中央の要素と比較します。等しくない場合、ターゲットが存在しない半分が除外され、残りの半分で検索が続行され、再び中央の要素をターゲット値と比較し、ターゲット値が見つかるまでこれを繰り返します。検索が終了して残りの半分が空になった場合、ターゲットは配列内にありません。
二分探索は最悪の場合対数時間で実行されるため、比較では、は配列内の要素の数です。[ a ] [ 6 ]バイナリサーチは、小さな配列を除いて、線形サーチよりも高速です。ただし、バイナリサーチを適用するには、まず配列をソートする必要があります。ハッシュテーブルなど、高速検索用に設計された特殊なデータ構造があり、バイナリサーチよりも効率的に検索できます。ただし、バイナリサーチは、配列に存在しない場合でも、ターゲットに対して配列内で次に小さい要素または次に大きい要素を見つけるなど、より広範囲の問題を解決するために使用できます。
二分探索には数多くのバリエーションが存在する。特に、分数カスケード法は、複数の配列から同じ値を検索する際の二分探索を高速化する。分数カスケード法は、計算幾何学をはじめとする多くの分野における様々な探索問題を効率的に解決する。指数探索は、二分探索を無制限のリストに拡張する。二分探索木やB木といったデータ構造は、二分探索に基づいている。
二分探索は、ソートされた配列に対して機能します。二分探索は、配列の中央にある要素と目標値を比較することから始まります。目標値が要素と一致する場合、配列内のその位置が返されます。目標値が要素より小さい場合、探索は配列の下半分で続行されます。目標値が要素より大きい場合、探索は配列の上半分で続行されます。このようにして、アルゴリズムは各反復で目標値が存在しない半分を排除します。[ 7 ]
配列が与えられた場合の値またはレコードを持つ要素ソートされた、および目標値次のサブルーチンは、バイナリサーチを使用してインデックスを見つけます。で[ 7 ]
この反復手順では、2 つの変数を使用して検索範囲を追跡します。そして手順は擬似コードで次のように表すことができ、変数名と型は上記と同じで、floorは床関数、はunsuccessful検索の失敗を示す特定の値を参照します。[ 7 ]

関数binary_search(A, n, T)は L := 0 R := n − 1 while L ≤ R do m := L + floor((R - L) / 2) A[m] < Tの場合 L := m + 1 else if A[m] > T then R := m − 1 else : return m return unuccessful
あるいは、アルゴリズムは天井を取るかもしれない配列内に目的の値が複数回出現する場合、結果が変わる可能性があります。
上記の手順では、アルゴリズムは中間要素()は目標値と等しい()各イテレーションで。一部の実装では、各イテレーション中にこのチェックを省略します。アルゴリズムは、要素が 1 つだけ残っているとき(これにより、1回の反復で1回の比較が省略され、平均して1回多く反復するだけで済むため、比較ループが高速化されます。[ 8 ]
ヘルマン・ボッテンブルッフは、このチェックを省略した最初の実装を1962年に発表した。[ 8 ] [ 9 ]
ceil天井関数はどこにあるのか、このバージョンの擬似コードは次のとおりです。
関数binary_search_alternative(A, n, T)は L := 0 R := n − 1 while L != R do m := L + ceil((R - L) / 2) A[m] > Tの場合、 R := m − 1 それ以外の場合: L := m A[L] = Tの場合、 L を返す。失敗を返す。
この手順では、配列に重複する要素があっても、要素がターゲット値と等しいインデックスを返すことがあります。たとえば、検索対象の配列が次のようになっている場合です。そしてターゲットはその場合、アルゴリズムが4番目(インデックス3)または5番目(インデックス4)の要素を返すのが正しいでしょう。通常の手順では、この場合4番目の要素(インデックス3)が返されます。常に最初の重複要素を返すとは限りません(これは依然として4番目の要素を返します)。ただし、配列内で重複している対象値の最左端の要素または最右端の要素を見つける必要がある場合もあります。上記の例では、4番目の要素は値4の最左端の要素であり、5番目の要素は値4の最右端の要素です。上記の代替手順では、そのような要素が存在する場合、常に最右端の要素のインデックスが返されます。[ 9 ]
最も左の要素を見つけるには、次の手順を使用できます。[ 10 ]
もしそして、 それからは、。 たとえ配列に含まれていません。ランクは配列内、または配列内の以下の値より小さい要素の数。
floor床関数はどこにありますか?このバージョンの擬似コードは次のとおりです。
関数binary_search_leftmost(A, n, T): L := 0 R := n L < R の間: m := L + floor((R - L) / 2) A[m] < Tの場合: L := m + 1 それ以外: R := m Lを返す
最も右の要素を見つけるには、次の手順を使用できます。[ 10 ]
もしそして、 それからは、。 たとえ配列に含まれていません。は、配列内の要素のうち、より大きい数です。。
floor床関数はどこにありますか?このバージョンの擬似コードは次のとおりです。
関数binary_search_rightmost(A, n, T): L := 0 R := n L < R の間: m := L + floor((R - L) / 2) A[m] > T の場合: R := m それ以外: L := m + 1 R - 1を返す

上記の手順では、対象値の位置を見つける完全一致のみを実行します。しかし、バイナリサーチはソートされた配列に対して動作するため、バイナリサーチを拡張して近似一致を実行することは容易です。たとえば、バイナリサーチを使用して、特定の値に対して、そのランク(より小さい要素の数)、先行要素(次に小さい要素)、後続要素(次に大きい要素)、および最近傍を計算できます。2つの値の間の要素の数を求める範囲クエリは、2 つのランククエリで実行できます。 [ 11 ]


比較回数の観点から、二分探索のパフォーマンスは、二分木上での手順の実行を見ることで分析できます。木のルートノードは配列の中央の要素です。下半分の中央の要素はルートの左の子ノードであり、上半分の中央の要素はルートの右の子ノードです。木の残りの部分は同様の方法で構築されます。ルートノードから始めて、対象値が検討中のノードより小さいか大きいかに応じて、左または右のサブツリーをたどります。[ 6 ] [ 14 ]
最悪の場合、バイナリーサーチは比較ループの反復では、表記法は、引数以下の最大の整数を返す床関数を表し、は二進対数です。これは、探索がツリーの最深レベルに達したときに最悪のケースが発生し、常に任意の二分探索におけるツリーのレベル。
最悪のケースは、ターゲット要素が配列に含まれていない場合にも発生する可能性があります。が2 のべき乗より 1 小さい場合、これは常に当てはまります。そうでない場合は、検索が実行されることがあります。検索がツリーの最深部に達した場合、反復回数は増えません。ただし、反復回数は、ツリーの2番目に深いレベルで探索が終了する場合、最悪の場合より1回少ない。[ 15 ]
平均的に、各要素が検索される確率が等しいと仮定すると、二分探索では対象要素が配列内にある場合の反復回数。これはほぼ等しい。反復。ターゲット要素が配列にない場合、バイナリサーチは平均反復回数は、要素間と要素外の範囲が検索される可能性が等しいと仮定した場合である。[ 14 ]
最良の場合、ターゲット値が配列の中央の要素である場合、1回の反復でその位置が返されます。[ 16 ]
反復回数に関して言えば、要素の比較のみで動作する検索アルゴリズムは、平均および最悪の場合のパフォーマンスにおいて、二分探索よりも優れているものはありません。二分探索を表す比較木は、木の最下位レベルより上のすべてのレベルが完全に満たされているため、可能な限り少ないレベルを持ちます。[ b ]そうでなければ、検索アルゴリズムは反復で少数の要素しか削除できず、平均および最悪の場合に必要な反復回数が増加します。これは、比較に基づく他の検索アルゴリズムの場合にも当てはまります。これらのアルゴリズムは、一部のターゲット値ではより高速に動作するかもしれませんが、すべての要素の平均パフォーマンスは二分探索よりも劣ります。配列を半分に分割することで、二分探索は両方の部分配列のサイズが可能な限り類似するようにします。[ 14 ]
バイナリサーチでは、配列のサイズに関係なく、配列インデックスまたはメモリ位置へのポインタである要素へのポインタが 3 つ必要になります。したがって、バイナリサーチの空間計算量は次のようになります。RAMモデルの計算という言葉で。
二分探索で実行される平均反復回数は、各要素が検索される確率に依存します。平均的なケースは、検索が成功した場合と失敗した場合で異なります。検索が成功した場合は、各要素が検索される確率は等しいと仮定します。検索が失敗した場合は、要素間および要素外の区間が検索される確率は等しいと仮定します。検索が成功した場合の平均ケースは、すべての要素をちょうど1回検索するために必要な反復回数を、で割った値です。、要素の数。検索が失敗する平均的なケースは、各区間内で要素をちょうど 1 回検索するために必要な反復回数を で割ったものです。間隔。[ 14 ]
二分木表現では、検索の成功は、ルートからターゲットノードまでのパス(内部パスと呼ばれる)で表すことができます。パスの長さは、パスが通過するエッジ(ノード間の接続)の数です。対応するパスの長さがlである場合、検索によって実行される反復回数は次のようになります。最初の反復を数えます。内部パスの長さは、すべての固有の内部パスの長さの合計です。ルートから任意の単一ノードへのパスは 1 つしかないため、各内部パスは特定の要素の検索を表します。要素がn個あり、n 個が正の整数である場合、内部パスの長さはすると、検索が成功するまでの平均反復回数は1回の反復を追加して最初の反復をカウントします。[ 14 ]
二分探索は比較による探索に最適なアルゴリズムであるため、この問題は、 n個のノードを持つすべての二分木の最小内部パス長を計算することに帰着し、それは次のようになります。[ 17 ]
例えば、7要素の配列では、ルートは1回の反復処理を必要とし、ルートの下にある2つの要素は2回の反復処理を必要とし、その下の4つの要素は3回の反復処理を必要とします。この場合、内部パスの長さは次のようになります。[ 17 ]
平均反復回数は平均ケースの式に基づく。合計は以下のように簡略化できます。[ 14 ]
式を代入すると方程式に: [ 14 ]
整数nの場合、これは上記で指定した、検索が成功した場合の平均的なケースの式と同等です。
検索が失敗した場合は、ツリーに外部ノードを追加することで表現できます。これにより、拡張二分木が形成されます。内部ノード、つまりツリー内に存在するノードの子ノードが2つ未満の場合、各内部ノードが2つの子を持つように、外部ノードと呼ばれる追加の子ノードが追加されます。このようにすることで、検索が失敗した場合は、最後の反復処理で残った単一の要素を親とする外部ノードへのパスとして表現できます。外部パスとは、ルートから外部ノードへのパスです。外部パスの長さは、すべての固有の外部パスの長さの合計です。要素は正の整数であり、外部パスの長さはすると、検索が失敗した場合の平均反復回数は、最初の反復をカウントするために1回の反復が追加されます。外部パスの長さは、の代わりになぜなら外部パスは、配列の要素間および要素の外側の間隔を表します。[ 14 ]
この問題は同様に、すべての二分木の最小外部パス長を決定することに帰着します。ノード。すべての二分木において、外部パスの長さは内部パスの長さに等しい。[ 17 ]式を代入すると: [ 14 ]
式を代入すると方程式に検索が失敗した場合の平均ケースは次のように決定できます。[ 14 ]
上記で定義した二分探索手順の各反復では、1回または2回の比較が行われ、各反復で中央の要素がターゲットと等しいかどうかがチェックされます。各要素が等しい確率で探索されると仮定すると、各反復では平均して1.5回の比較が行われます。アルゴリズムのバリエーションでは、探索の最後に中央の要素がターゲットと等しいかどうかがチェックされます。これにより、平均して各反復から半分の比較が削減されます。これは、ほとんどのコンピュータで反復あたりの時間をわずかに短縮します。ただし、探索が最大反復回数を要することが保証され、平均して1回の反復が追加されます。比較ループは1回しか実行されないため、最悪の場合、反復ごとの効率のわずかな向上は、非常に大きな場合を除いて、追加の反復を補うことはできません。[ c ] [ 18 ] [ 19 ]
二分探索のパフォーマンスを分析する際には、2つの要素を比較するのに必要な時間も考慮する必要があります。整数と文字列の場合、必要な時間は要素のエンコード長(通常はビット数)が増加するにつれて直線的に増加します。たとえば、64ビットの符号なし整数のペアを比較する場合、32ビットの符号なし整数のペアを比較する場合の最大2倍のビット数を比較する必要があります。最悪のケースは、整数が等しい場合に発生します。これは、要素のエンコード長が大きい場合(大きな整数型や長い文字列など)に顕著になり、要素の比較コストが高くなります。さらに、浮動小数点値(実数の最も一般的なデジタル表現)の比較は、整数や短い文字列の比較よりもコストが高くなることがよくあります。
浮動小数点の高速比較は、整数として比較することで可能です。ただし、この種の比較は全順序を形成するため、すべての浮動小数点値は互いに異なり、自身と同じになります。これは、-0.0 が 0.0 と同じであるべきで、NaN が自身を含む他のどの値とも同じように比較されないべきである典型的な比較とは異なります。[ 20 ] [ 21 ]
Steel Bank Common Lisp の貢献者である Paul Khuong 氏によると、バイナリサーチはデータ依存の性質を持つにもかかわらず、分岐の予測ミスが非常に少ないとのことです。これは、バイナリサーチの大部分が分岐ではなく条件付き移動として表現できるためです。これは、ほとんどの対数分割統治探索アルゴリズムにも当てはまります。[ 22 ]
ほとんどのコンピュータアーキテクチャでは、プロセッサはRAMとは別のハードウェアキャッシュを備えています。キャッシュはプロセッサ自体の中に配置されているため、アクセスははるかに高速ですが、通常はRAMよりもはるかに少ないデータしか格納できません。そのため、ほとんどのプロセッサは、最近アクセスされたメモリ位置と、その近くのメモリ位置を格納します。たとえば、配列要素にアクセスすると、その要素自体がRAM内のその近くに格納されている要素とともに格納される可能性があり、インデックスが近い配列要素に順次アクセスする速度が向上します(参照の局所性)。ソートされた配列では、バイナリサーチは配列が大きい場合、遠く離れたメモリ位置にジャンプできます。これは、要素に順次アクセスするアルゴリズム(ハッシュテーブルの線形サーチや線形プロービングなど)とは異なります。これにより、ほとんどのシステムで大きな配列のバイナリサーチの実行時間がわずかに増加します。[ 23 ]
Paul Khuong氏は、 512 KiB以上の2のべき乗サイズの大きな配列に対する二分探索は、CPUキャッシュの実装方法に別の問題を引き起こす傾向があると指摘している。具体的には、変換ルックアサイドバッファ(TLB)は、通常、要求されたアドレスの下位ビットを「キー」とするコンテンツアドレス指定可能メモリ(CAM)として実装されることが多い。2のべき乗サイズの配列を検索する場合、下位ビットが同じメモリ アドレスにアクセスされる傾向があり、CAMの取得に使用される「キー」との衝突(「エイリアシング」)が発生する。一般的なTLBは4ウェイアソシアティブであり、同じ「キー」にヒットするアドレスは最大4つまでしか処理できず、それ以上になるとTLBスラッシングが発生する。 (CPUキャッシュの他のレベルも同様の構成を使用していますが、ウェイ数が多く(通常8または16)、より小さな領域を管理しているため、影響は少なくなります。)これは、バイナリサーチの分割点をオフセットして、ちょうど真ん中ではなく31/64で分割するようにすることで防止できます。 [ 24 ]
挿入と削除操作が検索と交互に行われる場合、バイナリサーチを使用したソート済み配列は非常に非効率的なソリューションであり、このような操作ごとに時間がかかります。さらに、ソートされた配列は、特に要素が頻繁に配列に挿入される場合、メモリの使用を複雑にする可能性があります。[ 25 ]挿入と削除をはるかに効率的にサポートする他のデータ構造があります。バイナリサーチは、完全一致とセットメンバーシップ(ターゲット値が値のコレクションに含まれているかどうかの判定)を実行するために使用できます。より高速な完全一致とセットメンバーシップをサポートするデータ構造があります。ただし、他の多くの検索方式とは異なり、バイナリサーチは効率的な近似一致に使用でき、通常はこのような一致を値自体の型や構造に関係なく、時間もかかりません。[ 26 ]さらに、最小要素や最大要素を見つけるなど、ソートされた配列に対して効率的に実行できる操作もあります。[ 11 ]
線形探索は、目的の値が見つかるまですべてのレコードをチェックする単純な探索アルゴリズムです。線形探索はリンクリスト上で実行でき、配列よりも高速な挿入と削除が可能です。二分探索は、配列が短い場合を除き、ソート済みの配列に対しては線形探索よりも高速ですが、配列は事前にソートしておく必要があります。[ d ] [ 28 ]クイックソートやマージソートなど、要素の比較に基づくすべてのソートアルゴリズムは、少なくとも最悪の場合の比較。[ 29 ]線形探索とは異なり、二分探索は効率的な近似マッチングに使用できます。最小要素や最大要素を見つけるなどの操作は、ソートされた配列では効率的に実行できますが、ソートされていない配列では効率的に実行できません。[ 30 ]

二分探索木は、二分探索の原理に基づいて動作する二分木データ構造です。木のレコードはソートされた順序で配置され、木内の各レコードは二分探索に似たアルゴリズムを使用して検索でき、平均対数時間かかります。挿入と削除も、二分探索木では平均対数時間かかります。これは、ソートされた配列の線形時間の挿入と削除よりも高速になる可能性があり、二分木は、範囲クエリや近似クエリなど、ソートされた配列で可能なすべての操作を実行する能力を保持しています。[ 26 ] [ 31 ]
しかし、バイナリサーチは通常、検索においてより効率的です。バイナリサーチツリーは不完全にバランスが取れていない可能性が高く、バイナリサーチよりもわずかにパフォーマンスが劣ります。これは、自身のノードのバランスが取れているバランスの取れたバイナリサーチツリーにも当てはまります。なぜなら、バランスの取れたバイナリサーチツリーは、可能な限り少ないレベルのツリーを生成することはほとんどないからです。バランスの取れたバイナリサーチツリーを除いて、ツリーは、2 つの子を持つ内部ノードが少ないほど著しく不均衡になる可能性があり、その結果、平均および最悪の場合の検索時間が近づきます。比較。[ e ]二分探索木はソート済み配列よりも多くのスペースを必要とします。[ 33 ]
バイナリサーチツリーは、ハードディスクに格納された外部メモリでの高速検索に適しています。バイナリサーチツリーはファイルシステム内で効率的に構造化できるためです。Bツリーはこのツリー構成方法を一般化したものです。Bツリーは、データベースやファイルシステムなどの長期ストレージを整理するためによく使用されます。[ 34 ] [ 35 ]
連想配列を実装する場合、ハッシュ関数を使用してキーをレコードにマッピングするデータ構造であるハッシュテーブルは、一般的に、レコードのソート済み配列に対するバイナリサーチよりも高速です。[ 36 ]ほとんどのハッシュテーブルの実装では、平均して償却定数時間しか必要ありません。 [ f ] [ 38 ]ただし、ハッシュは、次小数点、次大数、最も近いキーを計算するなどの近似一致には役立ちません。検索が失敗したときに得られる情報は、ターゲットがどのレコードにも存在しないことだけです。[ 39 ]バイナリサーチは、このような一致に最適で、対数時間で実行できます。バイナリサーチは近似一致もサポートします。最小要素と最大要素を見つけるなどの一部の操作は、ソート済み配列では効率的に実行できますが、ハッシュテーブルでは効率的に実行できません。[ 26 ]
検索に関連する問題として、集合メンバーシップがあります。二分探索のようなルックアップを行うアルゴリズムは、集合メンバーシップにも使用できます。集合メンバーシップに特化したアルゴリズムも存在します。ビット配列は最も単純なもので、キーの範囲が限られている場合に便利です。ビット配列は、各ビットがキーの範囲内の単一のキーを表すビットの集合をコンパクトに格納します。ビット配列は非常に高速で、必要なのはわずかです。時間。[ 40 ] Judy1 型のJudy 配列は64 ビットキーを効率的に処理します。[ 41 ]
近似的な結果を得るには、ハッシュに基づく別の確率的データ構造であるブルームフィルタを使用します。ブルームフィルタは、ビット配列と複数のハッシュ関数を使用してキーをエンコードすることで、キーのセットを格納します。ブルームフィルタは、ほとんどの場合、ビット配列よりもはるかにスペース効率が良く、速度もそれほど遅くありません。ハッシュ関数、メンバーシップクエリには時間。しかし、ブルームフィルタは誤検出の問題を抱えています。[ g ] [ h ] [ 43 ]
ソート済み配列の検索やその他の操作において、場合によってはバイナリサーチよりも優れたデータ構造が存在する。たとえば、検索、近似一致、およびソート済み配列で利用可能な操作は、van Emde Boas ツリー、融合ツリー、トライ、ビット配列などの特殊なデータ構造上でバイナリサーチよりも効率的に実行できる。これらの特殊なデータ構造が高速なのは、特定の属性を持つキー (通常は小さな整数であるキー) の特性を利用するためであり、その属性を持たないキーの場合は時間またはスペースを消費する。[ 26 ]キーが順序付け可能であれば、これらの操作はキーの種類に関係なく、ソート済み配列上で常に少なくとも効率的に実行できる。Judy 配列などの一部の構造は、効率性と近似一致を実行する能力を維持しながら、この問題を軽減するために複数のアプローチを組み合わせて使用する。[ 41 ]

一様二分探索では、下限と上限の代わりに、現在の反復から次の反復までの中間要素のインデックスの差を格納します。差分を含むルックアップテーブルは事前に計算されます。たとえば、検索対象の配列が[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]の場合、中間要素 () は6になります。この場合、左部分配列 ( [1, 2, 3, 4, 5] )の中央の要素は3 であり、右部分配列 ( [7, 8, 9, 10, 11] )の中央の要素は9です。一様二分探索では、両方のインデックスが6から同じ量だけ異なるため、値3が格納されます。 [ 44 ]探索空間を縮小するために、アルゴリズムはこの変更を中央の要素のインデックスに加算または減算します。一様二分探索は、10 進数コンピュータなど、中間点を計算するのが非効率的なシステムでは高速になる可能性があります。[ 45 ]

指数探索は、二分探索を無制限リストに拡張したものです。まず、インデックスが2のべき乗であり、かつ目標値より大きい最初の要素を見つけます。その後、そのインデックスを上限として設定し、二分探索に切り替えます。二分探索を開始する前の反復回数、最大バイナリサーチの反復、ここではターゲット値の位置です。指数探索は境界付きリストで機能しますが、ターゲット値が配列の先頭付近にある場合にのみ二分探索よりも改善されます。[ 46 ]

補間探索では、中間点を計算する代わりに、配列内の最小値と最大値、および配列の長さを考慮して、目標値の位置を推定します。これは、多くの場合、中間点が最良の推測ではないという前提に基づいています。たとえば、目標値が配列内の最大値に近い場合、配列の末尾付近に位置する可能性が高いです。[ 47 ]
一般的な補間関数は線形補間です。配列です。はそれぞれ下限値と上限値であり、がターゲットである場合、ターゲットは約その間のそして線形補間を使用し、配列要素の分布が均一またはほぼ均一である場合、補間探索は比較。[ 47 ] [ 48 ] [ 49 ]
実際には、補間探索は小さな配列では二分探索よりも遅くなります。これは、補間探索には追加の計算が必要だからです。その時間計算量は二分探索よりも緩やかに増加しますが、これは大きな配列の場合にのみ追加の計算を補います。[ 47 ]

分数カスケードは、複数のソート済み配列で同じ要素をバイナリ検索する速度を向上させる手法です。各配列を個別に検索するには、時間、は配列の数です。分数カスケードにより、これは に減少します。各配列に各要素と他の配列内での位置に関する特定の情報を格納することによって。[ 50 ] [ 51 ]
分数カスケードは、もともとさまざまな計算幾何学の問題を効率的に解決するために開発されました。分数カスケードは、データマイニングやインターネットプロトコルルーティングなど、他の分野にも応用されています。 [ 50 ]
二分探索は、ターゲット値が配列要素ではなく頂点に格納される特定の種類のグラフで動作するように一般化されています。二分探索木はそのような一般化の1つです。木内の頂点(ノード)がクエリされると、アルゴリズムは、その頂点がターゲットであることを学習するか、そうでなければターゲットがどのサブツリーにあるかを学習します。ただし、これは次のようにさらに一般化できます。無向で正の重みを持つグラフとターゲット頂点が与えられた場合、アルゴリズムは、頂点をクエリしたときに、それがターゲットと等しいことを学習するか、クエリされた頂点からターゲットへの最短パス上の接続エッジが与えられます。標準の二分探索アルゴリズムは、グラフがパスである場合に相当します。同様に、二分探索木は、クエリされた頂点がターゲットと等しくない場合に、左または右のサブツリーへのエッジが与えられる場合です。すべての無向で正の重みを持つグラフに対して、ターゲット頂点を見つけるアルゴリズムが存在します。最悪の場合のクエリ数。[ 52 ]

ノイズバイナリサーチアルゴリズムは、アルゴリズムが配列の要素を確実に比較できない場合に対処します。各要素ペアについて、アルゴリズムが誤った比較を行う一定の確率が存在します。ノイズバイナリサーチは、得られた位置の信頼性を制御する特定の確率で、ターゲットの正しい位置を見つけることができます。すべてのノイズバイナリサーチ手順は、少なくとも平均的に比較すると、はバイナリエントロピー関数であり、は、手順が間違った位置をもたらす確率です。[ 53 ] [ 54 ] [ 55 ]ノイズのある二分探索問題は、回答が間違っている可能性がある20の質問の変種であるレニー・ウラムゲーム[56]のケースとして考えることができます。[ 57 ]
古典的なコンピュータは、最悪の場合、正確にバイナリサーチを実行する際の反復回数。バイナリサーチの量子アルゴリズムは依然として一定の割合に制限されている。クエリ(古典的な手順の反復を表す)は、定数係数が 1 未満であるため、量子コンピュータではより低い時間計算量を実現します。正確な量子バイナリサーチ手順、つまり常に正しい結果を返す手順は、少なくとも最悪の場合のクエリでは、は自然対数です。[ 58 ]正確な量子バイナリーサーチ手順があり、最悪の場合のクエリ数。[ 59 ]それに対し、グローバーのアルゴリズムは、要素の順序付けされていないリストを検索するための最適な量子アルゴリズムであり、クエリ。[ 60 ]
項目のリストを並べ替えて検索を速くするというアイデアは古代に遡ります。最も古い既知の例は、紀元前200年頃にバビロンで発見されたイナキビト・アヌの粘土板です。この粘土板には、約500の六十進数とその逆数が辞書順に並べられており、特定の項目の検索が容易になっています。さらに、エーゲ海の島々では、最初の文字で並べられた名前のリストがいくつか発見されています。 1286年に完成したラテン語辞書であるカトリコンは、単語を最初の数文字ではなくアルファベット順に並べる規則を記述した最初の作品です。[ 9 ]
1946 年、ジョン・モークリーは、コンピューティングの基礎となる大学の講義であるムーア・スクール講義の一部として、バイナリサーチについて初めて言及しました。 [ 9 ] 1957 年、ウィリアム・ウェズリー・ピーターソンは、補間サーチの最初の方法を発表しました。[ 9 ] [ 61 ] 1960 年、デリック・ヘンリー・レーマーがすべての配列で動作するバイナリサーチアルゴリズムを発表するまで、発表されたバイナリサーチアルゴリズムはすべて、長さが 2 のべき乗より 1 少ない配列に対してのみ機能しました。[ i ] [ 63 ] 1962 年、ヘルマン・ボッテンブルッフは、等価性の比較を最後に配置したバイナリサーチのALGOL 60実装を発表しました。これにより、平均反復回数は 1 回増加しましたが、反復ごとの比較回数は 1 回に減少しました。[ 8 ]一様二分探索は、 1971 年にスタンフォード大学の AK Chandra によって開発されました。[ 9 ] 1986 年に、Bernard ChazelleとLeonidas J. Guibas は、計算幾何学における多数の探索問題を解決する方法として分数カスケードを導入しました。[ 50 ] [ 64 ] [ 65 ]
二分探索の基本的な考え方は比較的単純だが、その詳細は驚くほど複雑になることがある。
ジョン・ベントレーがプログラマー向けのコースで二分探索を問題として出題したところ、数時間かけても90%の学生が正しい解答を提供できなかったことがわかった。主な理由は、誤った実装では実行できなかったり、まれなエッジケースで間違った答えを返したりしたためである。[ 66 ] 1988年に発表された調査によると、正確なコードは20冊の教科書のうち5冊にしか見当たらない。[ 67 ]さらに、ベントレー自身が1986年に出版した著書『Programming Pearls』に掲載された二分探索の実装には、20年以上も検出されなかったオーバーフローエラーが含まれていた。Javaプログラミング言語ライブラリの二分探索の実装にも、9年以上同じオーバーフローバグが存在していた。[ 68 ]
実際の実装では、インデックスを表すために使用される変数は固定サイズ(整数)であることが多く、非常に大きな配列では算術オーバーフローが発生する可能性があります。スパンの中間点が次のように計算される場合すると、中間点を格納するために使用されるデータ型の整数範囲を超える可能性があります。そして範囲内です。そしては非負であり、中点を計算することでこれを回避できます。[ 69 ]
ループの終了条件が正しく定義されていない場合、無限ループが発生する可能性があります。を超える検索が失敗した場合は、その失敗を通知する必要があります。さらに、目的の要素が見つかったらループを終了するか、このチェックを最後に移動した実装の場合は、最後に検索が成功したか失敗したかのチェックを行う必要があります。ベントレーは、バイナリサーチを誤って実装したプログラマーのほとんどが、終了条件の定義に誤りがあったことを発見しました。[ 8 ] [ 70 ]
多くのプログラミング言語の標準ライブラリには、二分探索ルーチンが含まれています。
bsearch()binary_search()、関数lower_bound()、、upper_bound()およびを提供しますequal_range()。[ 72 ] C++20 ライブラリを使用すると、として範囲std::rangesに適用できます。std::ranges::binary_search()std.range型SortedRange(sort()およびassumeSorted()関数によって返される)が用意されています。 [ 73 ]contains()equalRange()lowerBound()trisect()SEARCH ALL。[ 74 ]sort標準ライブラリ パッケージには、一般的な二分探索を実装する関数Search、、、および、SearchIntsに加え、それぞれ整数、浮動小数点数、および文字列のスライスを検索するための特定の実装が含まれています。[ 75 ]SearchFloat64sSearchStringsbinarySearch()静的メソッドのセットを提供します。[ 76 ] [ 77 ]ArraysCollectionsjava.utilListSystem.Arrayのメソッドが挙げられますBinarySearch<T>(T[] array, T value)。[ 78 ]NSArray-indexOfObject:inSortedRange:options:usingComparator:CFArrayBSearchValues()bisectは、挿入のたびにリストをソートする必要なく、リストをソートされた順序で保持するモジュールが用意されています。 [ 81 ]bsearch近似マッチングを組み込んだメソッドが含まれています。[ 82 ]binary_search()およびを提供します。[ 83 ]binary_search_by()binary_search_by_key()partition_point()
この記事は、2018 年に外部の学術査読のためにWikiJournal of Scienceに投稿されました(査読者レポート)。更新されたコンテンツは、CC-BY-SA-3.0ライセンス ( 2019 年) の下で Wikipedia ページに再統合されました。査読された記録バージョンは次のとおりです。Anthony Lin; et al. (2019 年 7 月 2 日). "Binary search algorithm" (PDF) . WikiJournal of Science . 2 (1): 5. doi : 10.15347/WJS/2019.005 . ISSN 2470-6345 . Wikidata Q81434400 .
{{cite book}}ISBN /日付の不一致(ヘルプ)slice