【问题标题】:Finding Combinations to the provided Sum value查找提供的 Sum 值的组合
【发布时间】:2013-11-25 12:49:12
【问题描述】:

我有一系列这样的数字

myvar = [57, 71, 87, 97, 99, 101, 103, 113, 114, 115, 128, 129, 131, 137, 147, 156, 163, 186]

现在我想计算总和等于给定数字m 的所有此类可能的组合(长度为 1 到 20)。

我尝试使用以下代码解决:

def sum_count(m):    ## Where m is the sum required

    from itertools import combinations

    myseq = []
    for i in range(1,len(myvar)):
        mycomb = list(combinations(mass,i));  # Getting combinations of length i
        mycomb = [list(j) for j in mycomb];
        for j in range(len(mycomb)-1,-1,-1):
            if sum(mycomb[j]) == m:
                myseq.append(mycomb[j])

    return(myseq)

当我输入m = 270(例如)时,它给了我:

[[114, 156], [57, 99, 114]]

但是从myvar 中可以明显看出,还有其他组合的总和等于 270。我在哪里无法理解。

【问题讨论】:

  • 它的效率很低。您应该使用 Subset sum - DP 解决方案。
  • 好吧,我不太了解动态编程......上面的详细解释或修改版本将帮助我
  • 总和为 270 的其他组合是什么?
  • 而完整的sum_count函数体可以简化为return [j for i in range(1, len(mass) + 1) for j in combinations(mass, i) if sum(j) == m]
  • 顺便问一下,谁反对这样的问题??这些人认为每个人都是程序员专家吗???

标签: python function combinations


【解决方案1】:

TL;DR:

讨论不同的方法,这里列出了最好的方法以方便访问,最初由thefourtheye编写:

def subsets_with_sum(lst, target, with_replacement=False):
    x = 0 if with_replacement else 1
    def _a(idx, l, r, t):
        if t == sum(l): r.append(l)
        elif t < sum(l): return
        for u in range(idx, len(lst)):
            _a(u + x, l + [lst[u]], r, t)
        return r
    return _a(0, [], [], target)

注意:上面的方法在原版的基础上做了改进


原帖:

嗯 - 使用一些逻辑快速简单地应用您的数据得出的结论是您有正确的答案:

# data
vals = [57, 71, 87, 97, 99, 101, 103, 113, 114, 115, 128, 129, 131, 137, 147, 156, 163, 186]
target = 270

使用itertools.combinations

>>> from itertools import combinations
>>> [comb for i in range(1, 20) for comb in combinations(vals, i) if sum(comb) == target]
[(114, 156), (57, 99, 114)]

但是,也许您想使用 combinations_with_replacement,它允许从初始列表中多次使用值,而不是只使用一次。

使用itertools.combinations_with_replacement

>>> from itertools import combinations_with_replacement
>>> [comb for i in range(1, 20) for comb in combinations_with_replacement(vals, i) if sum(comb) == target]
>>>  # result takes too long ...

你可以把它变成一个健壮的函数:

def subsets_with_sum(lst, target, subset_lengths=range(1, 20), method='combinations'):   
    import itertools
    return [comb for i in subset_lengths for comb in
            getattr(itertools, method)(lst, i) if sum(comb) == target]

>>> subsets_with_sum(vals , 270)
[(114, 156), (57, 99, 114)]

thefourtheye 提供的另一种方法,它快得多,并且不需要导入:

def a(lst, target, with_replacement=False):
    def _a(idx, l, r, t, w):
        if t == sum(l): r.append(l)
        elif t < sum(l): return
        for u in range(idx, len(lst)):
            _a(u if w else (u + 1), l + [lst[u]], r, t, w)
        return r
    return _a(0, [], [], target, with_replacement)


>>> s = [57, 71, 87, 97, 99, 101, 103, 113, 114, 115, 128, 129, 131, 137, 147, 156, 163, 186]
>>> a(s, 270)
[[57, 99, 114], [114, 156]]
>>> a(s, 270, True)
[[57, 57, 57, 99], [57, 57, 156], [57, 71, 71, 71], [57, 99, 114], [71, 71, 128], [114, 156]]

时间:

def a(lst, target, with_replacement=False):
    def _a(idx, l, r, t, w):
        if t == sum(l): r.append(l)
        elif t < sum(l): return
        for u in range(idx, len(lst)):
            _a(u if w else (u + 1), l + [lst[u]], r, t, w)
        return r
    return _a(0, [], [], target, with_replacement)

def b(lst, target, subset_lengths=range(1, 21), method='combinations'):   
    import itertools
    return [comb for i in subset_lengths for comb in
            getattr(itertools, method)(lst, i) if sum(comb) == target]
    
vals = [57, 71, 87, 97, 99, 101, 103, 113, 114, 115, 128, 129, 131, 137, 147, 156, 163, 186]

from timeit import timeit
print 'no replacement'
print timeit("a(vals, 270)", "from __main__ import vals, a", number=10)
print timeit("b(vals, 270)", "from __main__ import vals, b", number=10)
print 'with replacement'
print timeit("a(vals, 270, True)", "from __main__ import vals, a", number=10)
print timeit("b(vals, 270, method='combinations_with_replacement')", "from __main__ import vals, b", number=10)

定时输出:

no replacement
0.0273933852733
0.683039054001
with replacement
0.0177899423427
... waited a long time ... no results ...

结论:

新方法 (a) 至少快 20 倍。

【讨论】:

  • with_replacement 关键字参数实际上并没有用于 a()_a() 函数中的任何内容(除了从前者传递给后者,所以它也可以忽略)。
  • 谢谢你,@InbarRose! subsets_with_sum() 功能对我帮助很大。我知道什么是递归,但是由于我很少或从不使用它,所以现在这个函数对我来说非常抽象。你能帮我理解一下这个函数是如何工作的吗?
  • @Guilherme 这里没有递归。这篇文章已经有将近 6 年的历史了。如果您需要帮助,您可能想问一个新问题。
  • stackoverflow.com/questions/58415182/… 在这里发帖,谢谢。
【解决方案2】:

如果您只是想计算组合的数量,请使用:

aminoacid_masses = [57, 71, 87, 97, 99, 101, 103, 113, 114, 115, 128, 129, 131, 137, 147, 156, 163, 186]


def peptides(n, d):
    for m in aminoacid_masses:
        if n-m in d:
            d[n] = d.get(n,0)+d[n-m]
    return d


def pep_counter(M):
    dicc = {0:1}
    mn = min(aminoacid_masses)
    for i in range(M-mn+1):
        j = i+mn
        peptides(j,dicc)
    return dicc


# This line calls the routine and indexes the returned dict.  Both with the desired mass (the mass we want peptides to sum up to)
print(pep_counter(1024)[1024])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    • 2014-06-15
    • 1970-01-01
    相关资源
    最近更新 更多