我正在学习Python,而我遇到的问题似乎很简单。
我想找到总结给定数字的所有可能的数字组合。
例如:4 - > [1,1,1,1] [1,1,2] [2,2] [1,3]
我选择生成所有可能子集(2 ^ n)的解决方案,然后只生成总和等于数字的那些子集。我的病情有问题。代码:
def allSum(number):
#mask = [0] * number
for i in xrange(2**number):
subSet = []
for j in xrange(number):
#if :
subSet.append(j)
if sum(subSet) == number:
yield subSet
for i in allSum(4):
print i
BTW这是一个很好的方法吗?
答案 0 :(得分:4)
几年前我看到的一些代码可以解决这个问题:
>>> def partitions(n):
if n:
for subpart in partitions(n-1):
yield [1] + subpart
if subpart and (len(subpart) < 2 or subpart[1] > subpart[0]):
yield [subpart[0] + 1] + subpart[1:]
else:
yield []
>>> print list(partitions(4))
[[1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3], [4]]
其他参考文献:
答案 1 :(得分:1)
该解决方案不起作用,对吗?它永远不会在一个子集中多次添加一个数字,所以你永远不会得到,例如,[1,1,2]。它永远不会跳过一个数字,所以你永远不会得到,例如,[1,3]。
所以你的解决方案的问题有两个:一,你实际上并没有在1..number范围内生成所有可能的子集。二,所有子集的集合将排除您应该包含的内容,因为它不允许数字出现多次。
这种问题可以概括为搜索问题。想象一下,您想要尝试的数字是树上的节点,然后您可以使用深度优先搜索来查找表示解决方案的树中的所有路径。它是一棵无限大的树,但幸运的是,你永远不需要搜索所有树。
答案 2 :(得分:1)
这是一种替代方法,它通过获取所有1的列表并通过添加后续元素递归地折叠它来工作,这应该比生成所有可能的子集更有效:
def allSum(number):
def _collapse(lst):
yield lst
while len(lst) > 1:
lst = lst[:-2] + [lst[-2] + lst[-1]]
for prefix in _collapse(lst[:-1]):
if not prefix or prefix[-1] <= lst[-1]:
yield prefix + [lst[-1]]
return list(_collapse([1] * number))
>>> allSum(4)
[[1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3], [4]]
>>> allSum(5)
[[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 2, 2], [1, 1, 3], [2, 3], [1, 4], [5]]
如果您不想要这个简单的案例,您可以去除最后一个值。如果您只是循环结果,请删除list
调用,然后返回生成器。
答案 3 :(得分:1)
这相当于this question中描述的问题,可以使用类似的解决方案。
详细说明:
def allSum(number):
for solution in possibilites(range(1, number+1), number):
expanded = []
for value, qty in zip(range(1, number+1), solution):
expanded.extend([value]*qty)
yield expanded
将此问题转化为该问题并再次转回。