[英]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.