簡體   English   中英

動態編程 - 使用一組整數計算和的方法數

[英]Dynamic Programming - Number of ways to calculate sum using a set of integers

我經歷了這個問題

添加到具有N個數字的和S的 方法的 數量 查找從給定集合求和給定數量(允許重復)的所有方法

不太明白那里的答案,

我寫了兩個方法來解決一個問題:

找到使用數字N(允許重復)達到總和S的方式的數量

例如。 sum = 4和number = 1,2,3 answer是1111,22,1122,31,13,1212,2112,2212

在一種方法中我使用memoization而在另一種方法中我沒有。 在我的機器中,memoize版本的運行速度比非memoized版本慢

這兩種解決方案都有效。

記憶版:

def find_denomination_combinations(amount, denominations):
    memo = {}

    def calculate_combinations(max_amt):
        return_list = list()

        for denomination in denominations:
            new_sum = max_amt - denomination
            if new_sum == 0:
                return_list.append([max_amt])
                return return_list
            elif new_sum < 0:
                return [[]]
            else:
                if new_sum in memo:
                    combi_list = memo[new_sum]
                else:
                    combi_list = calculate_combinations(new_sum)
                for combination in combi_list:
                    if new_sum in memo and combination[:] not in memo[new_sum]:
                        memo[new_sum].append(combination[:])
                    else:
                        memo[new_sum] = []
                        memo[new_sum].append(combination[:])
                    combination.append(denomination)
                    return_list.append(combination)
        return return_list

    result = calculate_combinations(amount)
    return result

非記憶版

def find_denomination_combinations_nmemo(amount, denominations):

    def calculate_combinations(max_amt):
        return_list = list()

        for denomination in denominations:
            new_sum = max_amt - denomination
            if new_sum == 0:
                return_list.append([max_amt])
                return return_list
            elif new_sum < 0:
                return [[]]
            else:
                combi_list = calculate_combinations(new_sum)
                for combination in combi_list:
                    combination.append(denomination)
                    return_list.append(combination)
        return return_list

    result = calculate_combinations(amount)
    return result

我的算法是:

每個D的[T(sum-D)],其中D屬於給定的整數集合

如果輸入sum = 16且整數組= [1,2,3]

非記憶版本在0.3秒內運行,記憶版本需要5秒鍾

我相信memoized版本比較慢,因為復雜的代碼,它使用更新的最備忘錄字典else塊。 它可以更簡單:

if new_sum in memo:
    combi_list = memo[new_sum]
else:
    combi_list = memo[new_sum] = calculate_combinations(new_sum)
for combination in combi_list:
    return_list.append(combination + [denomination])

這要快得多。 使用此修復程序,在大多數情況下,memoized版本應該比非memoized代碼更快。

但是還有其他問題。 如果您的denominations列表沒有按遞增順序排序或者面額值之間存在差距,您將得到錯誤的結果。 基本上,任何可能導致elif案件被擊中的情況都會給出錯誤的結果。

這是for循環體的一個版本, for糾正這些問題:

new_sum = max_amt - denomination
if new_sum == 0:
    return_list.append([max_amt]) # don't return here, to allow unsorted denominations!
elif new_sum > 0:
    if new_sum in memo:
        combi_list = memo[new_sum]
    else:
        combi_list = memo[new_sum] = calculate_combinations(new_sum)
    for combination in combi_list:
        return_list.append(combination + [denomination])
# do nothing for new_amt < 0

您可以通過使每個調用在備忘錄中保存自己的結果,而不是依賴其調用者來執行此操作,並將基本案例邏輯(對於new_sum == 0 )與new_sum == 0相結合,從而進一步簡化操作。 我還重命名或刪除了幾個變量:

def find_denomination_combinations_blckknght(amount, denominations):
    memo = {0: [[]]} # prefill value for base case of calculate_combinations where amt==0

    def calculate_combinations(amt):
        if amt not in memo:
            memo[amt] = []
            for denomination in denominations:
                new_amt = amt - denomination
                if new_amt >= 0:
                    for combination in calculate_combinations(new_amt):
                        memo[amt].append(combination + [denomination])
                # do nothing for new_amt < 0
        return memo[amt]

    return calculate_combinations(amount)

這有點慢,可能是因為額外的函數調用,但代碼更簡單,沒有elifelse任何else情況!

暫無
暫無

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

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