回 2-1 道 2 データ構造とアルゴリズム
二分探索は、
読み終えたらデータが
この頁の流れ
探す前に
100 万件の 名簿から 1 人を、 何回で 見つけられるか
名簿が
1 から
この
この
線形探索と 二分 探索は どう動くのか
線形探索は
線形探索は 端から 順に比べる
線形探索は、
最悪なのは、
二分探索は 真ん中を 見て 半分を捨てる
二分探索は、
- 候補の
範囲の 真ん中の値を見る - 探す値と
同じなら、 見つかったので止まる - 真ん中の
値が 探す値より 小さければ、 真ん中と その 左を 捨てる。 大きければ、 真ん中と その 右を捨てる - 候補が
無くなったら、 探す値は無い
1 回見る
この
1,000 倍で 10 回しか 増えない理由
2 を
データが
これを
| 件数 | 線形探索 | 二分探索 |
|---|---|---|
| 1,000 | 1,000 | 10 |
| 10,000 | 10,000 | 14 |
| 100,000 | 100,000 | 17 |
| 1,000,000 | 1,000,000 | 20 |
表は、
動かして確かめる
1 本目は、
動かす 線形探索と二分探索の回数を数える
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
1000 1000 10
10000 10000 14
100000 100000 17
1000000 1000000 20線形探索の
2 本目は、n.bit_length() (2 進数の
動かす 数当てと 2 進数の桁数
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
最悪 7
平均 5.8
1 回で当たる 1
7 回かかる 37
8 1000 4 4 4
100 1100100 7 7 7
1000 1111101000 10 10 10
1024 10000000000 11 11 111 から
後半の
二分探索は どこで 崩れるのか
二分探索は、
並んでいないと、 ある 値を見落とす
二分探索は
動かす 並んでいないと見落とす
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
in で探す True
並べずに二分探索
見た 1
見た 7
見た 5
答え False
並べてから二分探索
見た 5
見た 7
見た 9
答え True[3, 9, 1, 7, 5] には
並んでいない
真ん中の 計算が、 器の 幅を超える
真ん中の位置を (low + high) // 2 でlow + high がint は
Python の int は& 0xFFFFFFFF で
動かす 32 ビットで真ん中を求める
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
本当の和 3100000000
32 ビットの和 -1194967296
誤った真ん中 -597483648
正しい真ん中 1550000000本当のlow + (high - low) // 2 と
1 回しか 探さないなら、 並べるほうが損
二分探索の
次の== と < で
動かす 並べてから探すと得になる回数
▶ 実行ボタンを押すと結果がここに表示されます
実行すると、次の通りに出ます
並べる 8723
線形の平均 500.5
二分の平均 16.974
得になる探す回数 19並べるのに
データが
二分探索は 仕事で どう 確かめるのか
仕事では、
- Python では標準の
bisectを使う。bisect_left(a, x)は、並んだaにxを入れても 並びが 崩れない、 いちばん左の 位置を 返す。 公式文書は、 並んだ 状態を 保つための 道具で、 比べるのが 重い 長いリストでは 線形探索や 並べ直しより 得に なることがあると 書いている - 探す前に、
データが 並んでいる ことを 確かめる。試験で a == sorted(a)を見るのが手軽 - 線形探索と
答えを 照らす。 どの 値を 探しても、 二分探索と 線形探索が 同じ 答えを 返す ことを 試験で確かめる - 境目を
試す。 0 件、 1 件、 先頭、 末尾、 無い値、 同じ 値が 並んだ所の 6 つは、 書き間違いが出やすい
二分探索は、git bisect は、
データベースの< や >= を
bisect で探す
from bisect import bisect_left
def find(a, x):
i = bisect_left(a, x)
if i == len(a):
return False
return a[i] == x関わる実例
Java の 二分探索に 9 年ほど 潜んでいた 上限越え (2006年6月2日)
2006年6月2日、java.util.Arrays にint mid = (low + high) / 2; のlow と high の和が int のArrayIndexOutOfBoundsException が出ます。
記事にlow + ((high - low) / 2) と、(low + high) >>> 1 が
崩れた
出典は Nearly All Binary Searches and Mergesorts are Broken (Google Research Blog) です。
よくある問い
二分探索の 計算量は なぜ O(log n) なのですか
1 回見る
二分探索は 並んでいない データにも 使えますか
使えません。
二分探索と ハッシュ探索は どう 使い 分けますか
あるset や dict の in は
同じ 値が 並んでいる とき、 最初の 位置は 探せますか
探せます。bisect_left は、bisect_right は
二分探索は 配列の ほかにも 使えますか
使えます。git bisect や、
確認クイズ
答えだと
1 問目
100 万件の
2 問目
並んでいないリスト [3, 9, 1, 7, 5] で、
3 問目
1000 件の
4 問目
このcount は、a は
真ん中を見た回数
def count(a, x):
lo = 0
hi = len(a) - 1
n = 0 # 真ん中を見た回数
while lo <= hi:
mid = (lo + hi) // 2
n += 1
if a[mid] == x:
return n
if a[mid] < x:
lo = mid + 1
else:
hi = mid - 1
return n
a = list(range(1, 16))
print(count(a, 1))説明課題
データが
自分の
この回の 先の一歩
並んだ
もっと深く
この
一次資料
どれも
- Nearly All Binary Searches and Mergesorts are Broken (Google Research Blog、英語)。2006年6月2日の公表、
java.util.Arraysの二分探索の (low + high) / 2がintの最大値を 超えて 負に なる こと、 要素が 2 の 30 乗以上の 配列で 起きる こと、 9 年ほど 潜んでいた こと、 直し方の 2 つ、 マージソートにも 当てはまること - bisect, Array bisection algorithm (Python 公式文書、英語)。並んだ
状態を 保つための 道具である こと、 比べるのが 重い 長いリストでは 線形探索や 並べ直しより 得に なること、 bisect_leftがいちばん左の 位置を 返すこと、 bisect_rightが同じ値の 右に 入れる 位置を返すこと - Time complexity of operations on built-in types (Python 公式文書、英語)。
setとdictのinが平均 O(1) であること - git-bisect (Git 公式文書、英語)。二分探索で
バグの 入った コミットを 探す道具であること - Index Types (PostgreSQL 公式文書、英語)。B-tree の
索引が、 並べられる データの 等しいと 範囲の 問い 合わせを扱い、 <や>=の比べで 使われること
次へ
道 2 データ構造とアルゴリズムの回
- 2-0アルゴリズムとは順次、
分岐、 反復の 3 つで書く手順頁で読む - 2-1線形探索と二分探索100 万件でも
20 回で見つかるこの回 - 2-2バブルソートと
挿入・選択ソート遅い ソート 3 つ、 2 倍で 約 4 倍頁で読む - 2-3クイックソートと
マージソート速い ソート 2 つ、 平均と最悪頁で読む - 2-4スタックとキュー積む順と並ぶ順頁で読む
- 2-5再帰自分を
呼んで 小さくする頁で読む - 2-6木と
二分探索木たどる 順、片寄り頁で読む - 2-7ハッシュテーブル鍵から
一気に 引く、衝突頁で読む - 2-8ダイクストラ法と最短経路点と
線、 幅優先、 重みのある道頁で読む - 2-9動的計画法同じ
計算を 2 度しない頁で読む - 2-10試験と
面接で確かめる基本情報 科目B、 道場 10 問、 面接 20 問頁で読む
