Python:递归地创建一个包含条件的int列表

时间:2015-10-20 14:42:27

标签: python recursion

我想创建n个项目的所有可能分布。这是指众所周知的鸽笼原理。

以下值是Microsoft Excel的结果:

get_distributions(list, number_of_items_to_distribute)
get_distributions([], 1) = [[1]]
get_distributions([], 2) = [[1, 1], [2]]
get_distributions([], 3) = [[1, 1, 1], [1, 2], [2, 1], [3]]
get_distributions([], 4) = [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]]

我已经有了一些代码,但删除临时列表时存在一些问题。

all_distributions = []

def get_distributions(distribution, items):

    print('call with distribution = ' + str(distribution) + ', items = ' + str(items))
    print('---------------')

    # base case
    if items == 0:
        all_distributions.append(distribution)
        print('end: ' + str(distribution))
        distribution.clear()
        return []

    # recursion
    else:
        for i in range(1, items + 1):
            distribution.append(i)
            get_distributions(distribution, items - i)

有了这个,我在“结束:”之后打印出了很好的结果,但是缺少像[1,2](用n = 3调用)这样的值。除此之外,这些值不会附加到我的all_distributions。

我对我试图解决这个问题的方式很感兴趣。这是一个好方法还是我绝对错了?

1 个答案:

答案 0 :(得分:1)

代码的主要问题是列表all_distributions最终包含对同一输入列表distribution的许多引用。当您致电all_distributions.append(distribution)时,列表distribution不会被复制到列表all_distributions中,而只会附加对列表的引用。您可以通过明确插入副本来解决此问题:all_distributions.append(list(distribution))

对代码的最小修复是插入副本,删除基本案例中的distribution.clear(),并在递归调用后添加distribution.pop()

all_distributions = []

def get_distributions(distribution, items):
    if items == 0:
        all_distributions.append(list(distribution))
    else:
        for i in range(1, items + 1):
            distribution.append(i)
            get_distributions(distribution, items - i)
            distribution.pop()

get_distributions([], 3)
print(all_distributions)

输出:[[1, 1, 1], [1, 2], [2, 1], [3]]

更好的方法是避免使用distribution.append,而是在列表中使用加号运算符,如下所示:

def get_distributions(distribution, items):
    if items == 0:
        all_distributions.append(distribution)
    else:
        for i in range(1, items + 1):
            d = distribution + [i]
            get_distributions(d, items - i)

列表中的加号运算符通过连接两个给定列表来创建新列表。在这种情况下,我们在i右侧连接单个元素distribution以获取包含distribution后跟i的元素的新副本。

另一个改进是避免全局变量all_distributions,而是返回分发列表:

def get_distributions(distribution, items):
    if items == 0:
        return [distribution]
    else:
        all_distributions = []
        for i in range(1, items + 1):
            d = distribution + [i]
            all_distributions += get_distributions(d, items - i)
        return all_distributions

print(get_distributions([], 4))

输出:[[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]]