这一组的所有设置是一行函数做什么的?

时间:2016-11-23 15:36:52

标签: python lambda set list-comprehension

我在python wiki上找到了这一行函数,它创建了一组可以从作为参数传递的列表中创建的集合。

f = lambda x: [[y for j, y in enumerate(set(x)) if (i >> j) & 1] for i in range(2**len(set(x)))]

有人可以解释一下这个功能是如何运作的吗?

2 个答案:

答案 0 :(得分:1)

要构建powerset,迭代2**len(set(x))会给出集合的所有二进制组合。

range(2**len(set(x))) == [00000, 00001, 00010, ..., 11110, 11111]

现在您只需要测试该位是否在i中设置,以确定是否需要将其包含在集合中,例如:

>>> i = 0b10010
>>> [y for j, y in enumerate(range(5)) if (i >> j) & 1]
[1, 4]

虽然我不确定每次迭代调用set(x)的效率如何。有一个小黑客可以避免这种情况:

f = lambda x: [[y for j, y in enumerate(s) if (i >> j) & 1] for s in [set(x)] for i in range(2**len(s))]

使用itertools的其他几种形式:

import itertools as it
f1 = lambda x: [list(it.compress(s, i)) for s in [set(x)] for i in it.product((0,1), repeat=len(s))]
f2 = lambda x: list(it.chain.from_iterable(it.combinations(set(x), r) for r in range(len(set(x))+1)))

注意:如果删除list(),则最后一个可以返回一个可迭代vs列表,具体取决于用例可以节省一些内存。

查看25个随机数0-50列表的一些时间:

%%timeit binary: 1 loop, best of 3: 20.1 s per loop
%%timeit binary+hack: 1 loop, best of 3: 17.9 s per loop
%%timeit compress/product: 1 loop, best of 3: 5.27 s per loop
%%timeit chain/combinations: 1 loop, best of 3: 659 ms per loop

答案 1 :(得分:1)

让我们重写一下,然后逐步分解:

f = lambda x: [[y for j, y in enumerate(set(x)) if (i >> j) & 1] for i in range(2**len(set(x)))]

相当于:

def f(x):
    n = len(set(x))
    sets = []
    for i in range(n): # all combinations of members of the set in binary
        set_i = []
        for j, y in enumerate(set(x)):
            if (i>>j) & 1: #check if bit nr j is set
                set_x.append(y)
        sets.append(set_i)
    return sets

对于像[1,2,3,4]这样的输入列表,会发生以下情况:

n=4
range(2**n)=[0,1,2,3...15]

,二进制是:

0,1,10,11,100...1110,1111

枚举使用其索引生成y的元组,因此在我们的例子中:

[(0,1),(1,2),(2,3),(3,4)]

(i>>j) & 1部分可能需要一些解释。 (i>>j)i j个数字向右移动,例如十进制:4>>2=1或二进制:100>>2=001&是逐位and运算符。这将检查两个操作数的每一位,如果它们是1并将结果作为数字返回,就像过滤器一样:10111 & 11001 = 10101

在我们的示例中,它检查位置j的位是否为1.如果是,则将相应的值添加到结果列表中。这样,组合的二进制映射将转换为列表列表,并返回。