【问题标题】:What's the most efficient way to search a list millions of times?搜索列表数百万次的最有效方法是什么?
【发布时间】:2015-04-02 00:08:40
【问题描述】:

我知道搜索的简单方法是创建一个包含字符串的列表,然后执行if string in list,但它会变慢,而且我听说字典键实际上不会因为大集合而减速它们没有被订购。

但是,我不需要任何与项目相关的额外信息,所以制作字典只是为了保存键并将值设置为 None 感觉有点不对。

有什么我可以使用的东西在速度方面像字典键一样,但又像一个列表?

这是一个简单的例子:

import time, random

totalRange = 100000
searchFor = 5000

#Create a list of 10 million characters
searchableList = []
for i in range( totalRange ):
    searchableList.append( random.randint( 0, totalRange ) )

#Create dictonary with keys set to 'None'
searchableDict = {}
for i in searchableList:
    searchableDict[i] = None

searchableSet = set( searchableList )

#Search list
startTime = time.time()
numberMatches = 0
for number in range( searchFor ):
    if number in searchableList:
        numberMatches += 1
print numberMatches, time.time()-startTime

#Search dictionary keys
startTime = time.time()
numberMatches = 0
for number in range( searchFor ):
    if number in searchableDict:
        numberMatches += 1
print numberMatches, time.time()-startTime

#Search set
startTime = time.time()
numberMatches = 0
for number in range( searchFor ):
    if number in searchableSet:
        numberMatches += 1
print numberMatches, time.time()-startTime

以下是时间输出:

List: 18.8 seconds
Set: 0.002 seconds
Dictionary: 0.0009 seconds

尽管 set 比 list 快很多,但字典仍然快两倍,所以我想知道是否还有什么我不知道的。使用字典不会太糟糕,我只是想有一种比dictionary[key]=None 更清洁的方法。



根据 iCodez 的回答进行编辑:

totalRange=1000000searchFor=50000(高 10 倍)时进行测试:

List = 20 minutes and still going
Dictionary = 0.023 seconds
Set = 0.02 seconds
Set.intersection = 0.008 seconds

随着计算次数的增加,似乎集合和字典的效率非常相似,但set.intersetion 的方式显然要好得多。

【问题讨论】:

  • 最干净、最清晰、最明显的方法是使用集合。不幸的是,您当前的实现似乎对此有轻微的惩罚,但看起来并没有那么担心。如果你能容忍一点模糊,布隆过滤器可能是一个不错的选择。
  • 使用一套。您的时差可以忽略不计,它在概念上是正确的数据结构。
  • .002 vs .0009 太小了,无法真正说出哪个更快。这完全在您正在使用的计时器的限制范围内。
  • 你应该在这样的小时候使用 timeit 模块...
  • FWIW 我刚刚做的一些 timeit 测试有 sets 稍微快一些。但是,如果 Joran 对 set.intersection 的建议还没有快很多(以内存换取速度),我会感到非常惊讶。

标签: python


【解决方案1】:

在这种情况下,您应该使用set。集合与字典 (constant) 具有相同的查找时间,但它们由单个项目而不是键/值对组成。因此,您可以以更少的内存和更好的数据表示获得相同的速度。


此外,您还可以通过使用set.intersection 而不是 for 循环来提高效率:

numberMatches = len(searchableSet.intersection(xrange(searchFor)))

您也会注意到我将range 替换为xrange。这可以防止 Python 构建不必要的列表,从而浪费内存。

【讨论】:

  • 它们具有相同的渐近查找时间,是的,但是 OP 的时间表明,由于某种原因,dict 查找对他的数据更快。
  • @senshin a set 几乎是一个dict,其值设置为None
  • 因为他使用set.intersection 计算交叉口的数量可能会更快
  • @JoranBeasley - 好主意。我会添加它。
  • @Peter - set.intersection 是用 C 编写的,所以 Python 所要做的就是查找名称,然后由 C 完成其余的工作。但是,您的代码让 Python 完成了大部分工作,这几乎总是会变慢。另外,range 正在构建一个相当大的列表,这很浪费时间。
【解决方案2】:

使用

a_dict = dict.fromkeys(my_text.split())

【讨论】:

  • 谢谢,这比我的方法要简洁得多,但它仍然存在作为字典的问题,所有键的值都为None,我的意思是完全避免这种情况并且有一个列表在速度方面就像一本字典:)
  • 我不同意 set 是适当的数据结构...这只是回答有关d[key] = None的更好方法的问题
  • 我不知道你可以这样做,所以它仍然非常有用啊哈,我倾向于使用循环来做所有事情,所以很高兴看到更好的做事方式:)
猜你喜欢
  • 1970-01-01
  • 2013-11-11
  • 1970-01-01
  • 1970-01-01
  • 2012-08-11
  • 1970-01-01
  • 2015-08-14
  • 2012-03-24
  • 1970-01-01
相关资源
最近更新 更多