Python中的联合列表/集合

时间:2015-01-03 01:40:12

标签: python set

我正在编写一个小函数来查找数字列表S的所有子集,输出是一个列表列表。

def subsets(S):
    if S is None or len(S) == 0:
        return [[]]

    output_list = []
    sub = [[[], [S[0]]]]
    for i in xrange(1, len(S)):
        without_ith = sub[i - 1]
        with_ith = [element + [S[i]] for element in without_ith]

        # convert to set of tuples, for set union
        without_set = set(tuple(element) for element in without_ith)
        with_set = set(tuple(element) for element in with_ith)
        new_set = without_set | with_set

        # convert back to list of lists
        new = list(list(element) for element in new_set)
        sub.append(new)

    # sort each sublist into non-descending order
    # output_list = [sorted(element) for element in sub[-1]]
    for element in sub[-1]:
        output_list.append(sorted(element))
    return output_list

该帖子的接受答案中描述了该算法:Finding all the subsets of a set

令我烦恼的是从列表列表到元组集的转换,然后执行两组元组的并集,并转换回列表列表。所有这些都发生在每次迭代中。原因是在Python中,集合必须包含可移动的不可变对象,以便与其他集合执行集合操作。但是列表和集合是可变的且不可删除的,需要元组或格式作为这些集合的元素。对于我的代码,我首先改变元素列表并将它们转换为联合的元组,然后转换回列表。我想知道是否有解决方法?它看起来不是很干净和有效。

(还有一个小小的疑问是我评论过的列表理解# output_list = [sorted(element) for element in sub[-1]]。我使用PyCharm并建议用for循环替换列表理解。有什么理由?我认为列表理解总是更好。)

3 个答案:

答案 0 :(得分:1)

我喜欢“返回所有子集”等任务的“计数”方法。假设S是一个没有重复的数字列表:

def subsets(S):   # S is a list of `whatever`
    result = []
    S = sorted(S)  # iff S can't be assumed to be sorted to start
    # S = sorted(set(S)) if duplicates are possible and must be pruned
    for i in range(2**len(S)):
        asubset = []
        for j, x in enumerate(S):
            if i & 1<<j: asubset.append(x)
        result.append(asubset)
    return result

本质上,这利用了N个子集的子集与从0到2**N - 1的二进制整数形式之间的对应关系。

答案 1 :(得分:1)

您的without_ithwith_ith列表之间没有重复的项目,因为前者中的列表从不包含S[i],后者中的列表总是包含set。这意味着在组合它们时不需要使用extend个对象,只需将一个列表连接到另一个列表上就可以了!或者,您可以使用单个列表变量,并使用列表解析def subsets(S): results = [[]] for x in S: results.extend([item + [x] for item in results]) return results

sorted(S)

如果输入列表已排序,则所有子集也将排序。如果输入并不总是按顺序排列并且您需要输出,则直接在S而不是extend循环。子集中的项目将始终按照迭代的顺序显示。

请注意,在{{1}}调用中使用列表推导非常重要,而不是生成器表达式。生成器将继续迭代新添加的项,从而导致无限循环(直到系统内存不足以扩展列表)。

答案 2 :(得分:0)

看起来列表推导比追加项目更快,因为使用它不需要将列表追加功能加载到内存中。检查this great article深度列表理解与追加比较。

因此,对于您的特定问题,我猜列表理解速度更快。