我正在编写一个小函数来查找数字列表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循环替换列表理解。有什么理由?我认为列表理解总是更好。)
答案 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_ith
和with_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深度列表理解与追加比较。
因此,对于您的特定问题,我猜列表理解速度更快。