Upgrade to Pro — share decks privately, control downloads, hide ads and more …

みんなで書こう二分探索

 みんなで書こう二分探索

2021/7月の大LTでの発表資料です

イベントページはこちら
https://zli.connpass.com/event/216615/

matumoto

June 10, 2021
Tweet

More Decks by matumoto

Other Decks in Technology

Transcript

  1. なにができるの ・ある境界を求められる 例 A = {3, 5, 10, 20, 33,

    90, 98}の配列で値が15以上を満たす最小の index を返す場合 left : 0 right : 6 mid : 3 ← ( left + right ) / 2 A mid は20なので、15以上! ということは、探索する区間を左側にずらす→ right を mid にする
  2. A = {3, 5, 10, 20, 33, 90, 98}の配列で値が15以上を満たす最小の index

    を返す場合 left : 0 right : 3 ← mid mid : 1 ← ( left + right ) / 2 A mid は5なので、15以上じゃない! ということは、探索する区間を右側にずらす→ left を mid + 1にする
  3. A = {3, 5, 10, 20, 33, 90, 98}の配列で値が15以上を満たす最小の index

    を返す場合 left : 2 ← mid + 1 right : 3 mid : 2 ← ( left + right ) / 2 A mid は10なので、15以上! ということは、探索する区間を右側にずらす→ left を mid + 1にする
  4. A = {3, 5, 10, 20, 33, 90, 98}の配列で値が15以上を満たす最小の index

    を返す場合 left : 3 ← mid + 1 right : 3 mid : 3 ← ( left + right ) / 2 A mid は20なので、15以上! left と right が同じになったので left を返す( right でもよい)
  5. なにがうれしい? ・応用ができる! 例:値の検索 配列の中で値が x 以上を満たす最小の index → index 番目が

    x ならOK、そうでないならその配列にはない ・計算量が少ない! 最悪でも計算量がO(logN) 0~1018くらいの区間だったとしても十分に高速(そんな大きい数を扱う職場...)
  6. 2.標準の関数を使う • C++ ◦ lower_bound ◦ upper_bound • Python ◦

    bisect • java ◦ Arrays.binarySearch ただ、配列に対して二分探索はできるが それ以外への二分探索ができない...
  7. めぐる式二分探索の前に.. ・二分探索の実装上の工夫(先人たちの汗と涙の知恵) • 区間は半開区間[ left, right )で扱う 閉区間[ left, right

    ] これは left 以上 right 以下 半開区間 [ left, right ) これは left 以上 right 未満 今まで [ left, right ] → [ left, mid - 1 ]だったが [ left, right ) → [ left, mid )と簡潔に! ※ left を mid にする場合もおなじ ついでに、長さが N の配列のとき [ 0, N - 1 ] としてたのが [ 0, N ) にできる
  8. めぐる式二分探索とは • めぐる式二分探索の特徴 ・ループの条件に絶対値を使っている ・ok, ng などの変数がある ・半開区間 ・最終的には ok

    に求めたい値が入っている ・判定部分を関数にしている(←これについては諸説ありです) (ok, ng 以外の変数名の候補としては valid, invalid がある)
  9. valid, invalid(ok, ng)みたいな変数には何の値が入るの? • ある条件を元にその境界を求めたい→二分探索でのis_valid関数を作る • validにはis_valid(valid)としたときに必ずtrueとなる値を入れる • invalidにはis_valid(invalid)としたときに必ずfalseとなる値を入れる •

    例 ◦ n要素のソート済み配列 aからx以上の要素の最小のインデックスを見つけたい ▪ (配列aにはx以上の要素が少なくとも 1つは存在するものとする ) ◦ is_valid関数はindexを受け取ってa[i] >= x かどうかを判定するように作る ◦ validの初期値にはn-1を入れておく ◦ invalidの初期値には-1を入れておく(今回は invalidが開区間なので0じゃない) ◦ これであとは二分探索するだけ ▪ ※実際には、配列aの末尾に番兵として INFを挿入してvalid=nとする方が多いと思います