Pythonで二分探索を行うライブラリ「bisect」

Pythonで二分探索を行うライブラリ「bisect」

Sep 14, 2019

趣味で競プロをやるようになり、Atcoderの問題を何問か解いているのだが、
やっている内に覚えないといけないこともいくつかあるわけで。
その内の一つに二分探索というアルゴリズムを使うという場面を多く見てきたため、自分が今よく利用しているpythonでいつでも使えるように二分探索を行うスクリプトでも書いておこうかと思った。

しかし取り掛かろうとして調べて見たところ、なんとpythonには二分探索を行うライブラリが最初から用意されているらしい。 それがこの「bisect」というもので、このライブラリについて調べて見た。

bisect #

bisectモジュールのbisect関数はリストと値を入力とし、値をリストに挿入する位置のインデックスを返す。 とりあえずは使用例ということで、bisectを使ってみた。

>>> import bisect
>>> a=[10,20,30,40,50,60,70,80,90,100]
>>> 
>>> bisect.bisect(a,55)
5
>>> 

この例の場合、リストaに値55を入れようとした時、a[5](50と60の間)に入れるのが適切なため、5を返す。

ちなみにbisectを使う時、入力するリストはすでにソート済みであることが前提である(これは本来の二分探索でも同じ)。ソートされてないリストを入力してもエラーは発生しないが、ソートしていないため間違った形で二分探索が行われてしまう。

>>> import bisect
>>> a=[54,32,76,33,89,44,323,67,88,1]
>>> bisect.bisect(a,55) # ソートしなくても二分探索は行うが・・・
6

また、入力した値が既にリストに入っていた場合、リストでその値の前に入れようとするのか、後に入れようとするのか分かれるがどうなるのか?

bisectモジュールには更にbisect_leftbisect_rightという関数があり、各々の場合に応じてこれらを使い分ける。 使用例を示す。

>>> import bisect
>>> a=[1,2,2,2,3]
>>> 
>>> bisect.bisect_left(a,2)
1
>>> bisect.bisect_right(a,2)
4
>>> bisect.bisect(a,2)
4

この例の場合、a[1]からa[3]まで2であり、bisect_leftで2をリストaに適用すると、挿入点は2の一番前の位置である1を返す。 bisect_rightを使った場合はその逆で、挿入点は2の一番後の位置である4を返す。 ちなみにbisect関数はbisect_rightと同じ動作をする。

insort #

insort関数はbisect関数の動作に加えて、リストへの挿入も行う関数である。 bisect関数と同様に、リストに入力と同じ値があった場合にその値の前か後のどちらに挿入するかは、insort_leftinsort_rightという関数があるので使い分ける。 ちなみにinsort関数はinsort_rightと同じ動作をする。

>>> import bisect
>>> a=[10,20,30,40,50,60,70,80,90,100]
>>> a
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> 
>>> bisect.insort(a,55)
>>> a
[10, 20, 30, 40, 50, 55, 60, 70, 80, 90, 100]
>>> 

P.S.

今後二分探索をする必要が出てきたときはこれらを活用していきたい。 まあでも、できればライブラリ頼りではなく、本当はアルゴリズム自体も自分で理解して、一から書けるようにもすべきだけど・・ね