Pythonで二分探索する
ソート済みリストの中から指定した値の位置を探す。高速に。
情報源
コード
挿入位置を取得する。
import bisect l = ['A', 'B', 'C', 'D', 'E'] # 予めソートしておくこと bisect.bisect_left(l, 'D');
>>> import bisect >>> l = ['A', 'B', 'C', 'D', 'E'] # 予めソートしておくこと >>> bisect.bisect_left(l, 'D'); 3
存在しない値は末尾を指す。
import bisect l = ['A', 'B', 'C', 'D', 'E'] # 予めソートしておくこと bisect.bisect_left(l, 'Z');
>>> import bisect >>> l = ['A', 'B', 'C', 'D', 'E'] # 予めソートしておくこと >>> bisect.bisect_left(l, 'Z'); 5
二分探索
中間にある値より小さいか大きいかを繰り返すことで、高速に検索できる。
追ってみる
こんな感じで検索するはず。
位置 | リスト | 検索位置 |
---|---|---|
0 | A | |
1 | B | |
2 | C | |
3 | D | |
4 | E | ← |
5 | F | |
6 | G | |
7 | H | |
8 | I |
- 先頭
A
〜末尾I
の中間を探す - 中間が探索値ならインデックスを返して終了する
- 中間が探索値より小さいなら、先頭〜中間-1の範囲内でこれをくりかえす
- 中間が探索値より大きいなら、中間+1〜末尾の範囲内でこれをくりかえす
探索値D
は中間値E
より小さい。よって、先頭A
〜中間-1D
の範囲を対象に、これをくりかえす。
位置 | リスト | 検索位置 |
---|---|---|
0 | A | |
1 | B | ← |
2 | C | |
3 | D |
偶数のため中間はない。そこで中間-1を中間とする。
検索対象D
は中間値B
より大きい。よって、中間+1C
〜末尾の範囲を対象に、これをくりかえす。
位置 | リスト | 検索位置 |
---|---|---|
2 | C | ← |
3 | D |
偶数のため中間はない。そこで中間-1を中間とする。
検索対象D
は中間値C
より大きい。よって、中間+1D
〜末尾の範囲を対象に、これをくりかえす。
位置 | リスト | 検索位置 |
---|---|---|
3 | D | ← |
1件しかないため中間値はない。検索対象に一致するならインデックスを返す。さもなくばNone
なり-1
なり例外なり、無効を意味する値を返す。
検索対象D
に一致。これのインデックス3
を返す。
今回の場合、4回目でヒット。これは先頭から順にやったのと同じ回数。ぜんぜん高速じゃない。が、件数が増えるほど、二分探索のほうが少なくて済むはず。
keyを使いたい
複数のキーを対象に位置検索したい。
できないことはないのかもしれない。でもよくわからない。コードもわけわからんくなる。Pythonあるある。
どうしよう……。
複数キーは不可
以下のように公開日時とURLの2キーで一意確認したいのだが、できない。これだとO(N)操作となり高速でなくなるようだ……。
ins_idx = bisect.bisect_left(news_ids, (db_latest_published, db_latest_url))
それならlist.index(探索値)
でいいわ。
結論
二分探索は複数キーで使えない。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- MeCab 0.996ユーザ辞書
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux
前回まで
- NewsApiを使ってみた
- NewsApiでカテゴリ別にニュースを取得する
- NewsApiで得たニュースを重複なく取り込む方法を考える
- NewsApiで得たニュースを保存するSQLite3テーブルを考える
- NewsApiのJSONからSQLite3DBファイルへ挿入する
- SQLite3に登録済みのNewsApiデータから最新を取得する
- NewsApiのJSONからSQLite3DBファイルへ挿入する(未登録のみ)
- HTMLから本文テキストだけを抽出したい(python-extractcontent)
- HTMLから本文を抽出してフォーマットする(改行+全角スペース)
- NewsApiのURLから本文を抽出してSQLite3に挿入する
- ニュースサイトを探す
- PythonでRSSを取得する(feedparser)
- PythonでのWebスクレイピング環境構築(chromium-driver,selenium,beautifulsoup4)
- PythonでRSSからHTMLの本文を抽出してSQLite3に挿入する(重複してしまう版)
- Pythonでソート(複数キーでdescとasc混在)