[英]Return the subset of a list with the largest sum up to n recursively
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}
'''
I'm asked to code this. 我被要求对此进行编码。 It takes in a list and an integer and returns the best combination to get that integer less than or equal to.
它接受一个列表和一个整数,并返回最佳组合以使该整数小于或等于。
I used a helper function that takes in the sum, but it's not correct since I don't know how I could replace a number while in recursion. 我使用了一个求和的辅助函数,但这是不正确的,因为我不知道在递归时如何替换数字。
# 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)
Any hints or help? 有任何提示或帮助吗? Thx
谢谢
Here's a simple recursive function that does the job: 这是完成任务的简单递归函数:
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)
If you're using Python 3, you can pass the default
parameter to max
instead: 如果您使用的是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())
The test data here is small enough that brute force is pretty fast. 这里的测试数据足够小,以至于蛮力非常快。 Recursion is not necessary:
递归是没有必要的:
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
However, assuming positive weights, the dynamic programming solution is pretty short. 但是,假设权重为正,则动态编程解决方案非常短。
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]
How does this work? 这是如何运作的?
a[x]
stores the best set of weights processed so far that sum up to x
or less (and the sum, just to save time). a[x]
存储到目前为止已处理的最佳权重集,这些权重之和等于x
或更小(和为求节省时间)。 Before any weights have been processed, these are all empty ()
. 在处理任何权重之前,这些都是空的
()
。
To process a new weight w
at target x
, one of the following two sets must be the best. 为了在目标
x
处处理新的权重w
,以下两个集合之一必须是最佳的。
x
(the old a[x]
), or x
(旧的a[x]
),或 x - w
, plus this new weight w
x - w
,加上这个新权重w
Once all the weights are processed, the solution is right there at the end. 一旦处理完所有的砝码,解决方案就在最后。
By the way, this is the well-known 0/1 knapsack problem . 顺便说一下,这就是众所周知的0/1背包问题 。 (The Wikipedia article currently has a solution that uses O(len(L) * n) time and O(len(L) * n) space, but it's doable in O(n) space, as I demonstrated here.)
(Wikipedia文章当前提供了一种使用O(len(L)* n)时间和O(len(L)* n)空间的解决方案,但是在O(n)空间中是可行的,如我在此处演示的那样。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.