二分探索とは
二分探索とは、データ検索アルゴリズムの一つで、ソート(整列)済みのデータ群の探索範囲を半分に絞り込むを操作を繰り返すことで高速に探索を行う手法。 二分探索(2分探索 / バイナリサーチ)とは - 意味をわかりやすく - IT用語辞典 e-Words
・ポイント
探索のたびに探索範囲が半分になるから高速。
ソート済みじゃないとダメ。
・競プロ用ポイント
めぐる式二分探索というのがバグりにくい。
条件を満たす最小を求めるか、最大を求めるかで若干コードを変える。
理解のポイント
OKは常に条件を満たすことが確定したエリアにいる。
NGは常に条件を満たさないことが確定したエリアにいる。
自力実装
条件を満たす最小の値を求める
# ok・・・条件を満たすなかで最小のindex。左側をOKにする # ng・・・条件を満たさないなかで最大のindex。右側をNGにする def is_ok2(i): return i <= 5 ok = 1 #左側(解が存在する値) ng = 11 #右側(解が存在しない値) while abs(ok-ng) > 1: mid = (ok+ng) // 2 if is_ok2(mid): ok = mid else: ng = mid print(ok,ng) # "5 6" が出力される
条件を満たす最大の値を求める
# ok・・・条件を満たすなかで最大のindex。右側をOKにする # ng・・・条件を満たさないなかで最小のindex。左側をNGにする def is_ok(i): return i > 5 ok = 10 #右側(解が存在する値) ng = -1 #左側(解が存在しない値) while abs(ok-ng) > 1: mid = (ok+ng) // 2 if is_ok(mid): ok = mid else: ng = mid print(ok,ng) # "6 5" が出力される
参考サイト
【めぐるのアルゴリズム講座】
— 因幡めぐる@競技プログラミング (@meguru_comp) 2016年2月9日
二分探索(整数)の書き方
難しさ:4 pic.twitter.com/LGLbkS0D7l
bisect関数
import bisect ls1 = [1,2,2,3,4,5] #bisect(ソート済みリスト, 探索したい値) print(bisect.bisect_left(ls1, 2)) #1 挿入できる値の左INDEXを返す print(bisect.bisect_right(ls1, 2)) #3 挿入できる値の右INDEXを返す print(bisect.bisect(ls1, 2)) #3 同上
番兵の活用方法
・実データには出現しない、データの終了を表すための専用の値
・入力データを処理するループの終了条件が複数ある場合に、条件判定の数を削減するために置くダミーのデータ
番兵 - Wikipedia
2分探索で配列外参照にならないように、両端に番兵を置く。
import bisect ls1 = [1,2,2,3,4,5] i = bisect.bisect_left(ls1, 8) #6。配列外のINDEXが返る ls1[i] # 配列外参照エラー ls1 = ls1 + [-1*10**9, 10**9]#両端に番兵を追加 ls1.sort() i = bisect.bisect_left(ls1, 6) #6。5と10**9の間のINDEX ls1[i] # 1000000000 print(ls1) #[-1000000000, 1, 2, 2, 3, 4, 5, 1000000000]
・参考
アライグマ「ABC212のC問題の原案だったのだ。C問題は、Biそれぞれで「Biに一番近いAの要素」を見つけられればいいから、Aをソートして二分探索すればいいのだ! Aに番兵を入れると端の場合分けがいらなくなって実装が簡単なのだ!」
— 競技プログラミングをするフレンズ (@kyopro_friends) 2021年7月31日
Python:https://t.co/Ifq8BNPSxP