python - 生成所有长度为 X 且以 '0' 结尾且没有相邻的 '1' 的字符串

标签 python

我需要生成满足以下两个规则的特定长度 X 的所有可能字符串:

  1. 必须以'0'结尾
  2. 不能有两个或更多个相邻的'1'

例如,当 X = 4 时,所有“合法”字符串都是 [0000, 0010, 0100, 1000, 1010]。

我已经写了一段递归代码,它只是将新找到的字符串附加到列表中。

def generate(pre0, pre1, cur_len, max_len, container = []):
    if (cur_len == max_len-1):
        container.append("".join([pre0, pre1, "0"]))
        return

    if (pre1 == '1'):
        cur_char = '0'
        generate(pre0+pre1, cur_char, cur_len+1, max_len, container)
    else:   
        cur_char = '0'
        generate(pre0+pre1, cur_char, cur_len+1, max_len, container)
        cur_char = '1'
        generate(pre0+pre1, cur_char, cur_len+1, max_len, container)

if __name__ == "__main__": 
    container = []
    _generate("", "", 0, 4, container)
    print container

但是,当 X 达到 100+ 时,由于内存复杂性,此方法将不起作用。我不熟悉 Python 中的生成器方法,所以这里的任何人都可以帮我弄清楚如何将它重写成生成器吗?非常感谢!

附言我正在研究 Project Euler 问题,这不是家庭作业。

更新 1:

感谢前 3 个答案,我使用的是 Python 2.7,可以切换到 python 3.4。我要求生成器的原因是我什至无法在我的内存中保存最终结果列表。一个快速的数学证明将表明长度 X 有 Fibonacci(X) 个可能的字符串,这意味着我必须真正使用生成器并即时过滤结果。

最佳答案

Lame 基于字符串的测试“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 ,因此您需要循环和生成器。

//edit: 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 秒 (!)。

shx2 算法在 C++ 中的实现非常重要,因为递归方法会导致堆栈溢出(哈哈),并且没有yield。见:

但如果您想要原始速度,那就别无选择。

关于python - 生成所有长度为 X 且以 '0' 结尾且没有相邻的 '1' 的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32171138/

相关文章:

python - 如何使用 Python 在 XML 中搜索和替换?

python - 如何打包要在Windows10上的python3.8 32bits上运行的库的轮子?

python - 屏蔽数组索引问题

python - 在代码中兼容地使用字符串和类字节对象以在 Python 2 和 3 中运行

python - 在 valgrind 下运行 python 显示很多内存错误是否正常?

python - 为什么 myVar = strings.Fields(scanner.Text()) 比 python 中的类似操作花费更多的时间?

python - 如何转义 Python 字符串中的任何特殊 shell 字符?

python - AWS Lambda 出现 Python 2.7 错误

python - Django 通过电子邮件发送每次对logging.error的调用

python - 可以用 python 编写 ActionScript 程序吗?