2012-01-12 27 views
3

因此,我有一个列表中的数字在Python这样的列表中: [1,89,1221,1919,1920,10210,...]与成千上万的数字,我想检查是否我有一个变种。什么是最有效的方法来检查python中的一组数字的存在

我做这种方式:

if i in mylist: 

但是,这是最快的方法是什么?

一些进一步的规范:

  • 列表中没有保持重复
  • 列表仅持有整数
  • 列表如果提高性能
  • 名单其实可以是一组可订购(所以没有重复)
  • 该列表可以是任何种类的集合
+0

设置/数字列表的大小是否固定? – helpermethod 2012-01-12 09:34:46

+0

不,它可能会有所不同,对固定数量列表的其他见解也是受欢迎的。 – Peter 2012-01-12 09:38:00

+1

转换成一套是一个相对昂贵的操作,但'我在mylist'也是昂贵的。如果你在同一个列表中多次执行此操作,最好使用一个集合 – 2012-01-12 10:00:56

回答

2

转换为集合并做in是最快的方法。

if i in set(mylist): 

一组基本上是一个哈希表,查找是O(1)。

+0

谢谢,如果没有人认为这是一种方式,我 – Peter 2012-01-12 09:40:40

+2

否。如果他只在'if'语句将列表转换为集合,它实际上会比原始代码慢,因为它必须遍历列表并创建一个新的数据结构。 – thesamet 2012-01-12 09:42:50

+3

请注意,如果要重复执行该操作,该方法只会更快。否则,将列表转换为集的开销将大于在O(1)中执行搜索所获得的性能,而不是O(N)。 – 2012-01-12 09:55:38

1

从列表中选择一个集合,相交集合并检查交集的大小?

+0

+1:我喜欢你的想法,'?'尽管不会使其成为接受答案的候选人。 (另外:我无法想象它是最好的方式) – Peter 2012-01-12 09:39:53

+0

我误解了你最初的问题,我想 - 你想检查一个存在的数字,而不是一组数字,在这种情况下,你可能只是使用'(我如果你正在创建集合,直接创建一个集合,在这种情况下'in'将是O(1) – 2012-01-12 09:46:29

4

只有当您在此列表中进行多次查找时,转换为set才值得。如果性能很重要,那么应该从一开始就测量如果使用set(仍然插入元素)的性能比说出列表更好。总之,尝试一些事情和措施。

但是,如果只是由于创建新数据结构的开销,转换为仅用于单个成员资格测试的集合听起来效率低下。

import random 
import timeit 

mylist = list(random.randint(1, 50000) for i in xrange(1000)) 
myset = set(mylist) 

s = "1919 in mylist" 
t = timeit.Timer(s, "from __main__ import mylist") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

s = "1919 in set(mylist)" 
t = timeit.Timer(s, "from __main__ import mylist") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

下面是结果:

1919 in mylist:22.81 usec/pass 
1919 in set(mylist):65.42 usec/pass 
3

一种方法是使用timeit module测试的各种方法。例如,我刮起了下面的代码:

import array 
import bisect 
import random 
import timeit 

mylist = list(random.randint(1, 50000) for i in xrange(1000)) 
myset = set(mylist) 
myarray = array.array('l', mylist) 

s = "1919 in mylist" 
t = timeit.Timer(s, "from __main__ import mylist") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

s = "1919 in myset" 
t = timeit.Timer(s, "from __main__ import myset") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

s = "1919 in myarray" 
t = timeit.Timer(s, "from __main__ import myarray") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

mysortedlist = sorted(mylist) 
mysortedarray = array.array('l', mysortedlist) 

s = "1919 in mysortedlist" 
t = timeit.Timer(s, "from __main__ import mysortedlist") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

s = "1919 in mysortedarray" 
t = timeit.Timer(s, "from __main__ import mysortedarray") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

def bisect_in(a, x): 
    i = bisect.bisect_left(a, x) 
    return (i != len(a) and a[i] == x) 

s = "bisect_in(mysortedlist, 1919)" 
t = timeit.Timer(s, "from __main__ import bisect_in, mysortedlist") 
print s + ":%.2f usec/pass" % (1000000 * t.timeit(number = 100000)/100000) 

,我得到了以下结果:

1919 in mylist:73.89 usec/pass 
1919 in myset:0.29 usec/pass 
1919 in myarray:103.77 usec/pass 
1919 in mysortedlist:75.12 usec/pass 
1919 in mysortedarray:114.21 usec/pass 
bisect_in(mysortedlist, 1919):4.17 usec/pass 

支持他人的论点,使用一组是最快的(假设这个测试下代码使)。

+0

谢谢srgerg,我将在我自己的基准测试中使用它 – Peter 2012-01-12 09:57:45

1

如果您愿意牺牲一些内存效率,您可以构建一个查找表,其中列表索引是您希望检查的值。

最初的名单:

In [106]: %timeit i in myList 
    10000 loops, best of 3: 21.3 us per loop 

构建一个查找表:

In [90]: lookup = [False for i in range(max(myList)+1)] 

    In [91]: for i in myList: 
       lookup[i] = True 

    In [92]: %timeit lookup[i] 
    10000000 loops, best of 3: 50.7 ns per loop 

查找表是这里〜400倍比无序列表更快。

如果您的列表的最大值可接受地较低,并且如果设置查找表的时间显着小于检查变量是否在表格中花费的总时间,则此选项才真正可行。

有趣的是,使用Numpy数组时,查找表方法速度降低了25%。 (但建立查找表要快得多)

编辑:此方法在速度方面也优于“我在set(myList)”中的2倍。

+1

您确定只是对列表进行排序会导致改进吗?我尝试在未排序列表和已排序列表中搜索不存在的元素,并且每个元素花费的时间大致相同。搜索一个现存的值会在排序列表中花费更多或更少的时间,具体取决于原始列表中值的位置。 – srgerg 2012-01-12 13:03:38

+0

你是对的。这是一个错误,分类将会加快进程速度,从而降低进程速度。所有更多的理由使用集合或查找表。我已更改帖子以免误导。 – ebarr 2012-01-12 17:51:14

相关问题