以递归方式返回最大总和为n的列表子集

时间:2017-08-06 03:24:50

标签: python recursion

def pack(L, n):
    '''Return the subset of L with the largest sum up to n
    >>> s = [4,1,3,5]
    >>> pack(s, 7)
    {3, 4}
    >>> pack(s, 6)
    {1, 5}
    >>> pack(s, 11)
    {1, 4, 5}
    '''

我被要求对此进行编码。它接受一个列表和一个整数,并返回最佳组合以获得小于或等于的整数。

我使用了一个辅助函数来获取总和,但它不正确,因为我不知道如何在递归时替换数字。

# doesn't work as intended
def pack_helper(L, n, sum=0):
    '''Return the subset of L with the largest sum up to n and the sum total
    >>> s = [4,1,3,5]
    >>> pack_helper(s, 7)
    ({3, 4}, 7)
    >>> pack(s, 6)
    ({1, 5}, 6)
    >>> pack(s, 11)
    ({1, 4, 5}, 10)
    '''
    package = set()
    if L == []:
        result = (package, sum)
    else:
        first = L[0]
        (package, sum) = pack_helper(L[1:], n, sum)
        if sum < n and (first + sum) <= n:
            package.add(first)
            sum = sum + first

    return (package, sum)

任何提示或帮助?谢谢

2 个答案:

答案 0 :(得分:0)

这是一个简单的递归函数,可以完成这项任务:

def pack(L, n):
    '''Return the subset of L with the largest sum up to n
    >>> s = [4,1,3,5]
    >>> pack(s, 7)
    {3, 4}
    >>> pack(s, 6)
    {1, 5}
    >>> pack(s, 11)
    {1, 4, 5}
    '''

    if all(j > n for j in L):
        return set()

    return max(({j} | pack(L[i+1:], n-j) for i, j in enumerate(L) if j <= n), key=sum)

如果您使用的是Python 3,则可以将default参数传递给max,而不是:

def pack(L, n):
    return max(({j} | pack(L[i+1:], n-j) for i, j in enumerate(L) if j <= n), key=sum, default=set())

答案 1 :(得分:0)

这里的测试数据足够小,暴力非常快。递归不是必需的:

from itertools import chain, combinations

# taken from the itertools documentation
def powerset(iterable):
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def pack(L, n):
    best_set, best_sum = (), 0
    for candidate in powerset(L):
        total = sum(candidate)
        if best_sum < total <= n:
            best_set, best_sum = candidate, total
    return best_set

然而,假设权重为正,dynamic programming解决方案很短。

def pack(L, n):
    assert all(w > 0 for w in L), 'weights must all be positive'
    a = [((), 0)] * (n + 1)
    for w in L:
        a = [ (a[x - w][0] + (w,), a[x - w][1] + w)
                if w <= x and a[x][1] < a[x - w][1] + w
                else a[x] for x in range(n + 1) ]
    return a[n][0]

这是如何运作的?

a[x]存储到目前为止处理的最佳权重集,总和最多x或更少(总和,只是为了节省时间)。在处理任何权重之前,这些权重都是空的()

要在目标w处理新的权重x,以下两个中的一个必须是最佳的。

  • 最佳权重集,没有此新权重总计x(旧a[x])或
  • 最佳权重集,没有总计x - w的新权重加上新的权重w

处理完所有权重后,解决方案就在那里。

顺便说一下,这是众所周知的0/1 knapsack problem。 (维基百科的文章目前有一个使用O(len(L)* n)时间和O(len(L)* n)空间的解决方案,但它在O(n)空间中是可行的,正如我在这里演示的那样。)