Existe-t-il une fonction de bibliothèque qui effectue une recherche binaire sur une liste/tuple et renvoie la position de l'élément s'il est trouvé et 'False' (-1, Aucun, etc.) sinon?
J'ai trouvé les fonctions bisect_left/right dans le module bisect , mais elles renvoient quand même une position même si l'élément n'est pas dans la liste. Cela convient parfaitement à l'usage auquel ils sont destinés, mais je veux simplement savoir si un élément est dans la liste ou non (ne souhaite rien insérer).
J'ai pensé à utiliser bisect_left
et à vérifier ensuite si l'élément à cette position est égal à ce que je cherche, mais cela semble fastidieux (et j'ai également besoin de faire des limites pour vérifier si le nombre peut être supérieur au plus grand nombre de ma liste) . S'il y a une méthode plus agréable, j'aimerais le savoir.
Edit Pour préciser pourquoi j'ai besoin de cela: je suis conscient qu'un dictionnaire conviendrait très bien à cela, mais j'essaie de garder la consommation de mémoire au minimum. Mon utilisation prévue serait une sorte de table de conversion à double sens. J'ai dans le tableau une liste de valeurs et je dois pouvoir accéder aux valeurs en fonction de leur index. Et aussi je veux pouvoir trouver l'index d'une valeur particulière ou Aucun si la valeur n'est pas dans la liste.
Utiliser un dictionnaire pour cela serait le moyen le plus rapide, mais doublerait (approximativement) les besoins en mémoire.
Je posais cette question en pensant que j'avais peut-être oublié quelque chose dans les bibliothèques Python. Il semble que je devrai écrire mon propre code, comme suggéré par 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
Pourquoi ne pas regarder le code pour bisect_left/right et l’adapter à vos besoins.
comme ça:
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
Ceci est un peu hors sujet (puisque la réponse de Moe semble complète à la question du PO), mais il pourrait être intéressant de regarder la complexité de toute votre procédure de bout en bout. Si vous stockez des éléments dans des listes triées (dans lesquelles une recherche binaire pourrait vous aider), et que vous ne faites que vérifier l'existence, vous rencontrez (dans le pire des cas, sauf indication contraire):
Listes triées
Considérant qu'avec un set()
, vous vous engagez
Ce que la liste triée obtient vraiment, vous êtes "next", "previous" et "gammes" (y compris l'insertion ou la suppression de gammes), qui sont O(1) ou O (| range |), avec un début indice. Si vous n'utilisez pas souvent ce type d'opération, alors stocker dans des ensembles et trier pour l'affichage peut être une meilleure affaire dans son ensemble. set()
n'engendre que très peu de temps système supplémentaire en python.
Il est peut-être intéressant de mentionner que la documentation de la bisectrice fournit maintenant des exemples de recherche: http://docs.python.org/library/bisect.html#searching-sorted-lists
(Augmenter ValueError au lieu de retourner -1 ou None, c'est plus Pythonic - list.index () le fait, par exemple. Mais bien sûr, vous pouvez adapter les exemples à vos besoins.)
Le plus simple est d'utiliser bisecter et de vérifier une position pour voir si l'élément est là:
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
Ceci est juste du manuel:
http://docs.python.org/2/library/bisect.html
8.5.1. Recherche de listes triées
Les fonctions bisect () ci-dessus sont utiles pour trouver des points d’insertion, mais peuvent être difficiles ou difficiles à utiliser pour les tâches de recherche courantes. Les cinq fonctions suivantes montrent comment les transformer en recherches standard pour les listes triées:
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
Donc, avec la légère modification, votre code devrait être:
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
Je conviens que la réponse de @ DaveAbrahams en utilisant le module bissect est la bonne approche. Il n'a pas mentionné un détail important dans sa réponse.
À partir de docsbisect.bisect_left(a, x, lo=0, hi=len(a))
Le module de bissection ne nécessite pas que le tableau de recherche soit pré-calculé à l'avance. Vous pouvez simplement présenter les points de terminaison au bisect.bisect_left
à la place, en utilisant les valeurs par défaut de 0
et len(a)
.
Encore plus important pour mon utilisation, rechercher une valeur X telle que l’erreur d’une fonction donnée soit minimisée. Pour ce faire, j'avais besoin d'un moyen de faire en sorte que l'algorithme de bisect_left appelle mon calcul. C'est vraiment simple.
Fournissez simplement un objet qui définit __getitem__
comme a
Par exemple, nous pourrions utiliser l'algorithme de bissect pour trouver une racine carrée avec une précision arbitraire!
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)
Si vous voulez juste voir si elle est présente, essayez de transformer la liste en dict:
# 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
Sur ma machine, "si n dans l" a pris 37 secondes, tandis que "si n dans d" a pris 0,4 seconde.
La solution de Dave Abrahams est bonne. Bien que je l'aurais fait minimaliste:
def binary_search(L, x):
i = bisect.bisect_left(L, x)
if i == len(L) or L[i] != x:
return -1
return i
Celui-ci est:
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
Bien qu'il n'y ait pas d'algorithme de recherche binaire explicite en Python, il existe un module - bisect
- conçu pour rechercher le point d'insertion d'un élément dans une liste triée à l'aide d'une recherche binaire. Cela peut être "trompé" en effectuant une recherche binaire. Le principal avantage de cet avantage est identique à celui de la plupart des codes de bibliothèque: il est hautement performant, bien testé et fonctionne parfaitement (les recherches binaires en particulier peuvent être assez difficiles à mettre en œuvre avec succès - en particulier si les cas Edge ne sont pas soigneux pris en considération).
Pour les types de base tels que Strings ou ints, c'est assez simple - tout ce dont vous avez besoin est le module bisect
et une liste triée:
>>> 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
Vous pouvez également l'utiliser pour trouver des doublons:
...
>>> 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']
Évidemment, vous pouvez simplement renvoyer l'index plutôt que la valeur à cet index si vous le souhaitez.
Pour les types ou les objets personnalisés, les choses sont un peu plus compliquées: vous devez vous assurer d’implémenter des méthodes de comparaison enrichies pour que la comparaison soit correcte.
>>> 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']
Cela devrait fonctionner dans au moins Python 2.7 -> 3.3
Ce code fonctionne avec les listes entières de manière récursive. Recherche le scénario de cas le plus simple, à savoir: longueur de liste inférieure à 2. Cela signifie que la réponse est déjà présente et qu'un test est effectué pour vérifier la réponse correcte . Si ce n'est pas le cas, une valeur intermédiaire est définie et testée. la bissection correcte est effectuée en rappelant la fonction, mais en définissant la valeur centrale comme limite supérieure ou inférieure, en la décalant vers la gauche ou la droite
def binary_search (intList, intValue, lowValue, highValue): if (highValue - lowValue) & lt 2: return intList [lowValue] == intValue ou intList [highValue] == intValue middleValue = lowValue + ((highValue - lowValue)/2) si intList [middleValue] == intValue: retourne True si intList [middleValue]> intValue: return binary_search (intList, intValue, lowValue, middleValue - 1) return binary_search (intList, intValue, middleValue + 1, highValue)
Utiliser un dict ne voudrait pas doubler votre utilisation de la mémoire, sauf si les objets que vous stockez sont vraiment minuscules, car les valeurs ne sont que des pointeurs sur les objets réels:
>>> a = 'foo'
>>> b = [a]
>>> c = [a]
>>> b[0] is c[0]
True
Dans cet exemple, "foo" n'est stocké qu'une seule fois. Cela fait-il une différence pour vous? Et de combien d'articles parlons-nous exactement?
Découvrez les exemples sur Wikipedia http://en.wikipedia.org/wiki/algorithme_recherche_binaire
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
est une liste. binary(s, 0, len(s) - 1, find)
est l'appel initial.Function renvoie un index de l'élément recherché. Si aucun élément de ce type n'existe, il retourne -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)
J'avais besoin d'une recherche binaire en python et générique pour les modèles Django. Dans les modèles Django, un modèle peut avoir la clé étrangère d'un autre modèle et je voulais effectuer une recherche sur les objets de modèles extraits. J'ai écrit la fonction suivante, vous pouvez l'utiliser.
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
Beaucoup de bonnes solutions ci-dessus mais je n'ai pas vu d'utilisation simple (KISS garder les choses simples (parce que je suis) stupide de la fonction intégrée/générique de Python pour effectuer une recherche binaire. Avec un peu de code autour de la fonction de bissect, Je pense avoir un exemple ci-dessous dans lequel j'ai testé tous les cas pour un petit tableau de noms de chaînes. Certaines des solutions ci-dessus font allusion à/disent cela, mais j'espère que le code simple ci-dessous aidera toute personne confuse comme moi.
Bisect Python est utilisé pour indiquer où insérer une nouvelle valeur/recherche dans une liste triée. Le code ci-dessous qui utilise bisect_left qui renverra l'index de l'occurrence si l'élément de recherche dans la liste/le tableau est trouvé (Note bisect et bisect_right renverront l'index de l'élément après l'occurrence ou la correspondance comme point d'insertion). Si introuvable , bisect_left retournera un index au prochain élément de la liste triée qui ne sera pas == la valeur de recherche. Le seul autre cas possible est le cas où l'élément de recherche irait à la fin de la liste, où l'index renvoyé serait au-delà de la fin de la liste/du tableau, et qui, dans le code situé sous la sortie anticipée par Python avec des descripteurs logiques "et". (première condition False Python ne vérifie pas les conditions suivantes)
#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
Recherche binaire :
// 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)
// Pour appeler la fonction ci-dessus, utilisez:
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
Je suppose que c'est beaucoup mieux et efficace. s'il vous plaît corrigez-moi :) . Je vous remercie
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