我基本上有这个问题,获取所有加起来为一个数字的数字,但我也需要包含0。
我已经尝试了公认的答案,并使其从0开始,基本上就像一样
def sum_to_n(n, size, limit=None):
"""Produce all lists of `size` positive integers in decreasing order
that add up to `n`."""
if size == 1:
yield [n]
return
if limit is None:
limit = n
start = 0
stop = min(limit, n - size + 1) + 1
for i in range(start, stop):
for tail in sum_to_n(n - i, size - 1, i):
yield [i] + tail
对于较小的数字来说效果很好,但当我有较大的数字作为目标时,它开始表现得很奇怪。例如:
for partition in sum_to_n(10,7):
print(partition)
输出类似
0 0 0 0 0 0 10
1 0 0 0 0 0 9
1 1 0 0 0 0 8
1 1 1 0 0 0 7
1 1 1 1 0 0 6
1 1 1 1 1 0 5
1 1 1 1 1 1 4
2 0 0 0 0 0 8
2 1 0 0 0 0 7
2 1 1 0 0 0 6
2 1 1 1 0 0 5
2 1 1 1 1 0 4
2 1 1 1 1 1 3
2 2 0 0 0 0 6
2 2 1 0 0 0 5
2 2 1 1 0 0 4
2 2 1 1 1 0 3
2 2 1 1 1 1 2
2 2 2 0 0 0 4
2 2 2 1 0 0 3
2 2 2 1 1 0 2
2 2 2 1 1 1 1
3 0 0 0 0 0 7
3 1 0 0 0 0 6
3 1 1 0 0 0 5
3 1 1 1 0 0 4
3 1 1 1 1 0 3
3 1 1 1 1 1 2
3 2 0 0 0 0 5
3 2 1 0 0 0 4
3 2 1 1 0 0 3
3 2 1 1 1 0 2
3 2 1 1 1 1 1
4 0 0 0 0 0 6
4 1 0 0 0 0 5
4 1 1 0 0 0 4
4 1 1 1 0 0 3
4 1 1 1 1 0 2
4 1 1 1 1 1 1
这里显然没有包括5 5 0 0 0 0的情况。它也有重复的情况,比如11 11 11 14和4 11 11 11 11,我不想要。这个代码出了什么问题?我如何修改它或对如何解决问题有任何其他想法?谢谢
我想这就是你想要的:
def sum_to_n2(n, size, limit=None):
if limit is None:
limit = n
if size == 1:
if n <= limit:
yield[n]
else:
for i in range(0, limit+1):
for tail in sum_to_n2(n - i, size - 1, min(i, n-i)):
yield[i] + tail
print(list(sum_to_n2(10, 7)))
与您的代码的差异:
- 在开始时检查
limit is None
,该函数有一个更简单的if .. else
(主要是装饰性的,我觉得更容易理解,return
不需要离开那里( - 如果
size == 1
,则总是产生[n]
,但这应该只在n <= limit
的情况下发生 - 你将范围的极限计算为
min(limit, n - size + 1) + 1
,但这很难在心理上跟踪(更重要的是,范围刚好停在它附近(;我只是说,极限是i
(我们要添加到序列中的数字,所以不再允许更大的数字(和n-i
(余数,我们不再需要比这更大的数(的最小值,并将其传递给min(i, n-i)
的递归调用
我还没有完成计算错误在代码中的位置的完整逻辑,但如果我将n <= limit
条件添加到代码中,它将不再包括零。我相信你可以在将值与我的算法实现进行比较时弄清楚,但我认为我的算法有点干净,所以更可取。
6, 3
的输出已经显示了代码的问题,该代码比10, 7
短。sum_to_n(5, 3)
:的输出
[[0, 0, 5], [1, 0, 4], [1, 1, 3], [2, 0, 3], [2, 1, 2], [2, 2, 1], [3, 0, 2], [3, 1, 1]]
例如,请注意[2, 1, 2]
和[2, 2, 1]
中的重复项。
我的sum_to_n2(5, 3)
:输出
[[2, 2, 1], [3, 1, 1], [3, 2, 0], [4, 1, 0], [5, 0, 0]]
就我个人而言,但这纯粹是风格,我更喜欢这个修改后的实现的输出:
def sum_to_n2(n, size, limit=None):
if limit is None:
limit = n
if size == 1:
if n <= limit:
yield[n]
else:
for i in range(limit, -1, -1):
for tail in sum_to_n2(n - i, size - 1, min(i, n - i)):
yield[i] + tail
结果(对于sum_to_n2(5, 3)
(:
[[5, 0, 0], [4, 1, 0], [3, 2, 0], [3, 1, 1], [2, 2, 1]]
它通过反转for
循环中的范围来实现这一点。
顺便说一句,这里有一个非递归的一行代码可以做到这一点,但它慢得像泥一样:(:
def sum_to_n_one_liner (n, sized):
set(tuple(sorted(t)) for t in filter(lambda s: sum(s) == n, product(*[range(n)]*size)))
它的工作原理是天真地做需要做的事情:
- 取0到
n
的数字,size
次 - 计算笛卡尔乘积,得到这些数字的所有可能组合
- 只保留总和为
n
的 - 对生成的元组进行排序,只保留唯一的元组
为了不得到重复的分区,您可以定义函数,使其只生成非递减顺序的结果。
def partition(N,size):
if size == 1 :
yield (N,) # base case, only 1 part
return
for a in range(N//size+1): # smaller part followed by
for p in partition(N-a*size,size-1): # equal or larger ones
yield (a, *(n+a for n in p)) # recursing on delta only
输出:
for p in partition(10,7): print(p)
(0, 0, 0, 0, 0, 0, 10)
(0, 0, 0, 0, 0, 1, 9)
(0, 0, 0, 0, 0, 2, 8)
(0, 0, 0, 0, 0, 3, 7)
(0, 0, 0, 0, 0, 4, 6)
(0, 0, 0, 0, 0, 5, 5)
(0, 0, 0, 0, 1, 1, 8)
(0, 0, 0, 0, 1, 2, 7)
(0, 0, 0, 0, 1, 3, 6)
(0, 0, 0, 0, 1, 4, 5)
(0, 0, 0, 0, 2, 2, 6)
(0, 0, 0, 0, 2, 3, 5)
(0, 0, 0, 0, 2, 4, 4)
(0, 0, 0, 0, 3, 3, 4)
(0, 0, 0, 1, 1, 1, 7)
(0, 0, 0, 1, 1, 2, 6)
(0, 0, 0, 1, 1, 3, 5)
(0, 0, 0, 1, 1, 4, 4)
(0, 0, 0, 1, 2, 2, 5)
(0, 0, 0, 1, 2, 3, 4)
(0, 0, 0, 1, 3, 3, 3)
(0, 0, 0, 2, 2, 2, 4)
(0, 0, 0, 2, 2, 3, 3)
(0, 0, 1, 1, 1, 1, 6)
(0, 0, 1, 1, 1, 2, 5)
(0, 0, 1, 1, 1, 3, 4)
(0, 0, 1, 1, 2, 2, 4)
(0, 0, 1, 1, 2, 3, 3)
(0, 0, 1, 2, 2, 2, 3)
(0, 0, 2, 2, 2, 2, 2)
(0, 1, 1, 1, 1, 1, 5)
(0, 1, 1, 1, 1, 2, 4)
(0, 1, 1, 1, 1, 3, 3)
(0, 1, 1, 1, 2, 2, 3)
(0, 1, 1, 2, 2, 2, 2)
(1, 1, 1, 1, 1, 1, 4)
(1, 1, 1, 1, 1, 2, 3)
(1, 1, 1, 1, 2, 2, 2)