JavaScriptのバイナリ検索。 実用例

画像

バイナリ検索とは何ですか?


配列を検索する必要がある場合、最も簡単な方法はindexOf()ループ、または場合によってはfor()ループを使用することです。 これらのメソッドはいずれも、配列の先頭から繰り返し処理を開始し、目的の値が見つかるまで配列の各要素を調べます。

次に、これをバイナリ検索と比較します

バイナリ検索では、配列を半分に繰り返し分割することにより、ソートされた配列を検索できます。

バイナリ検索は、検索値が配列の平均値よりも大きいか、 小さいか、 等しいかどうかをチェックすることで実行されます。


しかし、一番下の行は、配列を並べ替える必要があるということです。つまり、バイナリ検索が正しく機能するためには、値が正しい順序でなければなりません。

大量のデータを処理する場合、反復ごとに不適切な値が1つだけでなく、不要な配列値の半分が削除されるため、バイナリ検索を使用する方がはるかに効率的です。

プロジェクト


私は最近、React APIから30日間のインタラクティブなビットコイン価格チャートを作成した方法に関する記事を投稿しました。

チャートの最も重要な側面の1つは、チャートにカーソルを合わせると関連データを表示するツールチップです。 ツールチップ上でマウスを動かすと、チャートの最も近いポイントからのデータが更新されます。 例:

画像

どのように機能しますか?

すべての座標と対応するデータの配列があります。 座標はオブジェクト内にあり、各オブジェクトにはsvgXキーがあります。 次のようになります。

svgData = [ {svgX: 1367.844, data...}, {svgX: 1684.478, data...}, {svgX: 1168.474, data...}, {svgX: 1344.854, data...}, // etc. ] 


最初に、単純なforループを使用して、マウスカーソルの現在のX座標をデータ配列内のすべてのsvgX値と比較しました。

サイクル


forループコードは次のようになります。 すべてのsvgX値を反復処理し、マウスカーソルのX座標に近い値​​を持つオブジェクトは、データを表示するオブジェクトです。

 const {svgWidth} = this.props; let closestPoint = {}; for(let i = 0, c = svgWidth; i < svgData.length; i++){ if ( Math.abs(svgData[i].svgX — this.state.hoverLoc) <= c ){ c = Math.abs(svgData[i].svgX — this.state.hoverLoc); closestPoint = svgData[i]; } } 

まず、svg要素の幅を取得し、空のオブジェクトを作成して、最も近いポイントを保存します。

 const {svgWidth} = this.props; let closestPoint = {}; 

次に、データ配列を反復処理します。 配列内のオブジェクトの各svgX値は、マウスカーソルのX座標と比較されます。 ループの現在の反復が他の反復よりもカーソルに近い値を持つ場合、このポイントは最も近いポイントとして設定されます。

データ量が少ない場合、この方法は比較的高速です。 ただし、大量のデータがある場合、このオプションは効果的ではなくなります。

バイナリ検索


以下は、最も近い値を見つけるためのバイナリ検索コードの例です。

 const binarySearch = (data, target, start, end) => { if(end < 1) return data[0]; const middle = Math.floor((start + (end - start)/2); if (target == data[middle].svgX) return data[middle]; if (end — 1 === start) return Math.abs(data[start].svgX — target) > Math.abs(data[end].svgX — target) ? data[end] : data[start]; if (target > data[middle].svgX) return binarySearch(data,target,middle,end); if (target < data[middle].svgX) return binarySearch(data,target,start,middle); } let closestPoint = binarySearch(data,target, 0, data.length-1) 

バイナリ検索には5つの主要なコンポーネントが必要です。

  1. データ- データ。 オブジェクトの配列。
  2. ターゲットはターゲットです。 マウスカーソルの位置(x座標)。
  3. startは始まりです。 バイナリ検索の現在の反復の配列内の開始位置。
  4. end- 終わり。 バイナリ検索の現在の反復の配列内の終了位置。
  5. 真ん中です バイナリ検索の現在の反復の配列の中央。

これは少しわかりにくいので、上のコードを単純化する例を見てみましょう。 この例では、目標値は3.7です。 1から5までの5つの増加する数字の配列を検索します。

10行目の上記のコードからわかるように、バイナリ検索を開始するときに、データ配列全体、マウスオブジェクト、配列のフルサイズに等しい開始位置0および終了位置を入力に渡します。

データが得られたら、開始位置と終了位置の合計を2で割ることにより、配列の中央を見つけます。 分数を取得しないために、結果の数値は切り捨てられます。

 const middle = Math.floor((start + (end - start)/2); 

したがって、mid 0 +(4-0)/ 2は切り捨て= 2です。
= data[2]
この例では、目標値が3.7であることを覚えています。 中間の位置を見つけたら、目標値と等しいかどうかを確認します。 その場合、オブジェクトを返し、完了です!

 if (target == data[middle].svgX) return data[middle]; 

残念ながら、配列の平均値は3です。そして、目標は3.7です。

次に、最終位置-1が初期位置と一致するかどうかを確認します。

 if (end — 1 === start) { return Math.abs(data[start].svgX — target) > Math.abs(data[end].svgX — target) ? data[end] : data[start]; } 

この条件は、配列が2つの最終値に削減され、目標がそれらの間にある場合に機能します。 この場合、近い値を返します。

現在の反復では、条件はfalseを返します。

次に、目標値が平均よりも大きいかどうかを確認します。

 if (target > data[middle].svgX) return binarySearch(data,target,middle,end); 

これが私たちのケースです! 3.7の目標値は、平均3よりも大きくなっています。配列の最初の2つの値を取り除くことができます。

画像
再帰を使用して、binarySearch()関数の新しい呼び出しを返します。 配列を関数に渡します。ターゲット値は3.7で、 平均値を初期位置として、最終値を最終位置として使用します。

binarySearch()の新しい呼び出しは、位置2からdata.length-1まで検索します。

画像
関数は新しい中間位置データを見つけます[3]:
画像
3.7のターゲット値は4の平均値よりも小さいため、配列の右側を破棄し、binarySearch()関数の新しい呼び出しを返します。 今回は初期位置はデータのままであり[2]、最終位置は平均データに変わります[3]。
画像
条件が満たされる瞬間に到達しました:

 if (end — 1 === start) { return Math.abs(data[start].svgX — target) > Math.abs(data[end].svgX — target) ? data[end] : data[start]; } 

最終値から1を引いた値が初期値に等しいため、目標値が中間にあることがわかります。 値が3.7の値に近い配列の要素を返す必要があります。 4 (最終値)が近いため、それぞれ対応する要素を返します:data [3]。

速度試験


データの量が少ない場合、再帰はforループよりも遅くなる可能性があります。 10個の要素を持つjsperfでテストする場合、再帰関数はforループよりも平均で3%遅くなります。 ただし、10,000要素では、forループは再帰関数よりも72%遅くなります。 これは、再帰がforループのほぼ2倍の速度であり、時間を大幅に節約できることを意味します。

JavaScriptのバイナリ検索の基本を理解したことを願っています!

Source: https://habr.com/ru/post/J335750/


All Articles