从限制下的int列表生成所有可能的组合

时间:2012-08-03 09:10:19

标签: python list optimization combinations

我需要在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()是生成输入列表的函数。

6 个答案:

答案 0 :(得分:10)

一些老式的优化可能会让你获得比使用多个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等进行精美优化。

<强>更新 将此与其他建议进行比较(将rangegen替换为范围(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)

答案 1 :(得分:3)

编辑: 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

如果您只是对代码进行了一些小的更改,以便pset,则会产生巨大的差异:

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()

答案 2 :(得分:2)

编辑:包含限制(不在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)。

答案 3 :(得分:1)

如果输入列表已排序,则可以在达到限制时跳出内循环。另外,将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)

答案 4 :(得分:1)

你可以使用“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]

答案 5 :(得分:0)

如果列表可以包含重复的元素,那么首先摆脱它们可能是明智的想法,例如通过将列表转换为集合。