在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中生成唯一的二进制排列的主要内容,如果未能解决你的问题,请参考以下文章