web-dev-qa-db-fra.com

Le moyen le plus rapide de vérifier si une valeur existe dans une liste

Quel est le moyen le plus rapide de savoir si une valeur existe dans une liste (une liste contenant des millions de valeurs) et quel est son index?

Je sais que toutes les valeurs de la liste sont uniques, comme dans cet exemple.

La première méthode que j'essaie est (3,8 secondes dans mon code réel):

a = [4,2,3,1,5,6]

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"

La deuxième méthode que j'essaie est (2x plus rapide: 1.9 sec pour mon code réel):

a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"

Méthodes proposées par l'utilisateur Stack Overflow (2,74 s pour mon vrai code)):

a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)

Dans mon code réel, la première méthode prend 3,81 secondes et la seconde, 1,88 seconde. C'est une bonne amélioration, mais:

Je suis un débutant avec Python/script, et existe-t-il un moyen plus rapide de faire la même chose et d’économiser plus de temps de traitement?

Explication plus spécifique pour mon application:

Dans l’API de Blender, je peux accéder à une liste de particules:

particles = [1, 2, 3, 4, etc.]

De là, je peux accéder à l'emplacement d'une particule:

particles[x].location = [x,y,z]

Et pour chaque particule, je teste si un voisin existe en recherchant chaque emplacement de particule comme suit:

if [x+1,y,z] in particles.location
    "Find the identity of this neighbour particle in x:the particle's index
    in the array"
    particles.index([x+1,y,z])
696
7 in a

Le moyen le plus clair et le plus rapide de le faire.

Vous pouvez également envisager d'utiliser un set, mais la construction de cet ensemble à partir de votre liste risque de prendre plus de temps que les tests d'adhésion plus rapides ne sauveront. Le seul moyen d'être certain est de bien mesurer les performances. (cela dépend aussi de quelles opérations vous avez besoin)

1361
Rafe Kettler

Comme indiqué par d'autres, in peut être très lent pour les grandes listes. Voici quelques comparaisons des performances pour in, set et bisect. Notez que le temps (en secondes) est en échelle log.

enter image description here

Code pour tester:

import random
import bisect
import matplotlib.pyplot as plt
import math
import time

def method_in(a,b,c):
    start_time = time.time()
    for i,x in enumerate(a):
        if x in b:
            c[i] = 1
    return(time.time()-start_time)   

def method_set_in(a,b,c):
    start_time = time.time()
    s = set(b)
    for i,x in enumerate(a):
        if x in s:
            c[i] = 1
    return(time.time()-start_time)

def method_bisect(a,b,c):
    start_time = time.time()
    b.sort()
    for i,x in enumerate(a):
        index = bisect.bisect_left(b,x)
        if index < len(a):
            if x == b[index]:
                c[i] = 1
    return(time.time()-start_time)

def profile():
    time_method_in = []
    time_method_set_in = []
    time_method_bisect = []

    Nls = [x for x in range(1000,20000,1000)]
    for N in Nls:
        a = [x for x in range(0,N)]
        random.shuffle(a)
        b = [x for x in range(0,N)]
        random.shuffle(b)
        c = [0 for x in range(0,N)]

        time_method_in.append(math.log(method_in(a,b,c)))
        time_method_set_in.append(math.log(method_set_in(a,b,c)))
        time_method_bisect.append(math.log(method_bisect(a,b,c)))

    plt.plot(Nls,time_method_in,marker='o',color='r',linestyle='-',label='in')
    plt.plot(Nls,time_method_set_in,marker='o',color='b',linestyle='-',label='set')
    plt.plot(Nls,time_method_bisect,marker='o',color='g',linestyle='-',label='bisect')
    plt.xlabel('list size', fontsize=18)
    plt.ylabel('log(time)', fontsize=18)
    plt.legend(loc = 'upper left')
    plt.show()
173
xslittlegrass
def check_availability(element, collection: iter):
    return element in collection

tilisation

check_availability('a', [1,2,3,4,'a','b','c'])

Je crois que c'est le moyen le plus rapide de savoir si une valeur choisie est dans un tableau.

32
Tiago Moutinho

Vous pouvez placer vos objets dans un set . Les recherches de set sont très efficaces.

Essayer:

s = set(a)
if 7 in s:
  # do stuff

edit Dans un commentaire, vous indiquez que vous souhaitez obtenir l'index de l'élément. Malheureusement, les ensembles n'ont aucune notion de position d'élément. Une alternative consiste à trier votre liste, puis à utiliser recherche binaire à chaque fois que vous devez rechercher un élément.

32
NPE
a = [4,2,3,1,5,6]

index = dict((y,x) for x,y in enumerate(a))
try:
   a_index = index[7]
except KeyError:
   print "Not found"
else:
   print "found"

Ce ne sera une bonne idée que si ne change pas et que nous pouvons donc faire la partie dict () une fois, puis l’utiliser à plusieurs reprises. Si cela change, veuillez fournir plus de détails sur ce que vous faites.

16
Winston Ewert

Il semble que votre application pourrait tirer parti de l’utilisation d’une structure de données Bloom Filter.

En bref, une recherche de filtre de bloom peut vous dire très rapidement si une valeur n'est définitivement PAS présente dans un ensemble. Sinon, vous pouvez effectuer une recherche plus lente pour obtenir l'index d'une valeur qui POURRAIT ÊTRE dans la liste. Ainsi, si votre application tend à obtenir le résultat "non trouvé" beaucoup plus souvent que le résultat "trouvé", vous pouvez voir une accélération en ajoutant un filtre de Bloom.

Pour plus de détails, Wikipedia fournit un bon aperçu du fonctionnement de Bloom Filters, et une recherche sur le Web pour "bibliothèque de filtres Python Bloom" fournira au moins quelques implémentations utiles.

6
matt2000

Sachez que l'opérateur in teste non seulement l'égalité (==) mais également l'identité (is), la logique in pour lists est à peu près équivalent à ce qui suit (il est écrit en C et non pas Python bien que, du moins en CPython):

for element in s:
    if element is target:
        # fast check for identity implies equality
        return True
    if element == target:
        # slower check for actual equality
        return True
return False

Dans la plupart des cas, ces détails ne sont pas pertinents, mais ils peuvent parfois laisser un Python novice surpris, par exemple, numpy.NAN a la propriété inhabituelle d'être ne pas être égal à lui-même :

>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True

Pour distinguer ces cas inhabituels, vous pouvez utiliser any() comme:

>>> lst = [numpy.NAN, 1 , 2]
>>> any(element == numpy.NAN for element in lst)
False
>>> any(element is numpy.NAN for element in lst)
True 

Notez que la logique in pour lists avec any() serait:

any(element is target or element == target for element in lst)

Cependant, je devrais souligner qu’il s’agit d’un cas Edge, et dans la grande majorité des cas, l’opérateur in est hautement optimisé et correspond exactement à ce que vous voulez bien entendu (soit avec un list, soit avec un set).

5
Chris_Rands

Ou utilisez __contains__:

sequence.__contains__(value)

démo:

>>> l=[1,2,3]
>>> l.__contains__(3)
True
>>> 
4
U10-Forward

Ce n'est pas le code, mais l'algorithme pour une recherche très rapide.

Si votre liste et la valeur que vous recherchez sont tous des nombres, c'est assez simple. Si les chaînes: regardez en bas:

  • -Laissez "n" être la longueur de votre liste
  • -Etape optionnelle: si vous avez besoin de l'index de l'élément: ajoutez une deuxième colonne à la liste avec l'index actuel des éléments (0 à n-1) - voir plus tard
  • Commandez votre liste ou une copie de celle-ci (.sort ())
  • En boucle:
    • Comparez votre numéro au n/2ème élément de la liste
      • Si plus grand, boucle à nouveau entre les index n/2-n
      • Si plus petit, boucle à nouveau entre les index 0-n/2
      • Si le même: vous l'avez trouvé
  • Continuez à restreindre la liste jusqu'à ce que vous la trouviez ou n'ayez plus que 2 chiffres (au-dessous et au-dessus de celui que vous recherchez)
  • Ceci trouvera n'importe quel élément dans au plus 19 étapes pour une liste de 1.000.0 (log (2) n pour être précis)

Si vous avez également besoin de la position d'origine de votre numéro, recherchez-la dans la deuxième colonne d'index.

Si votre liste n'est pas composée de nombres, la méthode fonctionne toujours et sera la plus rapide, mais vous devrez peut-être définir une fonction permettant de comparer/ordonner des chaînes.

Bien sûr, cela nécessite l’investissement de la méthode triée (), mais si vous continuez à réutiliser la même liste pour vérification, cela peut en valoir la peine.

2
Adam
present = False
searchItem = 'd'
myList = ['a', 'b', 'c', 'd', 'e']
if searchItem in myList:
   present = True
   print('present = ', present)
else:
   print('present = ', present)
1
Hafizur Rahman

La solution de @Winston Ewert accélère considérablement les très grandes listes, mais cette réponse stackoverflow indique que la construction try:/except:/else: sera ralentie si la branche except est souvent atteinte . Une alternative consiste à tirer parti de la méthode .get() pour le dict:

a = [4,2,3,1,5,6]

index = dict((y, x) for x, y in enumerate(a))

b = index.get(7, None)
if b is not None:
    "Do something with variable b"

La méthode .get(key, default) est juste pour le cas où vous ne pouvez pas garantir qu'une clé sera dans le dict. Si la clé est présente , elle renvoie la valeur (comme le ferait dict[key]), mais si ce n'est pas le cas, .get() renvoie votre valeur par défaut (ici None). Dans ce cas, vous devez vous assurer que la valeur par défaut choisie ne sera pas dans a.

1
user3897315

Pour moi, il était de 0.030 sec (réel), 0.026 sec (utilisateur) et 0.004 sec (sys).

try:
print("Started")
x = ["a", "b", "c", "d", "e", "f"]

i = 0

while i < len(x):
    i += 1
    if x[i] == "e":
        print("Found")
except IndexError:
    pass
0
Tabin1000

Code pour vérifier s'il existe dans le tableau deux éléments dont le produit est égal à k:

n = len(arr1)
for i in arr1:
    if k%i==0:
        print(i)
0
ravi tanwar