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

时间:2015-08-23 20:22:29

标签: python

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

  1. 必须以'0'结尾
  2. 不能有两个或更多相邻的'1'
  3. 例如,当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中的生成器方法,所以这里的任何人都可以帮我弄清楚如何将它重新编写成生成器吗?非常感谢!

    P.S。我正在研究Project Euler问题,这不是功课。

    更新1:

    感谢前三个答案,我使用的是Python 2.7并且可以切换到python 3.4。我要求生成器的原因是我不可能只保留在我的内存中的最终结果列表。一个快速的数学证明将显示长度为X的Fibonacci(X)可能的字符串,这意味着我必须真正使用生成器并在运行中过滤结果。

2 个答案:

答案 0 :(得分:2)

您可以filter使用itertools.product

def generate(max_len):
    return list(filter(lambda i: '11' not in i, (''.join(i) + '0' for i in itertools.product('01', repeat=max_len-1))))

这使用生成器整个时间,直到返回最终创建list。 <{1}}将在filter生成的每个字符串上起作用。

itertools.product

修改要将此功能用作生成器表达式,只需删除>>> generate(5) ['00000', '00010', '00100', '01000', '01010', '10000', '10010', '10100'] 并将list切换为itertools.ifilter

filter

答案 1 :(得分:2)

Lame string-based测试&#34; 11&#34;包含在格式化的字符串中,如果不是则产生(对于每个偶数,最多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。参见:

但是如果你想要原始速度,那么就没有办法了。