关于python:检查列表list中是否存在值的最快方法

Fastest way to check if a value exist in a list

我正在寻找最快的方法来知道一个值是否存在于一个列表中(一个包含数百万值的列表),以及它的索引是什么?我知道列表中的所有值都是唯一的,就像我的示例一样。

我尝试的第一种方法是(实际代码中的3.8秒):

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

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

我尝试的第二种方法是(比实际代码快2倍:1.9秒):

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

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

StackOverflow用户提出的方法(实际代码上为2.74秒):

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

在我的实际代码中,第一个方法需要3.81秒,第二个方法需要1.88秒。这是一个很好的改进,但是:

我是Python/脚本的初学者,我想知道是否存在一种最快的方法来做同样的事情并节省更多的处理时间?

对我的申请有更具体的说明:

在搅拌机的API中,A可以访问颗粒列表:

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

从那里,我可以访问它的位置:

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

我通过搜索来测试每一个粒子是否存在邻居。在每个粒子的位置,如:

1
2
3
4
if [x+1,y,z] in particles.location
   "find the identity of this neighbour particles in x:the index
    of the particles array"

    particles.index([x+1,y,z])


1
7 in a

最清晰、最快速的方法。

您也可以考虑使用set,但是从您的列表中构造该集合可能需要比更快的成员资格测试节省更多的时间。唯一能确定的方法就是做好基准。(这还取决于您需要什么操作)


正如其他人所说,对于大的列表,in可能非常慢。这里比较了insetbisect的性能。注意时间(以秒为单位)以对数刻度表示。

enter image description here

测试规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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()


1
2
def check_availability(element, collection: iter):
    return element in collection

用法

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

我相信这是知道所选值是否在数组中的最快方法。


你可以把你的东西放进一个set里。设置查找非常有效。

尝试:

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

在注释中编辑您说要获取元素的索引。不幸的是,集合没有元素位置的概念。另一种方法是预先对列表排序,然后在每次需要查找元素时使用二进制搜索。


1
2
3
4
5
6
7
8
9
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"

只有在a不变的情况下,这才是一个好主意,因此我们可以执行dict()部分一次,然后重复使用它。如果a确实发生了变化,请提供有关您正在做什么的更多详细信息。


听起来您的应用程序可能从使用Bloom过滤器数据结构中获得优势。

简而言之,一个bloom过滤器查找可以很快地告诉您一个值是否在集合中不存在。否则,可以进行较慢的查找,以获取可能在列表中的值的索引。因此,如果您的应用程序倾向于比"找到"结果更频繁地得到"未找到"结果,那么您可能会看到通过添加bloom过滤器来加快速度。

有关详细信息,维基百科提供了Bloom过滤器如何工作的一个很好的概述,而"PythonBloom过滤器库"的Web搜索将提供至少两个有用的实现。


请注意,in操作符不仅测试等式(==而且测试同一性(is),listin逻辑大致相当于以下内容(实际上它是用C而不是用python编写的,至少在c python中是这样的):

1
2
3
4
5
6
7
8
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

在大多数情况下,这个细节是不相关的,但在某些情况下,它可能会让一个新手感到惊讶,例如,numpy.NAN具有不等于自身的异常属性:

1
2
3
4
5
6
7
>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True

为了区分这些异常情况,您可以使用any(),例如:

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

注:带any()lists的in逻辑为:

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

但是,我要强调的是,这是一个边缘情况,对于绝大多数情况,in运算符都是高度优化的,并且完全符合您的需要(使用listset)。


使用__contains__

ZZU1

Demo:

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

这不是代码,而是快速搜索的算法

如果您的列表和要查找的值都是数字,那么这非常简单,如果字符串:请查看底部:

  • -让"n"作为列表的长度
  • -可选步骤:如果需要元素的索引:使用元素的当前索引(0到N-1)向列表中添加第二列-请参阅后面的内容
  • 为列表或其副本排序(.sort())
  • 循环通过:
    • 将数字与列表中的n/2th元素进行比较
      • 如果较大,则在索引n/2-n之间再次循环
      • 如果较小,则在索引0-n/2之间再次循环
      • 如果相同:你找到了
  • 不断缩小列表范围,直到找到它或只有2个数字(在您要查找的数字下方和上方)
  • 对于1.000.000(对数(2)n为精确值)的列表,这将在最多19个步骤中找到任何元素。

如果还需要数字的原始位置,请在第二个索引列中查找它。

如果您的列表不是由数字组成的,那么该方法仍然有效,并且速度最快,但是您可能需要定义一个可以比较/排序字符串的函数。

当然,这需要sorted()方法的投资,但是如果您继续重用同一个列表进行检查,它可能是值得的。


对我来说是0.030s(真实),0.026s(用户)和0.004s(系统)。

1
2
3
4
5
6
7
8
9
10
11
12
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

代码检查两个元素在阵列中的存在,其中两个元素的产品等于K。

1
2
3
4
    n=len(arr1)
    for i in arr1:
        if k%i==0 :
            print(i)


1
2
3
4
5
6
7
8
present = False
searchItem = 'd'
myList = ['a', 'b', 'c', 'd', 'e']
if searchItem in myList:
   present = True
   print('present = ', present)
else:
   print('present = ', present)