繁体   English   中英

Python 中的 Powerset 算法:列表上的 + 和 append 之间的区别

[英]Powerset algorithm in Python: difference between + and append on lists

我正在解决 Python 中的 powerset 问题。

集合 S 的幂集 P(S) 是 S 的所有子集的集合。 例如,如果S = {a, b, c}那么P(s) = {{}, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}}

这个解决方案工作得很好:

def powerset(array):
    powerset = [[]]
    for num in array:
        for i in range(len(powerset)):
            curr_subset = powerset[i]
            powerset.append(curr_subset + [num])
    return powerset

但是,此解决方案不会:

def powerset(array):
    powerset = [[]]
    for num in array:
        for i in range(len(powerset)):
            curr_subset = powerset[i]
            curr_subset.append(num)
            powerset.append(curr_subset)
    return powerset

它似乎在每个 powerset.append 操作上覆盖了 powerset 中的每个数组。 对于[1, 2, 3]的输入,我最终得到的返回值为:

[[1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3],
 [1, 2, 2, 3, 3, 3, 3]]

知道我在这里不完全理解吗?

您的算法的问题在于列表是可变的,并且您正在创建一个包含对同一列表的引用的列表。 任何时候您附加到其中之一,您就是附加到所有这些,因为只有其中之一。 它们都是相同的列表,您只是有多个参考。

想象一下,有一份包含 10 个某人电话号码副本的列表; 如果你拨打第一个电话号码并要求他们戴上帽子,那么当你拨打第二个电话号码时,另一端的人已经戴上了帽子,因为它是同一个人。 如果您呼叫每个号码并每次都说“戴上帽子”,那么您最终会得到一个包含 10 个电话号码的列表,其中包含一个戴 10 个帽子的人的电话号码,而实际上您需要 10 个戴一顶帽子的人的电话号码。

设计这种算法最简单的方法就是完全避免变异; 使用元组而不是列表。 这样,每次向元组添加另一个元素时,您都是在创建一个新元组,而不是更改现有元组。

请注意,这与您使用curr_subset + [num]第一个解决方案非常相似; +操作创建一个新列表,不像append改变现有列表的状态。

def powerset(array):
    # a list containing an empty tuple
    powerset = [()]

    for num in array:
        for i in range(len(powerset)):
            curr_subset = powerset[i]
            # append one more number to the tuple
            curr_subset += (num,)
            powerset.append(curr_subset)

    return powerset

例子:

>>> powerset([1, 2, 3])
[(), (1,), (2,), (1, 2), (3,), (1, 3), (2, 3), (1, 2, 3)]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM