在整数列表中查找元素索引的最快方法是什么?
现在我在做
if value in mylist:
return mylist.index(value)
但似乎我做了两次相同的事情:要知道value
是否在mylist
我也知道索引位置。我还尝试了其他解决方案:
try:
return mylist.index(value)
except ValueError:
return None
或
for i, x in enumerate(mylist):
if x == value:
return i
return None
但所有这些解决方案似乎都比较慢。
数组未排序,只有4个元素。
答案 0 :(得分:15)
由于您只有四件物品,您也可以尝试:
if value == mylist[0]:
return 0
elif value == mylist[1]:
return 1
elif value == mylist[2]:
return 2
elif value == mylist [3]:
return 3
让我知道它在你的情况下是如何工作的。我好奇。 :)
答案 1 :(得分:1)
您可以使用一个集来检查成员资格,它比检查列表更有效,但最大的开销是索引:
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
答案 2 :(得分:1)
只使用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方法看起来更快(我删除了其他函数的运行时):
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
虽然很明显使用dict应该更快,因为它的查询是O(1)而不是所有其他方法的O(n),对于这么小的列表,解释器可能正在创建优化的字节码对于if-else版本和通过散列表进行指针追踪的开销抵消了dict的许多速度优势。但大多数时候它似乎仍然略快。我建议你在数据上测试这种方法,看看哪种方法更适合你。