基于蹩脚字符串的测试“11”是否包含在格式化字符串中,如果不包含则产生(对于每个偶数,直到 2^maxlen):
def gen(maxlen):
pattern = "{{:0{}b}}".format(maxlen)
for i in range(0, 2**maxlen, 2):
s = pattern.format(i) # not ideal, because we always format to test for "11"
if "11" not in s:
yield s
高级数学方法 (M xor M * 2 = M * 3):
def gen(maxlen):
pattern = "{{:0{}b}}".format(maxlen)
for i in range(0, 2**maxlen, 2):
if i ^ i*2 == i*3:
yield pattern.format(i)
这是 6 种不同实现(Python 3!)的基准:
from time import clock
from itertools import product
def math_range(maxlen):
pattern = "{{:0{}b}}".format(maxlen)
for i in range(0, 2**maxlen, 2):
if i ^ i*2 == i*3:
yield pattern.format(i)
def math_while(maxlen):
pattern = "{{:0{}b}}".format(maxlen)
maxnum = 2**maxlen - 1
i = 0
while True:
if i ^ i*2 == i*3:
yield pattern.format(i)
if i >= maxnum:
break
i += 2
def itertools_generator(max_len):
return filter(lambda i: '11' not in i, (''.join(i) + '0' for i in product('01', repeat=max_len-1)))
def itertools_list(maxlen):
return list(filter(lambda i: '11' not in i, (''.join(i) + '0' for i in product('01', repeat=maxlen-1))))
def string_based(maxlen):
pattern = "{{:0{}b}}".format(maxlen)
for i in range(0, 2**maxlen, 2):
s = pattern.format(i)
if "11" not in s:
yield s
def generate(pre0, pre1, cur_len, max_len):
if (cur_len == max_len-1):
yield "".join((pre0, pre1, "0"))
return
if (pre1 == '1'):
yield from generate(pre0+pre1, "0", cur_len+1, max_len)
else:
yield from generate(pre0+pre1, "0", cur_len+1, max_len)
yield from generate(pre0+pre1, "1", cur_len+1, max_len)
def string_based_smart(val):
yield from generate("", "", 0, val)
def benchmark(val, *funcs):
for i, func in enumerate(funcs, 1):
start = clock()
for g in func(val):
g
print("{}. {:6.2f} - {}".format(i, clock()-start, func.__name__))
benchmark(24, string_based_smart, math_range, math_while, itertools_generator, itertools_list, string_based)
字符串长度 = 24(以秒为单位)的一些数字:
1. 0.24 - string_based_smart
2. 1.73 - math_range
3. 2.59 - math_while
4. 6.95 - itertools_generator
5. 6.78 - itertools_list
6. 6.45 - string_based
shx2 的算法显然是赢家,其次是数学。如果您比较两种数学方法的结果(注意:范围也是生成器),Pythonic 代码会产生很大的不同。
值得注意:itertools_* 函数的执行速度几乎一样慢,但itertools_list 需要更多内存来存储列表(在我的测试中约为 6 MB 峰值)。所有其他基于生成器的解决方案都具有最小的内存占用,因为它们只需要存储当前状态而不是整个结果。
显示的函数都不会破坏堆栈,因为它们不使用实际的递归。 Python does not optimize tail recursion,因此您需要循环和生成器。
//编辑: math_range 的幼稚 C++ 实现(MSVS 2013):
#include "stdafx.h"
#include <iostream>
#include <bitset>
#include <ctime>
#include <fstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const unsigned __int32 maxlen = 24;
const unsigned __int32 maxnum = 2 << (maxlen - 1);
clock_t begin = clock();
ofstream out;
out.open("log.txt");
if (!out.is_open()){
cout << "Can't write to target";
return 1;
}
for (unsigned __int32 i = 0; i < maxnum; i+=2){
if ((i ^ i * 2) == i * 3){
out << std::bitset<maxlen>(i) << "\n"; // dont use std::endl!
}
}
out.close();
clock_t end = clock();
double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
cout << elapsed_secs << endl;
return 0;
}
maxlen = 24 (/Ox) 需要 0.08 秒 (!)。
在 C++ 中实现 shx2 的算法并非易事,因为递归的方法会导致堆栈溢出(哈哈),而且没有 yield。见:
但如果你想要原始速度,那就没有办法了。