Existe-t-il une fonction de bibliothèque qui effectue une recherche binaire sur une liste / un 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 toujours une position même si l'élément n'est pas dans la liste. C'est parfaitement bien pour leur utilisation prévue, mais je veux juste savoir si un élément est dans la liste ou non (je ne veux rien insérer).
J'ai pensé à utiliser bisect_left
puis à vérifier si l'élément à cette position est égal à ce que je recherche, mais cela semble fastidieux (et j'ai également besoin de vérifier si le nombre peut être supérieur au plus grand nombre de ma liste). S'il y a une meilleure méthode, j'aimerais en savoir plus.
Edit Pour clarifier ce pour quoi j'ai besoin de ceci: je suis conscient qu'un dictionnaire serait très bien adapté pour cela, mais j'essaie de garder la consommation de mémoire aussi faible que possible. Mon utilisation prévue serait une sorte de table de recherche à 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 être en mesure de trouver l'index d'une valeur particulière ou None 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 devrais écrire mon propre code, comme Moe l'a suggéré.
la source
np.searchsorted
c'est utile. docs.scipy.org/doc/numpy/reference/generated/…Réponses:
la source
if hi is None: hi = len(a)
Pourquoi ne pas regarder le code de bisect_left / right et l'adapter à vos besoins.
comme ça:
la source
hi = mid - 1
dans leelif
?hi = mid
dehi = mid-1
ethi = len(a)
enhi = len(a)-1
etwhile lo < hi:
enwhile lo <= hi
, et ce serait tout à fait correctbisect.bisect_left()
plutôt que cela.C'est un peu hors sujet (puisque la réponse de Moe semble complète à la question du PO), mais il pourrait être intéressant d'examiner la complexité de toute votre procédure de bout en bout. Si vous stockez quelque chose dans une liste triée (ce qui est où une recherche binaire aiderait), et que vous vérifiez simplement l'existence, vous encourez (le pire des cas, sauf indication contraire):
Listes triées
Alors qu'avec un
set()
, vous encourezCe qu'une liste triée vous procure vraiment, ce sont «suivant», «précédent» et «plages» (y compris l'insertion ou la suppression de plages), qui sont O (1) ou O (| plage |), étant donné un index de départ. Si vous n'utilisez pas souvent ce type d'opérations, le stockage en tant qu'ensembles et le tri pour l'affichage pourraient être une meilleure affaire dans l'ensemble.
set()
n'encourt que très peu de frais généraux supplémentaires en python.la source
Il peut être intéressant de mentionner que la documentation bisect fournit désormais des exemples de recherche: http://docs.python.org/library/bisect.html#searching-sorted-lists
(Augmenter ValueError au lieu de renvoyer -1 ou None est plus pythonique - list.index () le fait, par exemple. Mais bien sûr, vous pouvez adapter les exemples à vos besoins.)
la source
Le plus simple consiste à utiliser une bissectrice et à cocher une position pour voir si l'élément est là:
la source
Cela vient 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 à utiliser pour les tâches de recherche courantes. Les cinq fonctions suivantes montrent comment les transformer en recherches standard pour les listes triées:
Donc, avec la légère modification, votre code devrait être:
la source
Je suis d'accord que la réponse de @ DaveAbrahams en utilisant le module bisect est la bonne approche. Il n'a pas mentionné un détail important dans sa réponse.
À partir des documents
bisect.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 à la
bisect.bisect_left
place de celui-ci en utilisant les valeurs par défaut de0
etlen(a)
.Encore plus important pour mon utilisation, la recherche d'une valeur X telle que l'erreur d'une fonction donnée soit minimisée. Pour ce faire, j'avais besoin d'un moyen pour que l'algorithme de bisect_left appelle mon calcul à la place. C'est vraiment simple.
Fournissez simplement un objet qui définit
__getitem__
commea
Par exemple, nous pourrions utiliser l'algorithme de bissectrice pour trouver une racine carrée avec une précision arbitraire!
la source
scipy.optimize
pour cela.Si vous voulez juste voir s'il est présent, essayez de transformer la liste en un dict:
Sur ma machine, "if n in l" a pris 37 secondes, tandis que "if n in d" a pris 0,4 seconde.
la source
Celui-ci est:
la source
La solution de Dave Abrahams est bonne. Bien que je l'aurais fait minimaliste:
la source
Bien qu'il n'y ait pas d'algorithme de recherche binaire explicite en Python, il existe un module -
bisect
- conçu pour trouver 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 plus grand avantage de ceci est le même avantage que la plupart du code de bibliothèque - il est très performant, bien testé et fonctionne juste (les recherches binaires en particulier peuvent être assez difficiles à implémenter avec succès - en particulier si les cas de pointe ne sont pas soigneusement pris en compte).Types de base
Pour les types de base comme les chaînes ou les entiers, c'est assez facile - tout ce dont vous avez besoin est le
bisect
module et une liste triée:Vous pouvez également l'utiliser pour rechercher des doublons:
De toute évidence, vous pouvez simplement renvoyer l'index plutôt que la valeur de cet index si vous le souhaitez.
Objets
Pour les types ou objets personnalisés, les choses sont un peu plus délicates: vous devez vous assurer d'implémenter des méthodes de comparaison riches pour obtenir une bissectrice pour comparer correctement.
Cela devrait fonctionner au moins dans Python 2.7 -> 3.3
la source
L'utilisation d'un dict ne voudrait pas doubler votre utilisation de la mémoire à moins que les objets que vous stockez soient vraiment minuscules, car les valeurs ne sont que des pointeurs vers les objets réels:
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?
la source
Ce code fonctionne avec des listes d'entiers de manière récursive. Recherche le scénario de cas le plus simple, qui est: longueur de la liste inférieure à 2. Cela signifie que la réponse est déjà là et un test est effectué pour vérifier la bonne réponse. Sinon, une valeur médiane est définie et testée pour être correcte, sinon la bissection est effectuée en appelant à nouveau la fonction, mais en définissant la valeur médiane comme limite supérieure ou inférieure, en la décalant vers la gauche ou la droite
la source
Consultez les exemples sur Wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm
la source
Je suppose que c'est beaucoup mieux et efficace. s'il vous plaît corrigez-moi :) . Je vous remercie
la source
s
est une liste.binary(s, 0, len(s) - 1, find)
est l'appel initial.Function renvoie un index de l'élément interrogé. S'il n'y a pas un tel article, il retourne
-1
.la source
la source
Recherche binaire :
// Pour appeler la fonction ci-dessus, utilisez:
la source
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 une clé étrangère vers un autre modèle et je voulais effectuer une recherche sur les objets modèles récupérés. J'ai écrit la fonction suivante, vous pouvez l'utiliser.
la source
Beaucoup de bonnes solutions ci-dessus, mais je n'ai pas vu une utilisation simple (KISS reste simple (parce que je suis) stupide de la fonction Bissect intégrée / générique de Python pour faire une recherche binaire. Avec un peu de code autour de la fonction Bissect, Je pense avoir un exemple ci-dessous où j'ai testé tous les cas pour un petit tableau de chaînes de noms. Certaines des solutions ci-dessus font allusion à / disent cela, mais j'espère que le code simple ci-dessous aidera n'importe qui dans la confusion comme moi.
Python bisect est utilisé pour indiquer où insérer une nouvelle valeur / élément de recherche dans une liste triée. Le code ci-dessous qui utilise bisect_left qui renverra l'index du hit si l'élément de recherche dans la liste / tableau est trouvé (Notez que bisect et bisect_right renverront l'index de l'élément après le hit ou match comme point d'insertion) Si non trouvé , bisect_left renverra un index vers l'élément suivant dans la liste triée qui ne sera pas == la valeur de recherche. Le seul autre cas est celui où l'élément de recherche irait à la fin de la liste où l'index retourné serait au-delà de la fin de la liste / tableau, et qui dans le code ci-dessous la sortie anticipée de Python avec des poignées logiques "et". (première condition False Python ne vérifie pas les conditions suivantes)
la source