在python中生成唯一的二进制排列

Posted

技术标签:

【中文标题】在python中生成唯一的二进制排列【英文标题】:Generate unique binary permutations in python 【发布时间】:2018-11-08 14:15:45 【问题描述】:

请问,我怎样才能获得所有这些二进制排列,但在 Python 中不重复?

 a = list(itertools.permutations([1, 1, 0, 0]))
 for i in range(len(a)):
     print a[i]

    (1, 1, 0, 0)
    (1, 1, 0, 0)
    (1, 0, 1, 0)
    ...

如果它大致有效,那就太好了,因为我必须用这样一个包含 30 个元素的列表来做到这一点。

【问题讨论】:

即您要求“长度为 N 且恰好设置 M 位的二进制数” ***.com/questions/1851134/… 具有重复值的排列的想法对于重复的可辨别性是模棱两可的。正如the docs 解释的那样,itertools.permutations 选择总是可辨别的,因为那是最难的。因为简单的——你想要的——就是……@AnttiHaapala 所说的。 @AlexHall 但它是算法,而不是 Python。叹息。 相关:permutations with unique values 【参考方案1】:

正如@Antti 在评论中所说,这相当于在输入列表的位置查找combinations,这些位置确定输出中的哪些位是 1。

from itertools import combinations

def binary_permutations(lst):
    for comb in combinations(range(len(lst)), lst.count(1)):
        result = [0] * len(lst)
        for i in comb:
            result[i] = 1
        yield result

for perm in binary_permutations([1, 1, 0, 0]):
    print(perm)

输出:

[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]

【讨论】:

最好不要太从字面上理解 MCVE,例如,如果代码仍然可以通过像 [1, 1, 0, 0, 'potato'] 这样的输入来做一些明智的事情。【参考方案2】:

这是来自the accepted answer to the generic algorithm question 的算法,适用于 Python 3(应该适用于 Python 2.7+)。函数generate(start, n_bits) 将按字典顺序生成从start 开始的所有n 位 整数。

def generate(start, n_bits):
    # no ones to permute...
    if start == 0:
        yield 0
        return

    # fastest count of 1s in the input value!!
    n_ones = bin(start).count('1')

    # the minimum value to wrap to when maxv is reached;
    # all ones in LSB positions
    minv = 2 ** n_ones - 1

    # this one is just min value shifted left by number of zeroes
    maxv = minv << (n_bits - n_ones)

    # initialize the iteration value
    v = start

    while True:
        yield v

        # the bit permutation doesn't wrap after maxv by itself, so,
        if v == maxv:
            v = minv

        else:
            t = ((v | ((v - 1))) + 1)
            v = t | (((((t & -t)) // ((v & -v))) >> 1) - 1)

        # full circle yet?
        if v == start:
            break

for i in generate(12, 4):
    print(':04b'.format(i))

打印

1100
0011
0101
0110
1001
1010

如果生成了列表输出,则可以对其进行修饰:

def generate_list(start):
    n_bits = len(start)
    start_int = int(''.join(map(str, start)), 2)

    # old and new-style formatting in one
    binarifier = (':0%db' % n_bits).format

    for i in generate(start_int, n_bits): 
        yield [int(j) for j in binarifier(i)]

for i in generate_list([1, 1, 0, 0]):
    print(i)

打印

[1, 1, 0, 0]
[0, 0, 1, 1]
[0, 1, 0, 1]
[0, 1, 1, 0]
[1, 0, 0, 1]
[1, 0, 1, 0]

这个算法的好处是你可以在任何时候恢复它。如果您找到一种计算良好起点的方法,也可以进行并行化。 数字应该比列表更紧凑,所以如果可能的话你可以使用它们。

【讨论】:

这很有趣,我很高兴你做到了,但我觉得使用它会感到不舒服,因为我不知道它为什么会起作用。消息来源没有解释原因,所以在某种程度上我只是盲目地相信它有效。很高兴看到它针对其他方法之一进行了彻底的测试。性能比较也有助于证明这一点。【参考方案3】:

您正在尝试做的是选择两个位置元素将是1

代码

from itertools import combinations

def bit_patterns(size, ones):
    for pos in map(set, combinations(range(size), ones)):
        yield [int(i in pos) for i in range(size)]

输出

>>> print(*bit_patterns(4, 2), sep='\n')
[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]

另类

一个有趣的替代方法是将所需的输出视为只有两个的二进制表示。我们可以使用这个定义来得到你想要的输出。

from itertools import combinations

def bit_patterns(size, ones):
    for t in combinations([1 << i for i in range(size)], ones):
        yield [int(n) for n in f'sum(t):0sizeb']

【讨论】:

@AlexHall 很简单:您只需计算位数,生成 2 到 N - 1 的幂;然后数个数;相应地更改 2 和 4【参考方案4】:

这是一个递归解决方案:

def bin_combs_iter(ones, zeros):
    if not zeros:
        yield [1] * ones
    elif not ones:
        yield [0] * zeros
    else:
        for x in bin_combs_iter(ones - 1, zeros):
            x.append(1)
            yield x
        for x in bin_combs_iter(ones, zeros - 1):
            x.append(0)
            yield x


def bin_combs(ones, zeros):
    return list(bin_combs_iter(ones, zeros))

【讨论】:

你需要递归调用bin_combs_iter,而不是bin_combs,否则你会为大量输入使用大量内存。

以上是关于在python中生成唯一的二进制排列的主要内容,如果未能解决你的问题,请参考以下文章

使用特定规则在 Python 中生成排列

Java中生成的UUID(全局唯一标识符-----(唯一)-------)

在 haskell 中,如何从十进制数中生成二进制数字?

如何在 Python 中生成唯一 ID? [复制]

如何在 Openssl 中生成十六进制输出?

如何在 python 中生成唯一的身份验证令牌?