我可以用蛮力方法完成这个任务,但是我希望有一些聪明的编码方式,或者已有的函数,或者我没有意识到的方法...
下面是我需要的一些数字示例:
00000000001111110000
11111100000000000000
01010101010100000000
10101010101000000000
00100100100100100100
完全排列。但结果必须仅包含六个 1。不多,不少。64 或 32 位最理想。如果这能够给出答案,则 16 位也可行。
我可以用蛮力方法完成这个任务,但是我希望有一些聪明的编码方式,或者已有的函数,或者我没有意识到的方法...
下面是我需要的一些数字示例:
00000000001111110000
11111100000000000000
01010101010100000000
10101010101000000000
00100100100100100100
完全排列。但结果必须仅包含六个 1。不多,不少。64 或 32 位最理想。如果这能够给出答案,则 16 位也可行。
>>> ["".join(v) for v in set(itertools.permutations(["1"]*2+["0"]*3))]
['11000', '01001', '00101', '00011', '10010', '01100', '01010', '10001', '00110', '10100']
>>> [int("".join(v), 16) for v in set(itertools.permutations(["1"]*2+["0"]*3))]
[69632, 4097, 257, 17, 65552, 4352, 4112, 65537, 272, 65792]
如果你想要一个由6个1和26个0组成的32位二进制数,可以使用以下代码:
>>> [int("".join(v), 16) for v in set(itertools.permutations(["1"]*6+["0"]*26))]
但是这个计算需要超级计算机来处理(32!= 263130836933693530167218012160000000)
好的解决方案
因此,更聪明的做法是使用 combinations(组合),可能像这样:
import itertools
num_bits = 32
num_ones = 6
lst = [
f"{sum([2**vv for vv in v]):b}".zfill(num_bits)
for v in list(itertools.combinations(range(num_bits), num_ones))
]
print(len(lst))
这告诉我们,在32位数字的整个光谱中,有 906192
个数字包含6个1。
致谢:
感谢 @Mark Dickinson 指出使用排列
是不可行的,并建议使用组合
。
很抱歉,我不是Python编程人员,因此无法为您发布有效的代码。相反,我可以提供C++代码...
如果您看一下您的问题,您设置了6个位和许多零...所以我会通过6个嵌套的for循环来计算所有可能的1s
位置并设置位...
类似这样:
for (i0= 0;i0<32-5;i0++)
for (i1=i0+1;i1<32-4;i1++)
for (i2=i1+1;i2<32-3;i2++)
for (i3=i2+1;i3<32-2;i3++)
for (i4=i3+1;i4<32-1;i4++)
for (i5=i4+1;i5<32-0;i5++)
// here i0,...,i5 marks the set bits positions
O(2^32)
变成小于 `~O(26.25.24.23.22.21/16)`,你不能比这更快,因为那意味着你会错过有效的解决方案...int generate()
{
const int n1=6; // number of set bits
const int n=32; // number of bits
char x[n+2]; // output number string
int i[n1],j,cnt; // nested for loops iterator variables and found solutions count
for (j=0;j<n;j++) x[j]='0'; x[j]='b'; j++; x[j]=0; // x = 0
for (j=0;j<n1;j++){ i[j]=j; x[i[j]]='1'; } // first solution
for (cnt=0;;)
{
// Form1->mm_log->Lines->Add(x); // here x is the valid answer to print
cnt++;
for (j=n1-1;j>=0;j--) // this emulates n1 nested for loops
{
x[i[j]]='0'; i[j]++;
if (i[j]<n-n1+j+1){ x[i[j]]='1'; break; }
}
if (j<0) break;
for (j++;j<n1;j++){ i[j]=i[j-1]+1; x[i[j]]='1'; }
}
return cnt; // found valid answers
};
n1=6,n=32
时,我得到了以下输出(不包括数字的打印):cnt = 906192
它在 AMD A8-5500 3.2GHz(win7 x64 32bit应用程序无线程)上以 4.246 毫秒
完成,对我来说足够快了...
请注意,一旦您开始在某个地方输出数字,速度将急剧下降。特别是如果您输出到控制台或其他地方...最好以某种方式缓冲输出,例如一次输出 1024 个字符串数字等等...但正如我之前提到的,我不是 Python 程序员,所以环境可能已经处理了这个问题...
除此之外,一旦您玩弄变量 n1,n
,您可以使用零而不是一来进行相同的操作,并使用更快的方法(如果零比一少,则使用嵌套的 for 循环来标记零)
如果想要的解决方案数字是作为数字而不是字符串,则可以重写代码,使 i[]
或 i0,..i5
包含位掩码,而不是位位置...而不是增加/减少,您只需左/右移即可...不再需要 x
数组,因为数字将是 x = i0|...|i5
...
bitCount = 32
oneCount = 6
maxBit = 1<<(bitCount-1)
ones = [1<<b for b in reversed(range(oneCount)) ] # start with bits on low end
ones[0] >>= 1 # shift back 1st one because it will be incremented at start of loop
index = 0
result = []
while index < len(ones):
ones[index] <<= 1 # shift one at current position
if index == 0:
number = sum(ones) # build output number
result.append(number)
if ones[index] == maxBit:
index += 1 # go to next position when bit reaches max
elif index > 0:
index -= 1 # return to previous position
ones[index] = ones[index+1] # and prepare it to move up (relative to next)
def genOneBits(bitcount=32,onecount=6):
for bitPos in range(onecount-1,bitcount):
value = 1<<bitPos
if onecount == 1: yield value; continue
for otherBits in genOneBits(bitPos,onecount-1):
yield value + otherBits
result = [ n for n in genOneBits(32,6) ]
def numOneBits(bitcount=32,onecount=6):
def factorial(X): return 1 if X < 2 else X * factorial(X-1)
return factorial(bitcount)//factorial(onecount)//factorial(bitcount-onecount)
def nthOneBits(N,bitcount=32,onecount=6):
if onecount == 1: return 1<<N
bitPos = 0
while bitPos<=bitcount-onecount:
group = numOneBits(bitcount-bitPos-1,onecount-1)
if N < group: break
N -= group
bitPos += 1
if bitPos>bitcount-onecount: return None
result = 1<<bitPos
result |= nthOneBits(N,bitcount-bitPos-1,onecount-1)<<(bitPos+1)
return result
# bit pattern at position 1000:
nthOneBit(1000) # --> 10485799 (00000000101000000000000000100111)
nthOneBits(10000, bitcount=256, onecount=9)
# 77371252457588066994880639
# 100000000000000000000000000000000001000000000000000000000000000000000000000000001111111
def nextOneBits(N=0,bitcount=32,onecount=6):
if N == 0: return (1<<onecount)-1
bitPositions = []
for pos in range(bitcount):
bit = N%2
N //= 2
if bit==1: bitPositions.insert(0,pos)
index = 0
result = None
while index < onecount:
bitPositions[index] += 1
if bitPositions[index] == bitcount:
index += 1
continue
if index == 0:
result = sum( 1<<bp for bp in bitPositions )
break
if index > 0:
index -= 1
bitPositions[index] = bitPositions[index+1]
return result
nthOneBits(12) #--> 131103 00000000000000100000000000011111
nextOneBits(131103) #--> 262175 00000000000001000000000000011111 5.7ns
nthOneBits(13) #--> 262175 00000000000001000000000000011111 49.2ns
您正在处理多重集合的排列。有许多方法可以实现这一点,正如@BPL所指出的那样,高效地完成这项工作并不容易。这里提到了许多很好的方法:具有唯一值的排列。最干净的方法(不确定是否最有效)是使用 sympy
模块中的 multiset_permutations
。
import time
from sympy.utilities.iterables import multiset_permutations
t = time.process_time()
## Credit to @BPL for the general setup
multiPerms = ["".join(v) for v in multiset_permutations(["1"]*6+["0"]*26)]
elapsed_time = time.process_time() - t
print(elapsed_time)
len(multiPerms)
906192
itertools.permutations
调用将会遍历 32! = 263130836933693530167218012160000000 种排列方式。即使我们乐观地假设我们可以处理每纳秒一个排列,它仍然需要超过 8 百万亿年才能完成。 - Mark Dickinsonitertools.combinations
可以胜任。使用itertools.combinations(range(32), 6)
,并将返回的每个元组解释为给出1的位置。 - Mark Dickinson