簡體   English   中英

遞歸返回最大和為n的列表的子集

[英]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}
    '''

我被要求對此進行編碼。 它接受一個列表和一個整數,並返回最佳組合以使該整數小於或等於。

我使用了一個求和的輔助函數,但這是不正確的,因為我不知道在遞歸時如何替換數字。

# 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)

有任何提示或幫助嗎? 謝謝

這是完成任務的簡單遞歸函數:

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())

這里的測試數據足夠小,以至於蠻力非常快。 遞歸是沒有必要的:

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

但是,假設權重為正,則動態編程解決方案非常短。

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或更小(和為求節省時間)。 在處理任何權重之前,這些都是空的()

為了在目標x處處理新的權重w ,以下兩個集合之一必須是最佳的。

  • 沒有此新權重的最佳權重集總和為x (舊的a[x] ),或
  • 沒有這個新權重的最佳權重集總和為x - w ,加上這個新權重w

一旦處理完所有的砝碼,解決方案就在最后。


順便說一下,這就是眾所周知的0/1背包問題 (Wikipedia文章當前提供了一種使用O(len(L)* n)時間和O(len(L)* n)空間的解決方案,但是在O(n)空間中是可行的,如我在此處演示的那樣。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM