二分探索のソースを表示
←
二分探索
ナビゲーションに移動
検索に移動
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
{{pathnav|探索|frame=1}} '''二分探索'''(にぶんたんさく、{{lang-en-short|binary search}}、{{lang|en|BS}})や'''バイナリサーチ'''とは、[[ソート]]済み[[配列]]に対する[[探索]][[アルゴリズム]]の一つ。 == 概要 == [[ソート]]済みの[[リスト (抽象データ型)|リスト]]や[[配列]]に入ったデータ(同一の値はないものとする)に対する検索を行うにあたって、 中央の値を見て、検索したい値との大小関係を用いて、検索したい値が中央の値の右にあるか、左にあるかを判断して、片側には存在しないことを確かめながら検索していく。 大小関係を用いるため、未ソートのリストや大小関係の定義されない要素を含むリストには二分探索を用いることはできない。 n個のデータがある場合、時間計算量は<math>O(\log_2 n)</math><ref>O記法では定数倍を無視できるので、単に<math>O(\log n)</math>とも書ける。</ref>である([[O記法]])。 n個のデータの中央の値を見ることで、1回の操作でn/2個程度(奇数の場合は(n-1)/2個、偶数の場合はn/2個または(n/2)-1個)の要素を無視することができる。 ==例== 具体例を示す。 ===データが見つかる例=== 下記のようなソート済み配列から'''''25'''''を探しだすことを考える。なお、配列内に値の重複はない(あるデータと同じ値のデータは他に存在しない)ものとする。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || '''''25''''' || 28 |} 結果欄を設け、目的のデータがあるか否か不明な部分を「?」、データを調べた上で目的のデータが無いとわかった部分を「N」、データを調べるまでもなく目的のデータが無い部分を「n」、目的のデータがあった部分を「○」にすることにする。検索前は、以下のようになる。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || '''''25''''' || 28 |- ! 結果 | ? || ? || ? || ? || ? || ? || ? || ? || ? || ? |} まず、配列の中央の位置を求めると、1 + (10 - 1) / 2 = 5 :<small>除算の端数は切捨、切上のどちらでもいいが、ここは切捨とする。以下同じ。</small> :<small>中央位置の計算は、手計算では (1 + 10) / 2 でもよいが、プログラム上では 1 + (10 - 1) / 2 すなわち 最小位置 + (最大位置 - 最小位置) / 2 とした方が安全である。[[#実装上の間違い]]を参照。</small> 位置5のデータは12なので「N」、位置1~4まではデータを調べなくても「n」とわかる。目的のデータは位置6~10にあるかもしれない。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || '''''25''''' || 28 |- ! 結果 | n || n || n || n || N || ? || ? || ? || ? || ? |} 位置6~10の中央の位置は、6 + (10 - 6) / 2 = 8 位置8のデータは22なので「N」、位置6~7までは「n」とわかる。目的のデータは位置9~10にあるかもしれない。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || '''''25''''' || 28 |- ! 結果 | n || n || n || n || N || n || n || N || ? || ? |} 位置9~10の中央の位置は、9 + (10 - 9) / 2 = 9 位置9のデータは25なので目的のデータが見つかったことになる。位置10は調べていないが、配列内に値の重複はないという想定なので「n」としてよい。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || '''''25''''' || 28 |- ! 結果 | n || n || n || n || N || n || n || N || '''○''' || n |} ===データが見つからない例(1)=== 下記のようなソート済み配列から'''''4'''''を探しだすことを考える。なお、配列内に値の重複はないものとする。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || 25 || 28 |} まず、配列の中央の位置を求めると、1 + (10 - 1) / 2 = 5 :(除算の端数は切捨、切上のどちらでもいいが、ここは切捨とする。以下同じ) 位置5のデータは12なので「N」、位置6~10まではデータを調べなくても「n」とわかる。目的のデータは位置1~4にあるかも知れない。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || 25 || 28 |- ! 結果 | ? || ? || ? || ? || N || n || n || n || n || n |} 位置1~4の中央の位置は、1 + (4 - 1) / 2 = 2 位置2のデータは3なので「N」、位置1も「n」とわかる。目的のデータは位置3~4にあるかも知れない。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || 25 || 28 |- ! 結果 | n || N || ? || ? || N || n || n || n || n || n |} 位置3~4の中央の位置は、3 + (4 - 3) / 2 = 3 位置3のデータは5なので「N」。もし、データ4が存在するならば、位置3のデータ5より小さいので左になるはずである。しかし、すでにそこには存在しないことがわかっている。また、位置3より右である位置4は、データを調べていないが、位置3より大きなデータのはずなので「n」。以上でデータが見つからないという結果になる。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || 25 || 28 |- ! 結果 | n || N || N || n || N || n || n || n || n || n |} ===データが見つからない例(2)=== 下記のようなソート済み配列から'''''29'''''を探しだすことを考える。なお、配列内に値の重複はないものとする。 {| class="wikitable" ! 位置 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 |- ! データ | 1 || 3 || 5 || 11 || 12 || 13 || 17 || 22 || 25 || 28 |} データの全体の一番右側が29より小さいので、データが見つからないという結果になる。 == コード例 == ===[[C言語]]=== <syntaxhighlight lang="c"> int binary_search(int ary[], int key, int imin, int imax) { if (imax < imin) { return KEY_NOT_FOUND; } else { int imid = imin + (imax - imin) / 2; if (ary[imid] > key) { return binary_search(ary, key, imin, imid - 1); } else if (ary[imid] < key) { return binary_search(ary, key, imid + 1, imax); } else { return imid; } } } </syntaxhighlight> ===[[F Sharp]]=== <syntaxhighlight lang="fsharp"> let find value (xa: 'T[]) = let rec ifind min max = if max < min then None else let c = min + (max - min) / 2 if xa.[c] > value then ifind min (c - 1) else if xa.[c] < value then ifind (c + 1) max else Some c ifind 0 (xa.Length - 1) find 8 [|1; 2; 4; 5; 6; 8; 11; 13|] </syntaxhighlight> ===[[Scheme]]=== <syntaxhighlight lang="scheme"> (define (find val xa) (letrec ((ifind (lambda (min max) (if (< max min) #f (let ((c (+ min (quotient (- max min) 2)))) (cond ((> (list-ref xa c) val) (ifind min (- c 1))) ((< (list-ref xa c) val) (ifind (+ c 1) max)) (else c))))))) (ifind 0 (- (length xa) 1)))) </syntaxhighlight> == 実装上の間違い == [[ドナルド・クヌース]]は "Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky ..."(二分探索の基本的なアイディアは比較的平易だが、その詳細は驚くほど扱いが難しいものになりうる)と述べており<ref> {{cite book | last = Knuth | first = Donald | authorlink = Donald Knuth | series = [[The Art of Computer Programming]] | volume = 3 | title = Sorting and Searching | chapter = Section 6.2.1: Searching an Ordered Table | edition = 3rd | pages = 409–426 | publisher = Addison-Wesley | year = 1997 | isbn = 0-201-89685-0}} </ref>、二分探索が正確に実装されていないことは多い。Richard E. Pattis の1988年の調査では、書籍20冊のうち15冊が誤っていた<ref>{{cite journal | first = Richard E. | last = Pattis | doi = 10.1145/52965.53012 | title = Textbook errors in binary searching | journal = SIGCSE Bulletin | volume = 20 | year = 1988 | pages = 190–194 }} cited at {{cite book | last = Kruse | first = Robert | title = Data Structures and Program Design in C++ | page = 280 | publisher = Prentice Hall | year = 1998 | isbn = 0-13-768995-0}}</ref>。 よくある間違いの一つは、上記のC言語のコードで imin + (imax - imin) / 2 を (imax + imin) / 2 としてしまう事である。(imax + imin) / 2 では、imax + imin が int の値の上限 (INT_MAX) を超えて不正な値になってしまう可能性がある。(imax + imin が INT_MAX を超える可能性が全くないと保証できる場合は問題ない。)Java の標準ライブラリの Arrays.binarySearch() では JDK 1.2 (1998年) から間違えており、Java 6 (2006年) で修正された<ref>[http://bugs.java.com/bugdatabase/view_bug.do?bug_id=5045582 Bug ID: JDK-5045582 (coll) binarySearch() fails for size larger than 1<<30]</ref>。なお、このバグについてクヌースは、自分が気がついていなかったパターンだと、あるインタビューの際に述べている(書籍『Coders at Work』にて。オーム社から出ている邦訳では567ページにある)。 ==関連項目== *[[二分探索木]] *[[二分法]] - 二分探索のようなアイデアで方程式の近似解を求める方法 == 参照 == {{reflist}} {{アルゴリズム}} [[Category:検索アルゴリズム|にふんたんさく]]
このページで使用されているテンプレート:
テンプレート:Cite book
(
ソースを閲覧
)
テンプレート:Cite journal
(
ソースを閲覧
)
テンプレート:Lang
(
ソースを閲覧
)
テンプレート:Lang-en-short
(
ソースを閲覧
)
テンプレート:Pathnav
(
ソースを閲覧
)
テンプレート:Reflist
(
ソースを閲覧
)
テンプレート:アルゴリズム
(
ソースを閲覧
)
二分探索
に戻る。
ナビゲーション メニュー
個人用ツール
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
ソースを閲覧
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
特別ページ
ツール
リンク元
関連ページの更新状況
ページ情報