生成重复的组合列表......扭曲

时间:2013-01-31 01:21:26

标签: python combinatorics

我正在尝试生成CHAR_LISTlower范围内的动态字符集upper的每个组合。我在下面粘贴的代码有效,但我觉得它非常低效,我想尽可能快地制作它。

例如,如果我想在“aab”和“zzz”之间生成一个只有小写字母字符的列表,则会输出:['aab', 'aac', 'aad', ..., 'zzy', 'zzz']

如果有什么我不明的请发表评论,我会澄清。谢谢!

我现在正在做什么。

def generate_list(lower, upper):
    result = [lower]
    while lower != upper:
        if CHAR_LIST.index(lower[len(lower)-1:len(lower)]) + 1 < len(CHAR_LIST):
            lower = lower[:len(lower)-1] + CHAR_LIST[CHAR_LIST.index(lower[len(lower)-1:len(lower)]) + 1]
        else:
            new_lower = ""
            new_dig = 0
            inc_next = True
            for i in lower[::-1]:
                if i == CHAR_LIST[len(CHAR_LIST)-1] and inc_next:
                    new_lower += CHAR_LIST[0]
                    new_dig += 1
                else:
                    if inc_next:
                        inc_next = False
                        new_lower += CHAR_LIST[CHAR_LIST.index(i) + 1]
                    else:
                        new_lower += i
            if new_dig == len(lower):
                lower = str(CHAR_LIST[0])*int(len(lower)+1)
            else:
                lower = new_lower[::-1]
        result.append(lower)
    return result
编辑:我忘了添加,因为这是挑战的一部分,它还必须计算一个具有不同长度的开始和结束点的列表。例如,它也必须计算“a”和“zzz”之间的列表。对于修订后期感到抱歉,感谢目前为止的创意答案:)

3 个答案:

答案 0 :(得分:2)

import itertools
CHAR_LIST = list("abcdefghijklmnopqrstuvwxyz")

def generate_list(lower, upper):
    lower, upper = tuple(lower), tuple(upper)
    return ["".join(e) for e in itertools.product(CHAR_LIST, repeat=len(lower))
                  if e >= lower and e <= upper]

print generate_list("aab", "zzz")

答案 1 :(得分:2)

在我看来itertools.product就是你想要的:

from string import ascii_lowercase
from itertools import product
all_combos = (''.join(x) for x in product(ascii_lowercase,repeat=3))
filtered = (s for s in all_combos if s > 'aaa')

为了与itertools的精彩保持一致,我在这里使用了生成器,但你可以很容易地将第二个生成器用于列表理解以获得列表 - 而且我也相信很容易看到你如何能够转换它进入lowerupper边界的函数,对filtered生成器表达式进行非常简单的修改(已经包含下限;-) ...

答案 2 :(得分:2)

我花了很长时间才了解你的代码是如何工作的,因为你做的工作比你需要的要多得多。这是同一算法的积极“pythonized”版本,我怀疑它比现在的速度快得多:

def generate_strings(value, bound, alpha):
    yield value
    while value != bound: # run until we have reached bound
        for i, c in enumerate(reversed(value)): # loop over the string in reverse
            if c != alpha[-1]: # can this character be incremented?
                # construct an incremented value
                value = value[:-1-i] + alpha[alpha.index(c)+1] + alpha[0]*i
                break # exit the for loop
        else: # run only if for loop ended without breaking
            value = alpha[0]*(len(value) + 1) # make a longer string
        yield value

该函数是一个生成器,所以如果你想要一个列表结果,把它传递给列表构造函数,就像在这个例子中输出:

>>> print(list(generate_strings("b", "cc", "abcd")))
['b', 'c', 'd', 'aa', 'ab', 'ac', 'ad', 'ba', 'bb', 'bc', 'bd', 'ca', 'cb', 'cc']

我将字符序列作为函数的参数,而不是使用全局变量。 bound参数也可以是None或其他一些无意义的值来获取无限生成器(但不要将其传递给list()而不缩短它!)。以下是这两个功能的示例:

>>> from itertools import islice
>>> from string import ascii_lowercase
>>>
>>> print(list(islice(generate_strings("xyzzy", None, ascii_lowercase), 5)))
['xyzzy', 'xyzzz', 'xzaaa', 'xzaab', 'xzaac']

如果你是Python的新手,代码中有一些事情可能并不明显。

首先,我在字符串中使用了很多负面索引。这从右侧算起,从-1开始作为最右边的字符。仅这一点就可以简化你的代码(你有很多x[len(x)-1])。

接下来,我使用enumeratereversed内置函数从右到左循环遍历字符串,跟踪我循环的字符数。我认为这与您使用inew_dig值所做的事情有关,但我认为它更清晰。 Python中有许多有用的内置生成器!

最后,我使用break语句提前退出for循环,使用else块来处理我们在没有break的情况下结束的情况。当我第一次了解它时,循环上的这种else对我来说似乎毫无用处,但在这样的情况下它确实很方便,其中循环的大部分运行将导致break声明被击中。