在Python中生成所有可能的长度为N的列表,这些列表总和为S.

时间:2011-10-13 01:29:59

标签: python list recursion sum

我正在尝试生成总和为N的所有可能的长度N列表。我已经编写了一些代码来执行此操作,但是对于任何大的(特别是,我想要N = 5,S = 100),我遇到内存溢出错误。

我正在寻找一个更好的问题解决方案,或者一种改进我的代码的方法,这样我就可以在N = 5,S = 100上运行它。下面这两个程序协同工作,在嵌套列表中创建所有可能的数字组合,然后将它们重新设置为正确的格式。下面再现了一些样本输出。

我知道我的代码不是最好的。我是一名工程师(我知道,我知道),所以编码并不完全是我的专长。感谢您提供的任何帮助。

编辑:我只想澄清一些事情。首先,列表中的零可以,列表可以包含相同数字的倍数,列表中数字的顺序很重要。

def nToSum(N,S):
    ''' Creates a nested list of all possible lists of length N that sum to S'''
    if N <= 1: #base case
        return [S]
    else:
        L = []
        for x in range(S+1):   #create a sub-list for each possible entry of 0 to S 
            L += [[x,nToSum(N-1,S-x)]]  #create a sub-list for this value recursively
        return L

def compress(n=[],L): #designed to take in a list generated by nToSum
    '''takes the input from nToSum as list L, and then flattens it so that each list is a
       top level list.  Leading set n is the "prefix" list, and grows as you climb down the 
       sublists'''
    if type(L[0]) == int:  #base case:  you have exposed a pure integer
        return [n+L]       #take that integer, and prepend the leading set n
    else:
        Q = []
        for x in L:  # look at every sublist
            Q += compress(n+[x[0]],x[1])  # for each sublist, create top level lists recursively
        return Q                          # note:  append x[0] to leading set n

>>> nToSum(3,3)
[[0, [[0, [3]], [1, [2]], [2, [1]], [3, [0]]]], [1, [[0, [2]], [1, [1]], [2, [0]]]], [2, [[0, [1]], [1, [0]]]], [3, [[0, [0]]]]]

>>> compress([],nToSum(3,3))
[[0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0]]

2 个答案:

答案 0 :(得分:9)

使用生成器可以节省内存(如果使用Python 2,请使用xrange而不是range。这就是我提出的。它与nToSum非常相似,不需要compress

def f(length,total_sum):
    if length == 1:
        yield (total_sum,)
    else:
        for value in range(total_sum + 1):
            for permutation in sums(length - 1,total_sum - value):
                yield (value,) + permutation

L = list(f(5,100))
print('total permutations:',len(L))

# First and last 10 of list
for i in L[:10] + L[-10:]:
    print(i)

输出

total permutations: 4598126
(0, 0, 0, 0, 100)
(0, 0, 0, 1, 99)
(0, 0, 0, 2, 98)
(0, 0, 0, 3, 97)
(0, 0, 0, 4, 96)
(0, 0, 0, 5, 95)
(0, 0, 0, 6, 94)
(0, 0, 0, 7, 93)
(0, 0, 0, 8, 92)
(0, 0, 0, 9, 91)
(98, 0, 2, 0, 0)
(98, 1, 0, 0, 1)
(98, 1, 0, 1, 0)
(98, 1, 1, 0, 0)
(98, 2, 0, 0, 0)
(99, 0, 0, 0, 1)
(99, 0, 0, 1, 0)
(99, 0, 1, 0, 0)
(99, 1, 0, 0, 0)
(100, 0, 0, 0, 0)

答案 1 :(得分:0)

我一直在寻找与此问题类似的东西,但是N的每个n的选项中约束的复杂性增加了。这是我的方法:

例如,如果我们正在寻找10 to 50的5个数字的组合,这些数字加起来为100

def permutations_w_constraints(n_perm_elements, sum_total, min_value, max_value):
    # base case
    if n_perm_elements == 1:
        if (sum_total <= max_value) & (sum_total >= min_value):
            yield (sum_total,)
    else:
        for value in range(min_value, max_value + 1):
            for permutation in permutations_w_constraints(
                n_perm_elements - 1, sum_total - value, min_value, max_value
            ):
                yield (value,) + permutation

results = list(permutations_w_constraints(5, 100, 10, 50))

print('total permutations:',len(results))
for i in results[:10] + results[-10:]:
    print(i)
total permutations: 312676
(10, 10, 10, 20, 50)
(10, 10, 10, 21, 49)
(10, 10, 10, 22, 48)
(10, 10, 10, 23, 47)
(10, 10, 10, 24, 46)
(10, 10, 10, 25, 45)
(10, 10, 10, 26, 44)
(10, 10, 10, 27, 43)
(10, 10, 10, 28, 42)
(10, 10, 10, 29, 41)
(50, 18, 10, 10, 12)
(50, 18, 10, 11, 11)
(50, 18, 10, 12, 10)
(50, 18, 11, 10, 11)
(50, 18, 11, 11, 10)
(50, 18, 12, 10, 10)
(50, 19, 10, 10, 11)
(50, 19, 10, 11, 10)
(50, 19, 11, 10, 10)
(50, 20, 10, 10, 10)