我正在自学递归回溯。对于骰子求和问题,我无法弄清楚如何优雅地收集结果。
作为参考,这是我的代码,该代码仅打印符合条件的任何骰子。理想情况下,我想更改它,而不是打印输出,而是可以建立那些选择的骰子的列表并将其返回。
下面是无法满足我要求的代码
def dice_sum(num_dice: int, target_sum: int) -> None:
dice_sum_helper(num_dice, target_sum, [])
def dice_sum_helper(num_dice: int, target_sum: int, chosen: List[int]) -> None:
if num_dice == 0 and sum(chosen) == target_sum:
print(chosen)
elif num_dice == 0:
pass
else:
for i in range(1, 7):
chosen.append(i)
dice_sum_helper(num_dice - 1, target_sum, chosen)
chosen.pop()
相反,我希望它执行类似的操作
from typing import List
DiceResult = List[List[int]]
def dice_sum(num_dice: int, target_sum: int) -> DiceResult:
return dice_sum_helper(num_dice, target_sum, [])
def dice_sum_helper(num_dice: int, target_sum: int, chosen: List[int]) -> DiceResult:
if num_dice == 0 and sum(chosen) == target_sum:
# Return the value that meets the constraints
return chosen
elif num_dice == 0:
pass
else:
for i in range(1, 7):
chosen.append(i)
# Return the result of my recursive call and build the list of lists?
result = dice_sum_helper(num_dice - 1, target_sum, chosen)
return result.append(result)
# End of that logic
chosen.pop()
我正在寻找要使用的理论或模式,而不是确切的代码。如果不使用外部列表,我将无法获得收集和附加每个结果的代码。
答案 0 :(得分:2)
您可以利用yield
和yield from
从函数中返回结果:
from typing import List
def dice_sum(num_dice: int, target_sum: int) -> None:
yield from dice_sum_helper(num_dice, target_sum, [])
def dice_sum_helper(num_dice: int, target_sum: int, chosen: List[int]) -> None:
if num_dice == 0 and sum(chosen) == target_sum:
yield chosen[:]
elif num_dice == 0:
pass
else:
for i in range(1, 7):
chosen.append(i)
yield from dice_sum_helper(num_dice - 1, target_sum, chosen)
chosen.pop()
# you can store the results e.g. to list:
# results = list(dice_sum(3, 12))
for dices in dice_sum(3, 12):
for d in dices:
print('{: ^4}'.format(d), end='|')
print()
打印:
1 | 5 | 6 |
1 | 6 | 5 |
2 | 4 | 6 |
2 | 5 | 5 |
2 | 6 | 4 |
3 | 3 | 6 |
3 | 4 | 5 |
3 | 5 | 4 |
3 | 6 | 3 |
4 | 2 | 6 |
4 | 3 | 5 |
4 | 4 | 4 |
4 | 5 | 3 |
4 | 6 | 2 |
5 | 1 | 6 |
5 | 2 | 5 |
5 | 3 | 4 |
5 | 4 | 3 |
5 | 5 | 2 |
5 | 6 | 1 |
6 | 1 | 5 |
6 | 2 | 4 |
6 | 3 | 3 |
6 | 4 | 2 |
6 | 5 | 1 |
答案 1 :(得分:1)
您可以传递“结果”列表以存储结果:
from typing import List
DiceResult = List[List[int]]
def dice_sum(num_dice: int, target_sum: int) -> DiceResult:
results = []
dice_sum_helper(num_dice, target_sum, [], results)
return results
def dice_sum_helper(num_dice: int, target_sum: int, chosen: List[int], results: DiceResult):
if num_dice == 0 and sum(chosen) == target_sum:
# Store the value that meets the constraints
results.append(chosen.copy())
elif num_dice == 0:
pass
else:
for i in range(1, 7):
chosen.append(i)
dice_sum_helper(num_dice - 1, target_sum, chosen, results)
chosen.pop()
请注意,如果订购无所谓,这将返回许多重复项。您可能需要研究更改此值,以在每次递归中计算出较小的目标总和,并以某种方式记录下来,这样可以节省大量工作。另请参见Calculate the number of ways to roll a certain number
答案 2 :(得分:1)
为了实现优雅的既定目标,我将采用一个没有帮助函数或传递额外参数的简单设计:
def dice_sum(num_dice, target_sum):
solutions = []
for i in range(1, 7):
if target_sum < i:
continue
if target_sum == i:
if num_dice == 1:
solutions.append([i])
continue
for solution in dice_sum(num_dice - 1, target_sum - i):
solutions.append([i] + solution)
return solutions
print(dice_sum(3, 5))
输出
> python3 test.py
[[1, 1, 3], [1, 2, 2], [1, 3, 1], [2, 1, 2], [2, 2, 1], [3, 1, 1]]
>
要提高效率,您可以添加:
if target_sum - i < num_dice - 1:
continue
在内部for
循环之前,如果没有希望,可以避免递归。
答案 3 :(得分:0)
具有提前中止条件的递归生成器,因此就模辊顺序而言,您不会得到重复的解决方案。即我认为您的骰子无法识别,因此数字的顺序无关紧要。
def dice_sum (num_dice, target_sum, start=1):
if num_dice == 1 and 0 < target_sum < 7:
yield (target_sum, )
return
if num_dice * 6 < target_sum or num_dice > target_sum:
return
for i in xrange(start, 7):
for option in dice_sum(num_dice - 1, target_sum - i, i):
if i > option[0]:
return
yield (i, ) + option
>>> tuple(dice_sum(3, 12))
((1, 5, 6), (2, 4, 6), (2, 5, 5), (3, 3, 6), (3, 4, 5), (4, 4, 4))