简体   繁体   English

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

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

I'm working through the powerset problem in Python.我正在解决 Python 中的 powerset 问题。

The powerset P(S) of a set S is the set of all subsets of S. For example, if S = {a, b, c} then P(s) = {{}, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}} .集合 S 的幂集 P(S) 是 S 的所有子集的集合。 例如,如果S = {a, b, c}那么P(s) = {{}, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}}

This solution works just fine:这个解决方案工作得很好:

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

However, this solution does not:但是,此解决方案不会:

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

It seems to overwrite every array in the powerset on each powerset.append operation.它似乎在每个 powerset.append 操作上覆盖了 powerset 中的每个数组。 For an input of [1, 2, 3] , I end up with a return value of:对于[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]]

Any idea what I'm not fully understanding here?知道我在这里不完全理解吗?

The problem with your algorithm is that lists are mutable, and you are creating a list full of references to the same list.您的算法的问题在于列表是可变的,并且您正在创建一个包含对同一列表的引用的列表。 Any time you append to one of them, you are appending to all of them, because there is only one of them.任何时候您附加到其中之一,您就是附加到所有这些,因为只有其中之一。 They are all the same list, you just have more than one reference to it.它们都是相同的列表,您只是有多个参考。

Imagine having a list of 10 copies of somebody's phone number;想象一下,有一份包含 10 个某人电话号码副本的列表; if you call up the first phone number and ask them to put on a hat, then when you call the second phone number, the person on the other end will already be wearing a hat, because it is the same person.如果你拨打第一个电话号码并要求他们戴上帽子,那么当你拨打第二个电话号码时,另一端的人已经戴上了帽子,因为它是同一个人。 If you call each number and say "put on a hat" each time, you'll end up with a list of 10 phone numbers for one person wearing 10 hats, when you actually wanted phone numbers for 10 people wearing one hat each.如果您呼叫每个号码并每次都说“戴上帽子”,那么您最终会得到一个包含 10 个电话号码的列表,其中包含一个戴 10 个帽子的人的电话号码,而实际上您需要 10 个戴一顶帽子的人的电话号码。

The simplest way to design this kind of algorithm is to avoid mutation completely;设计这种算法最简单的方法就是完全避免变异; use tuples instead of lists.使用元组而不是列表。 This way, every time you add on another element to the tuple, you are creating a new tuple instead of changing the existing one.这样,每次向元组添加另一个元素时,您都是在创建一个新元组,而不是更改现有元组。

Note that this is quite similar to your first solution using curr_subset + [num] ;请注意,这与您使用curr_subset + [num]第一个解决方案非常相似; the + operation creates a new list, unlike append which changes the state of an existing list. +操作创建一个新列表,不像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

Example:例子:

>>> 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