检查一个值是否存在于一个非常大的列表的最快方法是什么?
当前回答
空间数据的边缘情况
可能有更快的算法来处理空间数据(例如重构以使用k-d树),但检查向量是否在数组中的特殊情况是有用的:
如果你有空间数据(即笛卡尔坐标) 如果你有整数掩码(即数组过滤)
在这种情况下,我想知道由两点定义的(无向)边是否在(无向)边的集合中,这样
(pair in unique_pairs) | (pair[::-1] in unique_pairs) for pair in pairs
其中pair构成两个任意长度的向量(即形状(2,N))。
如果这些向量之间的距离是有意义的,那么检验可以用一个浮点不等式来表示
test_result = Norm(v1 - v2) < Tol
和“值存在于列表”是简单的任何(test_result)。
下面是整数对和R3向量对的示例代码和虚拟测试集生成器。
# 3rd party
import numpy as np
import numpy.linalg as LA
import matplotlib.pyplot as plt
# optional
try:
from tqdm import tqdm
except ModuleNotFoundError:
def tqdm(X, *args, **kwargs):
return X
print('tqdm not found. tqdm is a handy progress bar module.')
def get_float_r3_pairs(size):
""" generate dummy vector pairs in R3 (i.e. case of spatial data) """
coordinates = np.random.random(size=(size, 3))
pairs = []
for b in coordinates:
for a in coordinates:
pairs.append((a,b))
pairs = np.asarray(pairs)
return pairs
def get_int_pairs(size):
""" generate dummy integer pairs (i.e. case of array masking) """
coordinates = np.random.randint(0, size, size)
pairs = []
for b in coordinates:
for a in coordinates:
pairs.append((a,b))
pairs = np.asarray(pairs)
return pairs
def float_tol_pair_in_pairs(pair:np.ndarray, pairs:np.ndarray) -> np.ndarray:
"""
True if abs(a0 - b0) <= tol & abs(a1 - b1) <= tol for (ai1, aj2), (bi1, bj2)
in [(a01, a02), ... (aik, ajl)]
NB this is expected to be called in iteration so no sanitization is performed.
Parameters
----------
pair : np.ndarray
pair of vectors with shape (2, M)
pairs : np.ndarray
collection of vector pairs with shape (N, 2, M)
Returns
-------
np.ndarray
(pair in pairs) | (pair[::-1] in pairs).
"""
m1 = np.sum( abs(LA.norm(pairs - pair, axis=2)) <= (1e-03, 1e-03), axis=1 ) == 2
m2 = np.sum( abs(LA.norm(pairs - pair[::-1], axis=2)) <= (1e-03, 1e-03), axis=1 ) == 2
return m1 | m2
def get_unique_pairs(pairs:np.ndarray) -> np.ndarray:
"""
apply float_tol_pair_in_pairs for pair in pairs
Parameters
----------
pairs : np.ndarray
collection of vector pairs with shape (N, 2, M)
Returns
-------
np.ndarray
pair if not ((pair in rv) | (pair[::-1] in rv)) for pair in pairs
"""
pairs = np.asarray(pairs).reshape((len(pairs), 2, -1))
rv = [pairs[0]]
for pair in tqdm(pairs[1:], desc='finding unique pairs...'):
if not any(float_tol_pair_in_pairs(pair, rv)):
rv.append(pair)
return np.array(rv)
其他回答
正如其他人所说,对于大型列表,in可能非常慢。这里比较了in, set和bisect的性能。注意时间(秒)是对数尺度。
测试代码:
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 too long or up if there are too 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()
如果您只想检查列表中是否存在一个元素,
7 in list_data
是最快的解决方案。请注意
7 in set_data
是一个近乎自由的操作,与集合的大小无关!从一个大列表中创建一个set要比在列表中慢300到400倍,所以如果您需要检查许多元素,首先创建一个set会更快。
用perfplot创建的Plot:
import perfplot
import numpy as np
def setup(n):
data = np.arange(n)
np.random.shuffle(data)
return data, set(data)
def list_in(data):
return 7 in data[0]
def create_set_from_list(data):
return set(data[0])
def set_in(data):
return 7 in data[1]
b = perfplot.bench(
setup=setup,
kernels=[list_in, set_in, create_set_from_list],
n_range=[2 ** k for k in range(24)],
xlabel="len(data)",
equality_check=None,
)
b.save("out.png")
b.show()
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确实发生了变化,请提供更多关于您正在做什么的细节。
这不是代码,而是快速搜索的算法。
如果您的列表和您正在寻找的值都是数字,这是相当简单的。如果字符串:看底部:
-Let "n" be the length of your list -Optional step: if you need the index of the element: add a second column to the list with current index of elements (0 to n-1) - see later Order your list or a copy of it (.sort()) Loop through: Compare your number to the n/2th element of the list If larger, loop again between indexes n/2-n If smaller, loop again between indexes 0-n/2 If the same: you found it Keep narrowing the list until you have found it or only have 2 numbers (below and above the one you are looking for) This will find any element in at most 19 steps for a list of 1.000.000 (log(2)n to be precise)
如果您还需要您的数字的原始位置,请在第二索引列中查找。
如果您的列表不是由数字组成的,该方法仍然有效,并且将是最快的,但您可能需要定义一个可以比较/排序字符串的函数。
当然,这需要使用sorted()方法,但如果您不断重用相同的列表进行检查,那么这样做可能是值得的。
请注意,in操作符不仅测试相等性(==),还测试恒等式(is),列表的in逻辑大致相当于以下内容(实际上是用C而不是Python编写的,至少在CPython中是这样):
对于s中的元素: 如果元素是目标: #快速检查身份意味着相等 还真 如果element == target: #慢速检查实际相等 还真 返回假
在大多数情况下,这个细节是无关紧要的,但在某些情况下,它可能会让Python新手感到惊讶,例如numpy。NAN具有不等于自身的不寻常性质:
>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True
为了区分这些不寻常的情况,你可以使用任何(),比如:
>>> 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()的列表的in逻辑将是:
any(element is target or element == target for element in lst)
然而,我应该强调这是一个边缘情况,对于绝大多数情况,in操作符是高度优化的,当然正是你想要的(无论是对列表还是对集合)。
推荐文章
- 有办法在Python中使用PhantomJS吗?
- 如何在Python中将if/else压缩成一行?
- 如何在Python 3中使用pip。Python 2.x
- 如何让IntelliJ识别常见的Python模块?
- Django:“projects”vs“apps”
- 如何列出导入的模块?
- 转换Python程序到C/ c++代码?
- 如何从gmtime()的时间+日期输出中获得自epoch以来的秒数?
- 在python模块文档字符串中放入什么?
- 我如何在Django中过滤一个DateTimeField的日期?
- 在Python中用索引迭代列表
- -e,——editable选项在pip install中什么时候有用?
- 使用pip命令从requirements.txt升级python包
- Django更改默认的runserver端口
- 输入对象的datetime。Datetime没有Datetime属性