Python 最快列表索引搜索
在整数列表中查找元素索引的最快方法是什么 现在我在做什么Python 最快列表索引搜索,python,performance,search,Python,Performance,Search,在整数列表中查找元素索引的最快方法是什么 现在我在做什么 if value in mylist: return mylist.index(value) 但我似乎做了两次同样的事情:为了知道value是否在mylist中,我还知道索引位置。我还尝试了其他解决方案: try: return mylist.index(value) except ValueError: return None 或 但所有这些解决方案似乎都比较慢 数组没有排序,只有4个元素。由于只有4个项目,
if value in mylist:
return mylist.index(value)
但我似乎做了两次同样的事情:为了知道value
是否在mylist
中,我还知道索引位置。我还尝试了其他解决方案:
try:
return mylist.index(value)
except ValueError:
return None
或
但所有这些解决方案似乎都比较慢
数组没有排序,只有4个元素。由于只有4个项目,您也可以尝试以下操作:
if value == mylist[0]:
return 0
elif value == mylist[1]:
return 1
elif value == mylist[2]:
return 2
elif value == mylist [3]:
return 3
让我知道它在你的情况下是如何工作的。我很好奇 您可以使用集合来检查成员资格,这将比检查列表更有效,但最大的开销是索引:
In [54]: l = [1,2,3,4]
In [55]: s = set([1,2,3,4])
In [56]: timeit l.index(6) if 6 in s else False
10000000 loops, best of 3: 79.9 ns per loop
In [57]: timeit l.index(6) if 6 in l else False
10000000 loops, best of 3: 141 ns per loop
In [58]: timeit l.index(4) if 4 in l else False
1000000 loops, best of 3: 381 ns per loop
In [59]: timeit l.index(4) if 4 in s else False
1000000 loops, best of 3: 333 ns per loop
仅使用if-elses是快速的,但是如果您总是在同一个列表上搜索(或者您的列表不会经常更改),则可以通过将元素->索引映射存储在dict中,然后执行字典查找来加快搜索速度 因此,您的代码应该如下所示:
# Precompute the mapping.
mapping = { index: value for value, index in enumerate(TEST_LIST) }
# Search function:
def lookup(value):
return mapping.get(value, None)
我运行了一些测试,将其与其他方法进行比较。以下是我的测试代码:
import timeit
TEST_LIST = [100, -2, 10007, 2**70 + 1]
mapping = { index: value for value, index in enumerate(TEST_LIST) }
NUM_TIMES = 10**6
def by_if_else(lst, value):
if lst[0] == value:
return 0
elif lst[1] == value:
return 1
elif lst[2] == value:
return 2
elif lst[3] == value:
return 3
else:
return None
def by_index(lst, value):
for i in xrange(4):
if lst[i] == value:
return i
return None
def by_exception(lst, value):
try:
lst.index(value)
except ValueError:
return None
def by_iter(lst, value):
for index, element in enumerate(lst):
if element == value:
return value
return None
def by_dict(lst, value):
return mapping.get(value, None)
def TimeFunction(function_name, value):
if 'dict' in function_name:
return timeit.timeit(
stmt = '%s(mapping, %d)' % (function_name, value),
setup = 'from __main__ import %s, mapping' % function_name,
number=NUM_TIMES)
else:
return timeit.timeit(
stmt = '%s(TEST_LIST, %d)' % (function_name, value),
setup = 'from __main__ import %s, TEST_LIST' % function_name,
number=NUM_TIMES)
def RunTestsOn(value):
print "Looking for %d in %s" % (value, str(TEST_LIST))
function_names = [name for name in globals() if name.startswith('by_')]
for function_name in function_names:
print "Function: %s\nTime: %f" % (
function_name, TimeFunction(function_name, value))
def main():
values_to_look_for = TEST_LIST + [ -10**70 - 1, 55, 29]
for value in values_to_look_for:
RunTestsOn(value)
if __name__ == '__main__':
main()
当搜索的值很小并且出现在列表中时(我删除了其他函数的运行时),if-else方法看起来更快:
但如果值较大(即比较昂贵),则速度较慢:
或者,当该值根本不在列表中时(即使该值很小):
很明显,使用dict应该更快,因为对于所有其他方法,dict的查询都是O(1),而不是O(n),对于如此小的列表,解释器可能正在为if-else版本创建优化的字节码,并通过哈希表执行指针跟踪的开销抵消了dict的许多速度优势。但在大多数情况下,它似乎仍然略快一些。我建议您在您的数据上测试这种方法,看看哪种方法更适合您。老实说,这在我看来是一种过早的优化,为什么您如此担心4个要素的加速?只需选择最具可读性的语法,然后继续。你是对的,这似乎是一个过早的优化。但事实并非如此。这是我的代码的内部循环,探查器告诉我这是执行次数最多的函数。我提出了一个精确的问题,请只回答它,我没有问你是否认为它有用。一定是因为设置
try…的堆栈的开销,除了…
变量<代码>尝试…除了…在我的测试中,当我在mylist
中有1000个元素时速度更快,但当我有4个元素时速度较慢,正如您报告的那样。但是,优化器必须与此交互:如果我将搜索操作(包括try
块)放入函数中,我只会复制您的问题;如果我将try
块放在我的主循环中,它会再次变快(但遗憾的是,只比mylist变量中的if值稍微快一点)。在try/except情况下,性能完全不同,这取决于是否引发异常。构建异常非常昂贵。在for的例子中,循环的每个步骤都有很多变量查找。对于小列表,我并不惊讶你的第一个选择是最好的。顺便问一下:我的第一个解决方案是做同样的事情两次,对吗?有没有办法避免这种情况?我想你是赢家。您的解决方案比Padraic的解决方案快25%。循环展开可能是最好的解决方案。一个问题的地狱般答案。:)
import timeit
TEST_LIST = [100, -2, 10007, 2**70 + 1]
mapping = { index: value for value, index in enumerate(TEST_LIST) }
NUM_TIMES = 10**6
def by_if_else(lst, value):
if lst[0] == value:
return 0
elif lst[1] == value:
return 1
elif lst[2] == value:
return 2
elif lst[3] == value:
return 3
else:
return None
def by_index(lst, value):
for i in xrange(4):
if lst[i] == value:
return i
return None
def by_exception(lst, value):
try:
lst.index(value)
except ValueError:
return None
def by_iter(lst, value):
for index, element in enumerate(lst):
if element == value:
return value
return None
def by_dict(lst, value):
return mapping.get(value, None)
def TimeFunction(function_name, value):
if 'dict' in function_name:
return timeit.timeit(
stmt = '%s(mapping, %d)' % (function_name, value),
setup = 'from __main__ import %s, mapping' % function_name,
number=NUM_TIMES)
else:
return timeit.timeit(
stmt = '%s(TEST_LIST, %d)' % (function_name, value),
setup = 'from __main__ import %s, TEST_LIST' % function_name,
number=NUM_TIMES)
def RunTestsOn(value):
print "Looking for %d in %s" % (value, str(TEST_LIST))
function_names = [name for name in globals() if name.startswith('by_')]
for function_name in function_names:
print "Function: %s\nTime: %f" % (
function_name, TimeFunction(function_name, value))
def main():
values_to_look_for = TEST_LIST + [ -10**70 - 1, 55, 29]
for value in values_to_look_for:
RunTestsOn(value)
if __name__ == '__main__':
main()
Looking for 10007 in [100, -2, 10007, 1180591620717411303425L]
Function: by_dict
Time: 0.213232
Function: by_if_else
Time: 0.181917
Looking for 1180591620717411303425 in [100, -2, 10007, 1180591620717411303425L]
Function: by_dict
Time: 0.223594
Function: by_if_else
Time: 0.380222
Looking for 29 in [100, -2, 10007, 1180591620717411303425L]
Function: by_dict
Time: 0.195733
Function: by_if_else
Time: 0.267689