【问题标题】:Generate all possible combinations from a int list under a limit从限制下的 int 列表生成所有可能的组合
【发布时间】:2012-08-03 09:10:19
【问题描述】:

我需要在 Python 中执行此操作。 有一个给定的列表 l,可能包含超过 5000 个整数元素。 数字总和有限制,20000 或可能很高。 输出应该是从列表中选择的 2 个数字的所有可能总和, 喜欢,

l=[1,2,3,4,5,6,7,8,9]
output 
1+1,1+2,1+3,1+4,1+5,1+6...........
2+2,2+3,2+4.......
.........
.......

2,3,4,5,6... like that

我正在使用这个代码,现在做这个, 但是很慢

l=listgen()
p=[]
for i in range(0,len(l)):
    for j in range(i,len(l)):
        k=l[i]+l[j]
        if k not in p:
            p.append(k)
p.sort
print(p)

listgen() 是生成输入列表的函数。

【问题讨论】:

  • 限制是什么意思?总和或输入列表长度的限制?
  • 总和的限制。对不起,我没有提到
  • @Madushan 您现在应该将其添加到问题中:) 也是所有元素或每对元素的总和?
  • 已添加:D 每对,我认为在代码上可以清楚地看到。无论如何,thanx

标签: python list optimization combinations


【解决方案1】:

一些老式的优化可能会为您提供比具有多个 for 循环的列表推导更容易理解的更快代码:

def sums(lst, limit):    # prevent global lookups by using a function
    res = set()          # set membership testing is much faster than lists
    res_add = res.add    # cache add method
    for i, first in enumerate(lst):   # get index and item at the same time
        for second in lst[i:]:        # one copy operation saves n index ops.
            res_add(first + second)   # prevent creation/lookup of extra local temporary
    return sorted([x for x in res if x < limit])

print sums(listgen(), 20000)

作为额外的奖励,此版本将与 psyco、cython 等完美优化。

更新: 将此与其他建议(将 listgen 替换为 range(5000) 进行比较时,我得到:

mine:        1.30 secs
WolframH:    2.65 secs
lazyr:       1.54 secs (estimate based on OPs timings -- I don't have Python 2.7 handy)

【讨论】:

  • @Madushan 我也试过运行你的代码,但是时间太长了,我不得不终止进程:-(
  • 使用 psyco 我的时间降至 0.7 秒 :-)
  • 在我的 Python 3.2 系统上是 4.30 和 9.10。无论如何,列表最初有 6965 个元素,限制是 28132。我从没想过使用 set() 会加快我的速度。我有很多东西要学!!。
  • +1 优化技术的精彩概述!我不确定lst[i:] 的副本,你有没有将它与直接索引到lst 进行比较?
  • @lazyr 在内部循环中使用列表索引将时间提高到 1.58 秒——请记住,索引会做很多工作(类似于 lst[j] =&gt; lst.__getitem__(j) =&gt; (getattr(lst, '__getitem__')(j))。这不是 C :-) 复制列表,otoh,是在 CPython 领域的深处执行的,希望通过调用 memcpy 或类似的方法。
【解决方案2】:

编辑: Thebjorn 说他有最有效的解决方案,我自己的测试也同意,尽管我的表现有所提高。他的代码也较少依赖于 python 版本,并且似乎经过深思熟虑并在优化方面进行了解释。你应该接受他的回答(并给他点赞)。

使用itertools.combinations_with_replacement(在python 2.7中添加),并将p设为set。

def sums(lst, limit):
    from itertools import combinations_with_replacement
    p = set(x + y for x, y in combinations_with_replacement(listgen(), 2))
    return sorted([x for x in p if x < limit])

由于这一行,您的代码很慢:

if k not in p: # O(N) lookup time in lists vs average case O(1) in sets

如果您只是对代码进行一些小的更改,使 p 成为 set,那将产生巨大的影响:

L = listgen()
p = set()
for i in range(0, len(L)):
    for j in range(i, len(L)):
        p.add(L[i] + L[j])
print(sorted(p))

顺便说一下,你的例子中的这一行

p.sort

没有效果。您必须调用一个方法来实际执行它,如下所示:

p.sort()

【讨论】:

  • 我应该如何使用限制?我不想要超过它的数字。
  • l 是一个错误的变量名,因为它可能与 1 混淆。
【解决方案3】:

编辑:包括限制(不在 OP 代码中)。

a = set(x + y for x in l for y in l)
print(sorted(x for x in a if x < limit))

这也降低了算法的复杂性(由于列表中的成员资格测试,您的算法可能是 O(n^4))。

【讨论】:

  • 我认为这会比我的需要更多时间。
  • @Madushan:是什么让你这么认为?在我的测试中,它比你的快 50 倍左右。
  • 可能是 set() 但你也在做我正在做的事,不是吗?(​​遍历整个列表)*list elimants。
  • @Madushan:你的代码做了一些事情,而我的代码没有:创建范围对象,通过索引查找项目,查找很多名称,每次查找属性p.append,调用looked up 方法,列表中的成员资格测试(慢)。
【解决方案4】:

如果输入列表是排序的,当达到限制时可以跳出内循环。另外,将p 设置为一组。

lst=listgen()
lst.sort()
p=set()
for i in range(0,len(lst)):
    for j in range(i,len(lst)):
        k=lst[i]+lst[j]
        if k > limit:
            break
        p.add(k)
p = sorted(p)
print(p)

【讨论】:

  • 输入列表已排序。我已经进行了您所说的修改。无论如何它太慢了。
【解决方案5】:

您可以为此使用“NumPy”。 这无疑为您提供了所需的性能:

import numpy as np

data = np.arange(5000)
limit = 20000
result = np.zeros(0,dtype='i4')
for i in data:
    result = np.concatenate((result,data[i]+data[i:]))
    if len(result) >= limit: break
result = result[:limit]

编辑: 我刚刚意识到限制是总和而不是元素的数量。那么代码应该是:

EDIT2: 发现更多的逻辑错误。我更正的建议是:

for idx, x in np.ndenumerate(data):
    result = np.concatenate((result,x+data[idx[0]:]))
    if x + data[-1] >= limit: break
result = result[result <= limit]

【讨论】:

  • 我还没有使用 NumPy 做任何事情。也许是时候开始了。谢谢
  • 最后一行似乎是语法错误(对不起,不知道 numpy 是否正确,或者您是否错误地解释了限制 - 请参阅其他答案。 ..?)
  • @thebjorn:当然 - 你是对的 - 我混淆了括号。现在更正了。谢谢!
  • @thebjorn:啊——现在我明白你的意思了。限制是总和,而不是元素的数量......
【解决方案6】:

如果列表可以包含重复的元素,那么首先删除它们可能是一个明智的主意,例如通过将列表转换为集合。

【讨论】:

  • 输入中没有重复项。我优化了其他代码以确保它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-07
相关资源
最近更新 更多