我输入字符串的长度(10或30或......)并输入"坏字符串" " 101"," 111",并计算不包含错误字符串的排列数。
我用itertools尝试了这个,它适用于10-15的字符串,但对于30,它超过一万亿的结果并且无法高效运行。我认为该方法是一次构建一个字符串的字符串,但我无法弄清楚算法来做到这一点。
import itertools
v1="111"
v2="101"
def perms(n):
for i in range(2**n):
s=bin(i)[2:]
s="0"*(n-len(s)) + s
if v1 not in s and v2 not in s:
print(s)
yield s
print len(list(perms(10)))
答案 0 :(得分:1)
您可以逐位构建字符串,立即避免错误路径。这是一个迭代版本:
def build(n):
strings = ['']
bad = '111', '101'
for _ in range(n):
strings = [s + bit
for s in strings
for bit in '01'
if not (s + bit).endswith(bad)]
return strings
在一瞬间确认169为10位,大约需要3.4秒才能找到3050位的2550409字符串。
>>> len(build(10))
169
>>> len(build(30))
2550409
还可以使用嵌套生成器来节省内存:
def build(n):
strings = ['']
bad = '111', '101'
for _ in range(n):
strings = (s + bit
for s in strings
for bit in '01'
if not (s + bit).endswith(bad))
return strings
演示,也花了我大约3.4秒:
>>> len(list(build(30)))
2550409
或者,递归版本:
def build(n, prefix=''):
if n == 0:
yield prefix
return
for bit in '01':
if not (prefix + bit).endswith(('111', '101')):
yield from build(n - 1, prefix + bit)
在大约7秒内找到2550409的30位:
>>> len(list(build(30)))
2550409
对于Python 2,将yield from
行替换为:
for bits in build(n - 1, prefix + bit):
yield bits
如果我传递输出列表而不是通过所有级别,我需要大约4.5秒:
def build(n, output, prefix=''):
if n == 0:
output.append(prefix)
return
for bit in '01':
if not (prefix + bit).endswith(('111', '101')):
build(n - 1, output, prefix + bit)
演示:
>>> output = []
>>> build(30, output)
>>> len(output)
2550409
答案 1 :(得分:1)
这是另一种解决方案。它在我的古老机器上比Stefan的代码更快地运行 。
我们使用str.format
方法迭代数字,将它们转换为位串。当我们找到一个带有错误模式的位串时,我们跳到该位位置不包含该模式的第一个数字。因此,如果我们发现在比特串的2**0
比特位置(LSB)结束的坏模式,我们前进1,如果坏模式在2**1
比特位置结束,我们前进2,等等。
这是循环中打印调用的版本,因此我们可以看到发生了什么。
def skip_bad(width, bad=("101", "111")):
n = 0
while n < 1<<width:
s = '{0:0{1}b}'.format(n, width)
for b in bad:
i = s.find(b)
if i != -1:
i = width - i - len(b)
print('skipping', s, i)
n += 1 << i
break
else:
yield s
n += 1
for i in skip_bad(5):
print(i)
<强>输出强>
00000
00001
00010
00011
00100
skipping 00101 0
00110
skipping 00111 0
01000
01001
skipping 01010 1
01100
skipping 01101 0
skipping 01110 1
10000
10001
10010
10011
skipping 10100 2
11000
11001
skipping 11010 1
skipping 11100 2
这是我用来获取评论中给出的时间数据的版本。
def skip_bad(width, bad=("101", "111")):
n = 0
while n < 1<<width:
s = '{0:0{1}b}'.format(n, width)
for b in bad:
i = s.find(b)
if i != -1:
n += 1 << (width - i - len(b))
break
else:
yield s
n += 1
width = 30
print(width)
print(sum(1 for _ in skip_bad(width)))
<强>输出强>
30
2550409