给定一个数字列表,找到所有加起来等于给定数值的数字组合。

41

我有一个数字列表,例如:

numbers = [1, 2, 3, 7, 7, 9, 10]

如您所见,此列表中的数字可能会出现多次。

我需要获取所有这些数字的组合,它们的总和为给定值,例如10

组合中的项目不得重复,但必须将numbers中的每个项目视为唯一,这意味着例如列表中的两个7表示具有相同值的不同项目。

顺序不重要,因此[1, 9][9, 1]是相同的组合。

组合的长度没有限制,[10][1, 2, 7]同样有效。

如何创建符合上述条件的所有组合的列表?

在此示例中,它将是[[1,2,7], [1,2,7], [1,9], [3,7], [3,7], [10]]

6个回答

39
你可以使用itertools迭代每种可能大小的组合,并过滤掉所有总和不为10的内容:
import itertools

numbers = [1, 2, 3, 7, 7, 9, 10]
target = 10

result = [seq for i in range(len(numbers), 0, -1)
          for seq in itertools.combinations(numbers, i)
          if sum(seq) == target]

print(result)

结果:

[(1, 2, 7), (1, 2, 7), (1, 9), (3, 7), (3, 7), (10,)]

遗憾的是,这个算法的时间复杂度大约为O(2^N),所以它不适用于超过20个元素的输入列表。


如果您在理解上述代码方面感到困惑,这里有一个简化版本的代码:for i in range(1, len(a)): for s in itertools.combinations(a, i): if sum(s) == sum1: print(s)其中 itertools 是 Python 中一个标准库,需要先导入。 - Abhi
这段代码有更小的时间复杂度版本吗? - The Dodo
@Kevin:如何处理更多元素的情况? - StackGuru
如果numbers包含重复元素,itertools.combinations将会创建重复项。 - Vepir

25

@kgoodrick 提供的 解决方案 很不错,但我认为将其作为生成器更有用:

def subset_sum(numbers, target, partial=[], partial_sum=0):
    if partial_sum == target:
        yield partial
    if partial_sum >= target:
        return
    for i, n in enumerate(numbers):
        remaining = numbers[i + 1:]
        yield from subset_sum(remaining, target, partial + [n], partial_sum + n)

输出:

print(list(subset_sum([1, 2, 3, 7, 7, 9, 10], 10)))
# [[1, 2, 7], [1, 2, 7], [1, 9], [3, 7], [3, 7], [10]]

1
你能解释一下这行代码的含义吗? "yield from subset_sum(remaining, target, partial + [n], partial_sum + n)" - user10508414
@Martin Valgur:它能处理10000个列表元素吗? - StackGuru
如果列表中的元素不都是唯一的,则会创建重复项。 - Vepir

23

这个问题之前已经被问过了,可以看一下@msalvadores的回答这里。我更新了给出的Python代码以在Python 3中运行:

def subset_sum(numbers, target, partial=[]):
    s = sum(partial)

    # check if the partial sum is equals to target
    if s == target:
        print("sum(%s)=%s" % (partial, target))
    if s >= target:
        return  # if we reach the number why bother to continue

    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i + 1:]
        subset_sum(remaining, target, partial + [n])


if __name__ == "__main__":
    subset_sum([3, 3, 9, 8, 4, 5, 7, 10], 15)

    # Outputs:
    # sum([3, 8, 4])=15
    # sum([3, 5, 7])=15
    # sum([8, 7])=15
    # sum([5, 10])=15

3
如果您希望每个输出包含一定数量的数字,比如12个数字,并且总和为15,该怎么办? - max
1
@max,我正在寻找与你类似的解决方案,你有找到什么吗? - qasimalbaqali
1
@qasimalbaqali 我已经完成了。不要介意我的评论,但这个程序是有效的:

Python3程序,用于查找具有给定和的整数列表中的所有对

from itertools import combinations def findPairs(lst, K, N): return [pair for pair in combinations(lst, N) if sum(pair) == K] #每月成本范围;唯一数字 lst = list(range(10, 30)) #每台机器/客户的年收入总和 K = 200 #月数(12-9=免费月数) N = 9print('可能的每月订阅费用仍然相当于每年$200:') #print(findPairs(lst, K, N)) findPairs(lst,K,N)
- max

5

@qasimalbaqali

这可能不完全是帖子所寻求的,但如果您想要:

查找一系列数字范围[lst]的所有组合,每个lst包含N个元素,并且它们总和为K:请使用以下方法:

# Python3 program to find all pairs in a list of integers with given sum  
from itertools import combinations 

def findPairs(lst, K, N): 
    return [pair for pair in combinations(lst, N) if sum(pair) == K] 

#monthly cost range; unique numbers
lst = list(range(10, 30))
#sum of annual revenue per machine/customer
K = 200
#number of months (12 - 9 = num months free)
N = 9

print('Possible monthly subscription costs that still equate to $200 per year:')
#print(findPairs(lst, K, N)) 
findPairs(lst,K,N)

结果:

Possible monthly subscription costs that still equate to $200 per year:
Out[27]:
[(10, 11, 20, 24, 25, 26, 27, 28, 29),
 (10, 11, 21, 23, 25, 26, 27, 28, 29),
 (10, 11, 22, 23, 24, 26, 27, 28, 29),

这个问题的思路是,“如果我们免费提供x个月的服务,仍然能够实现收入目标,那么我们能够每月收取多少费用。”

2

追加:包括零。

import random as rd

def combine(soma, menor, maior):
    """All combinations of 'n' sticks and '3' plus sinals.
    seq = [menor, menor+1, ..., maior]
    menor = min(seq); maior = max(seq)"""
    lista = []

    while len(set(lista)) < 286: # This number is defined by the combination
                                 # of (sum + #summands - 1, #summands - 1) -> choose(13, 3)     
        zero = rd.randint(menor, maior)

        if zero == soma and (zero, 0, 0, 0) not in lista:
            lista.append((zero, 0, 0, 0))

        else:
            # You can add more summands!

            um = rd.randint(0, soma - zero)
            dois = rd.randint(0, soma - zero - um)
            tres = rd.randint(0, soma - zero - um - dois)


            if (zero + um + dois + tres  == soma and
             (zero, um, dois, tres) not in lista):
                lista.append((zero, um, dois, tres))

    return sorted(lista)

>>> result_sum = 10
>>> combine(result_sum, 0, 10)

输出

[(0,0,0,10), (0,0,1,9), (0,0,2,8), (0,0,3,7), ...,
(9,1,0,0), (10,0,0,0)]

2

This works...

from itertools import combinations


def SumTheList(thelist, target):
    arr = []
    p = []    
    if len(thelist) > 0:
        for r in range(0,len(thelist)+1):        
            arr += list(combinations(thelist, r))

        for item in arr:        
            if sum(item) == target:
                p.append(item)

    return p

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接