簡體   English   中英

python 中的子集問題 - 修復目標總和的加數數

[英]Subset problem in python - fix the number of addends that sum to the target

我正在嘗試實現一個簡單的程序,旨在解決 python 中的子集問題。 我發現以下涉及動態編程的代碼:

def subset_sum(numbers, target, partial=[]):
    s = sum(partial)

    # check if the partial sum is equals to target
    if s == target: 
        print("sum(%s)=%s" % (partial, target))
    if s >= target:
        return  # if we reach the number why bother to continue

    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i+1:]
        subset_sum(remaining, target, partial + [n]) 

該代碼有效,但它找到了所有可能的組合,而我只對我想不時指定的最大加數的子集感興趣。

換句話說,我想設置內部循環的最大數量。 為此,我編寫了以下代碼,該代碼考慮了最多 4 個數字的所有可能組合,這些數字總和為目標(即 3 個內部循環)

def subset_sum(n_list, tot):

    cnd = n_list[n_list < tot]
    s = np.sort(cnd)
    n_max = len(cnd)

    possibilities = []

    for i1 in range(n_max):
        i2 = i1+1

        while (i2<n_max)and(s[i1]+s[i2]<=tot):
            if (s[i1]+s[i2]==tot):
                possibilities.append([s[i1],s[i2]])
            i3 = i2+1

            while (i3<n_max)and(s[i1]+s[i2]+s[i3]<=tot):
                if (s[i1]+s[i2]+s[i3]==tot):
                    possibilities.append([s[i1],s[i2],s[i3]])
                i4 = i3+1

                while (i4<n_max)and(s[i1]+s[i2]+s[i3]+s[i4]<=tot):
                    if (s[i1]+s[i2]+s[i3]+s[i4]==tot):
                        possibilities.append([s[i1],s[i2],s[i3],s[i4]])

                    i4+=1
                i3+=1
            i2+=1 
    return possibilities

這段代碼效果很好,也可以用 numba 加速(雖然第一個代碼沒有),但我無法修復最大加數數。 有沒有辦法用一個額外的參數來實現 function subset_sum 來修復目標總和的最大加數?

由於您在每次遞歸中添加一個數字,因此您可以限制遞歸深度。 為此,您需要添加一個新參數來控制最大深度(也就是最大加數)。

這是代碼:

def subset_sum(numbers, target, num_elems, partial=[]):
    # Check if the partial sum is equals to target                                                                                                                                                                                          
    s = sum(partial)
    if s == target:
        print("sum(%s)=%s" % (partial, target))

    # If we have surpassed the number there is no point to continue
    if s >= target:
        return
    # If we have surpassed the number of elements there is no point to continue
    if len(partial) >= num_elems:
        return

    # Otherwise go through the remaining numbers
    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i+1:]
        subset_sum(remaining, target, num_elems, partial + [n]) 

您可以使用以下命令運行它:

if __name__ == "__main__":
    nums = [1, 2, 3, 4, 5]
    num_elems = 3
    target = 10
    p = []
    subset_sum(nums, target, num_elems, p)

output 將是:

sum([1, 4, 5])=10
sum([2, 3, 5])=10

請注意,未顯示 4 個元素( [1, 2, 3, 4] )的組合。


編輯:

要使用 Numba 加速上述代碼,您需要構建它的迭代版本。 由於您基本上是在計算num_elements大小的集合中的numbers組合,因此您可以檢查itertools.combination的迭代實現(更多細節在這里)。 基於該實現,您可以獲得以下代碼:

def subset_sum_iter(numbers, target, num_elements):
    # General: we iterate among the indexes and build each solution by taking the values in those indexes

    # Initialize solutions list
    solutions = []

    # Build first index by taking the first num_elements from the numbers
    indices = list(range(num_elements))
    solution = [numbers[i] for i in indices]
    if sum(solution) == target:
        solutions.append(solution)

    # We iterate over the rest of the indices until we have tried all combinations
    while True:
        for i in reversed(range(num_elements)):
            if indices[i] != i + len(numbers) - num_elements:
                break
        else:
            # No combinations left
            break

        # Increase current index and all its following ones
        indices[i] += 1
        for j in range(i + 1, num_elements):
            indices[j] = indices[j - 1] + 1

        # Check current solution
        solution = [numbers[i] for i in indices]
        if sum(solution) == target:
            solutions.append(solution)

    # Print all valid solutions
    for sol in solutions:
        print ("sum(" + str(sol) + ")=" + str(target))

可以運行:

if __name__ == "__main__":
    nums = [1, 2, 3, 4, 5]
    num_elems = 3
    target = 10

    # Calling iterative subset
    subset_sum_iter(nums, target, num_elems)

並輸出:

sum([1, 4, 5])=10
sum([2, 3, 5])=10

與前一種情況一樣,請注意僅顯示了具有 3 個元素的組合。

我不確定您是否更喜歡這里的組合或排列,但您可以試試這個?

import itertools
limit = 1 #number of addends
possibilities = 0
combinations = []
not_possibilties = 0
number_addends = 4
while(limit <= number_addends):
    for comb in itertools.combinations([number_list], limit):
        if sum(comb) == target:
            possibilities +=1
            combinations.append(comb)
        else:
            not_possiblitities +=1
    limit +=1
total_combi = possibilities + not_possibilties #check if actually all possibilities were done

如果您需要排列,只需將 itertools.combinations 更改為 itertools.permutationss

暫無
暫無

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

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