Python 检查列表中是否存在值的最快方法

Python 检查列表中是否存在值的最快方法,python,performance,list,Python,Performance,List,知道列表中是否存在值(包含数百万个值的列表)及其索引是什么的最快方法是什么 我知道列表中的所有值都是唯一的,如本例所示 我尝试的第一种方法是(在我的实际代码中是3.8秒): a = [4,2,3,1,5,6] if a.count(7) == 1: b=a.index(7) "Do something with variable b" a = [4,2,3,1,5,6] try: b=a.index(7) except ValueError:

知道列表中是否存在值(包含数百万个值的列表)及其索引是什么的最快方法是什么

我知道列表中的所有值都是唯一的,如本例所示

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

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

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"
a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"
a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)
>>> l = [1, 2, 3]
>>> l.__contains__(3)
True
>>> 
我尝试的第二种方法是(比实际代码快2倍:1.9秒):

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

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"
a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"
a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)
>>> l = [1, 2, 3]
>>> l.__contains__(3)
True
>>> 
堆栈溢出用户建议的方法(我的真实代码为2.74秒):

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

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"
a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"
a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)
>>> l = [1, 2, 3]
>>> l.__contains__(3)
True
>>> 
在我的实际代码中,第一个方法需要3.81秒,第二个方法需要1.88秒。 这是一个很好的改进,但是:

我是Python/脚本的初学者,有没有更快的方法来做同样的事情并节省更多的处理时间

关于我的应用程序的更具体解释:

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

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"
a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"
a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)
>>> l = [1, 2, 3]
>>> l.__contains__(3)
True
>>> 
在Blender API中,我可以访问颗粒列表:

particles = [1, 2, 3, 4, etc.]
从那里,我可以访问粒子的位置:

particles[x].location = [x,y,z]
对于每个粒子,我通过如下方式搜索每个粒子位置来测试是否存在邻居:

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])
最清晰、最快捷的方法


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

您可以将项目放入一个文件夹中。设置查找非常有效

尝试:

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

def check_availability(element, collection: iter):
    return element in collection
用法

check_availability('a', [1,2,3,4,'a','b','c'])
import random
import bisect
import matplotlib.pyplot as plt
import math
import timeit
import itertools

def wrapper(func, *args, **kwargs):
    " Use to produced 0 argument function for call it"
    # Reference https://www.pythoncentral.io/time-a-python-function/
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

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

def method_try(a,b,c):
    for i, x in enumerate(a):
        try:
            c[i] = b.index(x)
        except ValueError:
            c[i] = -1

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

def method_bisect(a,b,c):
    " Finds indexes using bisection "

    # Create a sorted b with its index
    bsorted = sorted([(x, i) for i, x in enumerate(b)], key = lambda t: t[0])

    for i,x in enumerate(a):
        index = bisect.bisect_left(bsorted,(x, ))
        c[i] = -1
        if index < len(a):
            if x == bsorted[index][0]:
                c[i] = bsorted[index][1]  # index in the b array

    return c

def method_reverse_lookup(a, b, c):
    reverse_lookup = {x:i for i, x in enumerate(b)}
    for i, x in enumerate(a):
        c[i] = reverse_lookup.get(x, -1)
    return c

def profile():
    Nls = [x for x in range(1000,20000,1000)]
    number_iterations = 10
    methods = [method_in, method_try, method_set_in, method_bisect, method_reverse_lookup]
    time_methods = [[] for _ in range(len(methods))]

    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)]

        for i, func in enumerate(methods):
            wrapped = wrapper(func, a, b, c)
            time_methods[i].append(math.log(timeit.timeit(wrapped, number=number_iterations)))

    markers = itertools.cycle(('o', '+', '.', '>', '2'))
    colors = itertools.cycle(('r', 'b', 'g', 'y', 'c'))
    labels = itertools.cycle(('in', 'try', 'set', 'bisect', 'reverse'))

    for i in range(len(time_methods)):
        plt.plot(Nls,time_methods[i],marker = next(markers),color=next(colors),linestyle='-',label=next(labels))

    plt.xlabel('list size', fontsize=18)
    plt.ylabel('log(time)', fontsize=18)
    plt.legend(loc = 'upper left')
    plt.show()

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

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()部分,然后重复使用它。如果发生变化,请提供您正在做的更多详细信息。

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

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

  • -让“n”作为列表的长度
  • -可选步骤:如果需要元素的索引:将第二列添加到包含当前元素索引(0到n-1)的列表中-请参阅下文
  • 为列表或其副本排序(.sort())
  • 循环通过:
    • 将您的号码与列表的第n/2个元素进行比较
      • 如果较大,则在索引n/2-n之间再次循环
      • 如果较小,则在索引0-n/2之间再次循环
      • 如果相同:你找到了
  • 不断缩小列表范围,直到您找到它或只有2个数字(位于您要查找的数字的下方和上方)
  • 对于1.000.000的列表,这将在中找到最多19个步骤中的任何元素(精确地说是log(2)n)
如果您还需要号码的原始位置,请在第二个索引列中查找

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


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

听起来您的应用程序可能会从使用Bloom Filter数据结构中获得优势

简而言之,bloom筛选器查找可以非常快速地告诉您值是否绝对不存在于集合中。否则,您可以执行较慢的查找,以获取可能在列表中的值的索引。因此,如果您的应用程序倾向于获得“未找到”结果的频率比获得“已找到”结果的频率高得多,那么通过添加Bloom过滤器,您可能会看到速度有所加快


有关详细信息,Wikipedia提供了Bloom过滤器工作原理的良好概述,对“python Bloom filter library”的web搜索将提供至少两个有用的实现。

如其他人所述,中的
对于大型列表可能非常慢。下面是对
中的
对分
性能的一些比较。请注意,时间(以秒为单位)以对数刻度表示

测试代码:

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 = []

    # adjust range down if runtime is to great or up if there are to many zero entries in any of the time_method lists
    Nls = [x for x in range(10000, 30000, 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(method_in(a, b, c))
        time_method_set_in.append(method_set_in(a, b, c))
        time_method_bisect.append(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.yscale('log')
    plt.show()


profile()
随机导入
进口对分
将matplotlib.pyplot作为plt导入
输入数学
导入时间
(a、b、c)中的def方法_:
开始时间=time.time()
对于枚举(a)中的i,x:
如果b中的x:
c[i]=1
return time.time()-开始时间
(a、b、c)中的定义方法设置:
开始时间=time.time()
s=设置(b)
对于枚举(a)中的i,x:
如果x在s中:
c[i]=1
return time.time()-开始时间
定义方法对分(a、b、c):
开始时间=time.time()
b、 排序()
对于枚举(a)中的i,x:
索引=对分。左对分(b,x)
如果指数