这是使用itertools 库的最小解决方案:
from itertools import permutations, chain
solve = lambda n: [(1,)*n] + list(set(chain(*[permutations((2,)*i + (1,)*(n-2*i)) for i in range(1, n//2+1)])))
对于您的示例输入:
> solve(3)
[(1, 1, 1), (1, 2), (2, 1)]
它是如何工作的?
如果我们向后退一步,就会更容易看到正在发生的事情:
def solve(n):
combinations = [(1,)*n]
for i in range(1, n//2+1):
combinations.extend(permutations((2,)*i + (1,)*(n-2*i)))
return list(set(combinations))
最微不足道的情况是您一次迈出一步,所以 n 步骤:(1,)*n。然后我们可以看看我们最多可以走多少双步,这是 n 除以 2 的底数:n//2。然后我们迭代可能的双步:尝试在每次迭代(2,)*i 时添加一个双步,用单步填充剩余空间(1,)*(n-2*i)。
来自 itertools 的函数 permutations 将为该迭代生成所有可能的单步和双步排列。输入 (1,1,2),它将生成 (1,1,2)、(1,2,1) 和 (2,1,1)。最后,我们使用将结果转换为set 的技巧来删除重复项,然后将其转换回列表。
任何数量和长度的步骤的泛化(不是最优的!)
一个班轮:
from itertools import permutations, chain, combinations_with_replacement
solve = lambda n, steps: list(set(chain(*[permutations(sequence) for sequence in chain(*[combinations_with_replacement(steps, r) for r in range(n//min(steps)+1)]) if sum(sequence) == n])))
示例输出:
> solve(8, [2,3])
[(3, 2, 3), (2, 3, 3), (2, 2, 2, 2), (3, 3, 2)]
更易于阅读的版本:
def solve(n, steps):
result = []
for sequence_length in range(n//min(steps)+1):
sequences = combinations_with_replacement(steps, sequence_length)
for sequence in sequences:
if sum(sequence) == n:
result.extend(permutations(sequence))
return list(set(result))