如何解决获取列表子集的改进方法 方案一:返回一个生成器作为输出解决方案2:也接受生成器或迭代器作为输入衡量绩效
以下是我获取列表子集的方式:
def subsets(s):
if not s: return [[]]
rest = subsets(s[1:])
return rest + map(lambda x: [s[0]] + x,rest)
x = sorted(subsets([1,2,3]),key=lambda x: (len(x),x))
# [[],[1],[2],[3],[1,2],3],[2,3]]
它有效,但有点慢。我想知道在 python 中获取所有子集的更好的简单算法可能是什么(不只是使用像 itertools.combinations
这样的东西)。
解决方法
除非您确实需要,否则避免将子集列表存储在内存中会更有效率。
方案一:返回一个生成器作为输出
此代码通过递归生成器函数实现:
def subsets(s):
if not s:
yield []
else:
for x in subsets(s[1:]):
yield x
yield [s[0]] + x
这个函数只会在调用者需要的时候懒惰地做它的工作,并且永远不会在内存中存储更多的调用堆栈。
yield
子句中的第一个 else
用于生成 subsets(s[1:])
的所有元素不变,即 s
的那些子集不包含 {{1} },而第二个产生包含 s[0]
的所有 s
子集。
当然,如果你想让这个列表排序,当你对结果调用 s[0]
时会损失效率,它会在内存中构建列表。
解决方案2:也接受生成器或迭代器作为输入
解决方案 1 的缺点是它仅适用于可索引的 sorted()
类型,例如列表和元组,其中 s
和 s[0]
定义良好。
如果我们想让 s[1:]
更通用并接受任何可迭代的(包括迭代器),我们必须避免这种语法并使用 subsets
代替:
next()
衡量绩效
有了这一切,我想知道这些解决方案有多大的不同,所以我运行了一些基准测试。
我比较了您的初始代码,我称之为 def subsets(s):
iterator = iter(s)
try:
item = next(iterator)
except StopIteration:
# s in empty,bottom out of the recursion
yield []
else:
# s is not empty,recurse and expand
for x in subsets(iterator):
yield x
yield [item] + x
,我的解决方案 1,subsets_lists
在这里,我的解决方案 2,subsets_generator
。为完整起见,我添加了 powerset
function suggested in itertools。
结果:
大小为 10 的列表的所有子集,每次调用重复 10 次:
subsets_gen_iterable
大小为 20 的列表的所有子集,每次调用重复 10 次:
Code block 'l = subsets_lists(range(10)) repeats=10' took: 5.58828 ms
Code block 'g = subsets_generator(range(10)) repeats=10' took: 0.05693 ms
Code block 'g = subsets_gen_iterable(range(10)) repeats=10' took: 0.01316 ms
Code block 'l = list(subsets_generator(range(10))) repeats=10' took: 4.86464 ms
Code block 'l = list(subsets_gen_iterable(range(10))) repeats=10' took: 3.76597 ms
Code block 'l = list(powerset(range(10))) size=10 repeats=10' took: 1.11228 ms
观察:
- 您可以看到调用这两个生成器函数并没有做太多工作,除非将生成器输入到使用它的东西中,例如
Code block 'l = subsets_lists(range(20)) repeats=10' took: 12144.99487 ms Code block 'g = subsets_generator(range(20)) repeats=10' took: 65.18992 ms Code block 'g = subsets_gen_iterable(range(20)) repeats=10' took: 0.01784 ms Code block 'l = list(subsets_generator(range(20))) repeats=10' took: 10859.75128 ms Code block 'l = list(subsets_gen_iterable(range(20))) repeats=10' took: 10074.26618 ms Code block 'l = list(powerset(range(20))) size=20 repeats=10' took: 2336.81373 ms
构造函数。尽管list()
似乎确实比subsets_generator
预先做了很多工作。 - 我的两个解决方案对您的代码产生了适度的速度提升
- 基于
subsets_gen_iterable
的解决方案仍然要快得多。
实际基准测试代码:
itertools
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。