リスト/タプルでバイナリ検索を実行し、見つかった場合はアイテムの位置を返し、見つからない場合は「False」(-1、Noneなど)を返すライブラリ関数はありますか?
bisectモジュール で関数bisect_left/rightを見つけましたが、アイテムがリストになくても位置を返します。意図した使用法ではこれで十分ですが、リストに項目が含まれているかどうかを知りたいだけです(何も挿入したくない)。
bisect_left
を使用して、その位置にあるアイテムが検索しているものと等しいかどうかをチェックすることを考えましたが、それは面倒です私のリストで)。より良い方法があれば、それについて知りたいです。
Editこれに必要なものを明確にするために:辞書はこれに非常に適していることを知っていますが、可能な限り低いメモリ消費。私の意図する使用法は、ある種の双方向のルックアップテーブルです。テーブルには値のリストがあり、それらのインデックスに基づいて値にアクセスできる必要があります。また、特定の値のインデックス、または値がリストにない場合はNoneを検索できるようにしたいです。
これに辞書を使用するのが最も速い方法ですが、メモリ要件は(ほぼ)2倍になります。
私はPythonライブラリの何かを見落としているかもしれないと考えてこの質問をしていました。 Moeが提案したように、私は自分のコードを書かなければならないようです。
from bisect import bisect_left
def binary_search(a, x, lo=0, hi=None): # can't use a to specify default for hi
hi = hi if hi is not None else len(a) # hi defaults to len(a)
pos = bisect_left(a, x, lo, hi) # find insertion position
return (pos if pos != hi and a[pos] == x else -1) # don't walk off the end
Bisect_left/rightのコードを見て、目的に合わせて調整してみませんか。
このような:
def binary_search(a, x, lo=0, hi=None):
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
midval = a[mid]
if midval < x:
lo = mid+1
Elif midval > x:
hi = mid
else:
return mid
return -1
これは少しトピックから外れています(Moeの答えはOPの質問に対して完全に思えるので)が、手順全体の複雑さを端から端まで見る価値があるかもしれません。ソートされたリスト(バイナリ検索が役立つ場所)に物を保存していて、存在を確認するだけの場合(指定されない限り最悪の場合):
ソート済みリスト
一方、 set()
を使用すると、
ソートされたリストが実際に取得するのは、「次」、「前」、および「範囲」(範囲の挿入または削除を含む)であり、O(1)またはO(| range |)開始インデックス。これらの種類の操作を頻繁に使用しない場合は、セットとして保存し、表示用に並べ替えることをお勧めします。 set()
Pythonでは追加のオーバーヘッドはほとんど発生しません。
Bisectのドキュメントが検索の例を提供していることに言及する価値があるかもしれません: http://docs.python.org/library/bisect.html#searching-sorted-lists
(-1またはNoneを返す代わりにValueErrorを上げることは、よりPythonicです。たとえば、list.index()はそれを行います。しかし、もちろん、あなたのニーズに合わせて例を適応させることができます。)
最も簡単なのは bisect を使用し、1つの位置をチェックして、アイテムが存在するかどうかを確認することです。
def binary_search(a,x,lo=0,hi=-1):
i = bisect(a,x,lo,hi)
if i == 0:
return -1
Elif a[i-1] == x:
return i-1
else:
return -1
これはマニュアルから正しいです:
http://docs.python.org/2/library/bisect.html
8.5.1。ソート済みリストの検索
上記のbisect()関数は、挿入ポイントを見つけるのに役立ちますが、一般的な検索タスクに使用するには扱いにくい場合があります。次の5つの関数は、それらをソート済みリストの標準ルックアップに変換する方法を示しています。
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError
したがって、わずかな変更を加えると、コードは次のようになります。
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
return -1
@ DaveAbrahams's answer bisectモジュールを使用するのが正しいアプローチであることに同意します。彼は答えの中で一つの重要な詳細に言及しなかった。
docsbisect.bisect_left(a, x, lo=0, hi=len(a))
から
バイセクションモジュールでは、事前に検索配列を事前に計算する必要はありません。デフォルトのbisect.bisect_left
とlen(a)
を使用して、エンドポイントを0
の代わりに提示することができます。
私の使用にとってさらに重要なのは、特定の関数のエラーが最小化されるような値Xを探すことです。それを行うには、代わりにbisect_leftのアルゴリズムから計算を呼び出す方法が必要でした。これは本当に簡単です。
__getitem__
をa
として定義するオブジェクトを提供するだけです
たとえば、二等分アルゴリズムを使用して、任意の精度の平方根を見つけることができます!
import bisect
class sqrt_array(object):
def __init__(self, digits):
self.precision = float(10**(digits))
def __getitem__(self, key):
return (key/self.precision)**2.0
sa = sqrt_array(4)
# "search" in the range of 0 to 10 with a "precision" of 0.0001
index = bisect.bisect_left(sa, 7, 0, 10*10**4)
print 7**0.5
print index/(10**4.0)
存在するかどうかだけを見たい場合は、リストを辞書に変えてみてください:
# Generate a list
l = [n*n for n in range(1000)]
# Convert to dict - doesn't matter what you map values to
d = dict((x, 1) for x in l)
count = 0
for n in range(1000000):
# Compare with "if n in l"
if n in d:
count += 1
私のマシンでは、「if n in l」は37秒かかり、「if n in d」は0.4秒かかりました。
これは:
def binsearch(t, key, low = 0, high = len(t) - 1):
# bisecting the range
while low < high:
mid = (low + high)//2
if t[mid] < key:
low = mid + 1
else:
high = mid
# at this point 'low' should point at the place
# where the value of 'key' is possibly stored.
return low if t[low] == key else -1
Dave Abrahamsのソリューションは優れています。私はそれを最小限にしたでしょうが:
def binary_search(L, x):
i = bisect.bisect_left(L, x)
if i == len(L) or L[i] != x:
return -1
return i
Pythonには明示的なバイナリ検索アルゴリズムはありませんが、バイナリ検索を使用して並べ替えられたリスト内の要素の挿入ポイントを見つけるために設計されたモジュールbisect
があります。これをバイナリサーチの実行に「仕掛ける」ことができます。これの最大の利点は、ほとんどのライブラリコードと同じ利点です-高性能で十分にテストされており、動作します(特にバイナリ検索は 非常にうまく実装するのは非常に困難です -特にEdgeの場合は慎重に検討しないでください)。
Stringsやintのような基本型の場合、それは非常に簡単です-必要なのはbisect
モジュールとソートされたリストだけです:
>>> import bisect
>>> names = ['bender', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> bisect.bisect_left(names, 'fry')
1
>>> keyword = 'fry'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
True
>>> keyword = 'arnie'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
False
これを使用して重複を見つけることもできます。
...
>>> names = ['bender', 'fry', 'fry', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> keyword = 'fry'
>>> leftIndex = bisect.bisect_left(names, keyword)
>>> rightIndex = bisect.bisect_right(names, keyword)
>>> names[leftIndex:rightIndex]
['fry', 'fry', 'fry']
必要に応じて、そのインデックスの値ではなく、インデックスを返すこともできます。
カスタム型またはオブジェクトの場合、少し注意が必要です。バイセクトを正しく比較するには、豊富な比較メソッドを実装する必要があります。
>>> import bisect
>>> class Tag(object): # a simple wrapper around strings
... def __init__(self, tag):
... self.tag = tag
... def __lt__(self, other):
... return self.tag < other.tag
... def __gt__(self, other):
... return self.tag > other.tag
...
>>> tags = [Tag('bender'), Tag('fry'), Tag('leela'), Tag('nibbler'), Tag('zoidbe
rg')]
>>> key = Tag('fry')
>>> leftIndex = bisect.bisect_left(tags, key)
>>> rightIndex = bisect.bisect_right(tags, key)
>>> print([tag.tag for tag in tags[leftIndex:rightIndex]])
['fry']
これは少なくともPython 2.7-> 3.3で動作するはずです
このコードは、整数リストで再帰的に機能します。最も単純なケースシナリオを探します。リストの長さが2未満です。これは、回答が既に存在し、正しい回答を確認するためのテストが実行されることを意味します。そうでない場合は、中間値が設定され、正しいとテストされます。そうでない場合は、関数を再度呼び出して二分しますが、中間値を左または右にシフトして上限または下限として設定します。
def binary_search(intList、intValue、lowValue、highValue): if(highValue-lowValue)<2: return intList [lowValue] == intValueまたはintList [highValue] = = intValue middleValue = lowValue +((highValue-lowValue)/ 2) if intList [middleValue] == intValue: return True if intList [middleValue ]> intValue: return binary_search(intList、intValue、lowValue、middleValue-1) return binary_search(intList、intValue、middleValue + 1、highValue)
値が実際のオブジェクトへのポインタに過ぎないため、dictを使用しても、格納するオブジェクトが本当に小さい場合を除き、メモリ使用量が2倍になることはありません。
>>> a = 'foo'
>>> b = [a]
>>> c = [a]
>>> b[0] is c[0]
True
その例では、「foo」は一度だけ保存されます。それはあなたに違いをもたらしますか?とにかく正確にいくつのアイテムについて話しているのでしょうか?
ウィキペディアの例をご覧ください http://en.wikipedia.org/wiki/Binary_search_algorithm
def binary_search(a, key, imin=0, imax=None):
if imax is None:
# if max amount not set, get the total
imax = len(a) - 1
while imin <= imax:
# calculate the midpoint
mid = (imin + imax)//2
midval = a[mid]
# determine which subarray to search
if midval < key:
# change min index to search upper subarray
imin = mid + 1
Elif midval > key:
# change max index to search lower subarray
imax = mid - 1
else:
# return index number
return mid
raise ValueError
s
はリストです。binary(s, 0, len(s) - 1, find)
は最初の呼び出しです。関数は、クエリされたアイテムのインデックスを返します。そのようなアイテムがない場合、-1
を返します。
def binary(s,p,q,find):
if find==s[(p+q)/2]:
return (p+q)/2
Elif p==q-1 or p==q:
if find==s[q]:
return q
else:
return -1
Elif find < s[(p+q)/2]:
return binary(s,p,(p+q)/2,find)
Elif find > s[(p+q)/2]:
return binary(s,(p+q)/2+1,q,find)
pythonのバイナリ検索とDjangoモデルのジェネリック検索が必要でした。 Djangoモデルでは、1つのモデルが別のモデルへの外部キーを持つことができるため、取得したモデルオブジェクトで検索を実行したいと考えました。これを使用できる次の関数を書きました。
def binary_search(values, key, lo=0, hi=None, length=None, cmp=None):
"""
This is a binary search function which search for given key in values.
This is very generic since values and key can be of different type.
If they are of different type then caller must specify `cmp` function to
perform a comparison between key and values' item.
:param values: List of items in which key has to be search
:param key: search key
:param lo: start index to begin search
:param hi: end index where search will be performed
:param length: length of values
:param cmp: a comparator function which can be used to compare key and values
:return: -1 if key is not found else index
"""
assert type(values[0]) == type(key) or cmp, "can't be compared"
assert not (hi and length), "`hi`, `length` both can't be specified at the same time"
lo = lo
if not lo:
lo = 0
if hi:
hi = hi
Elif length:
hi = length - 1
else:
hi = len(values) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if not cmp:
if values[mid] == key:
return mid
if values[mid] < key:
lo = mid + 1
else:
hi = mid - 1
else:
val = cmp(values[mid], key)
# 0 -> a == b
# > 0 -> a > b
# < 0 -> a < b
if val == 0:
return mid
if val < 0:
lo = mid + 1
else:
hi = mid - 1
return -1
上記の多くの良い解決策がありますが、バイナリ検索を行うためにPythonビルトイン/ジェネリックbisect関数を単純に(KISSが単純に(私がそうだからです)愚かに使用しているのを見ませんでした。上記の解決策のいくつかはこれを暗示している/言うが、願わくば下記の簡単なコードが私のような混乱した人の助けになることを願っている。だった。
Python bisectは、新しい値/検索項目をソート済みリストに挿入する場所を示すために使用されます。リスト/配列内の検索項目が見つかった場合にヒットのインデックスを返すbisect_leftを使用する以下のコード(注bisectおよびbisect_rightは、ヒットまたはマッチ後の要素のインデックスを挿入ポイントとして返します)見つからない場合、bisect_leftは、==検索値ではないソート済みリスト内の次のアイテムへのインデックスを返します。他の唯一のケースは、検索項目がリストの末尾に移動し、返されるインデックスがリスト/配列の末尾を超え、コード内でPythonで早期終了する場合です「および」ロジックハンドル。 (最初の条件False Pythonは後続の条件をチェックしません)
#Code
from bisect import bisect_left
names=["Adam","Donny","Jalan","Zach","Zayed"]
search=""
lenNames = len(names)
while search !="none":
search =input("Enter name to search for or 'none' to terminate program:")
if search == "none":
break
i = bisect_left(names,search)
print(i) # show index returned by Python bisect_left
if i < (lenNames) and names[i] == search:
print(names[i],"found") #return True - if function
else:
print(search,"not found") #return False – if function
##Exhaustive test cases:
##Enter name to search for or 'none' to terminate program:Zayed
##4
##Zayed found
##Enter name to search for or 'none' to terminate program:Zach
##3
##Zach found
##Enter name to search for or 'none' to terminate program:Jalan
##2
##Jalan found
##Enter name to search for or 'none' to terminate program:Donny
##1
##Donny found
##Enter name to search for or 'none' to terminate program:Adam
##0
##Adam found
##Enter name to search for or 'none' to terminate program:Abie
##0
##Abie not found
##Enter name to search for or 'none' to terminate program:Carla
##1
##Carla not found
##Enter name to search for or 'none' to terminate program:Ed
##2
##Ed not found
##Enter name to search for or 'none' to terminate program:Roger
##3
##Roger not found
##Enter name to search for or 'none' to terminate program:Zap
##4
##Zap not found
##Enter name to search for or 'none' to terminate program:Zyss
##5
##Zyss not found
バイナリ検索:
// List - values inside list
// searchItem - Item to search
// size - Size of list
// upperBound - higher index of list
// lowerBound - lower index of list
def binarySearch(list, searchItem, size, upperBound, lowerBound):
print(list)
print(upperBound)
print(lowerBound)
mid = ((upperBound + lowerBound)) // 2
print(mid)
if int(list[int(mid)]) == value:
return "value exist"
Elif int(list[int(mid)]) < value:
return searchItem(list, value, size, upperBound, mid + 1)
Elif int(list[int(mid)]) > value:
return searchItem(list, value, size, mid - 1, lowerBound)
//上記の関数を呼び出すには:
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
searchItem = 1
print(searchItem(list[0], item, len(list[0]) -1, len(list[0]) - 1, 0))
'''
Only used if set your position as global
'''
position #set global
def bst(array,taget): # just pass the array and target
global position
low = 0
high = len(array)
while low <= high:
mid = (lo+hi)//2
if a[mid] == target:
position = mid
return -1
Elif a[mid] < target:
high = mid+1
else:
low = mid-1
return -1
私はこれがずっと良くて効果的だと思います。私を修正してください:)。ありがとうございました
def binary_search_length_of_a_list(single_method_list):
index = 0
first = 0
last = 1
while True:
mid = ((first + last) // 2)
if not single_method_list.get(index):
break
index = mid + 1
first = index
last = index + 1
return mid