最快列表索引搜索

时间:2014-08-24 20:46:53

标签: python performance search

在整数列表中查找元素索引的最快方法是什么?

现在我在做

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个元素。

3 个答案:

答案 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的许多速度优势。但大多数时候它似乎仍然略快。我建议你在数据上测试这种方法,看看哪种方法更适合你。