Python - 创建匹配对象输出最大物理内存的itertools.permutations列表

时间:2013-09-23 18:57:44

标签: python pattern-matching permutation

这是我的代码,附带一些注释:

import re
import itertools

nouns = ['bacon', 'cheese', 'eggs', 'milk', 'houses', 'dog']
CC = ['and', 'or']

def search_and_replace(text):
    noun_patt = r'\b(' + '|'.join(nouns) + r')\b'
    CC_patt = r'\b(' + '|'.join(CC) + r')\b'
    patt = r'({0},? )+{1} {0}'.format(noun_patt, CC_patt)

    searched = re.search(patt, text) 
    phrase = searched.group()
    print "Check re.search match object exists:", phrase # "bacon, eggs, and milk" prints

    perm_phrase = itertools.permutations(phrase)
    print "Check permutated list exists:", perm_phrase # permutation object position in memory prints

    perm_phrase_list = list(perm_phrase)
    print "Permutated object as list:", perm_phrase_list # THIS IS WHERE MEMORY MAXES AND COMPUTER FREEZES!!!
    # So print does not happen.

    return perm_phrase_list

def main():
    text = "I like bacon, eggs, and milk"
    print search_and_replace(text)


if __name__ == '__main__':
    main()

正如您在代码中的注释中所看到的,代码行perm_phrase_list = list(perm_phrase)占用了很多物理内存,而我的计算机却冻结了。 (我在每行代码后打印出来以确定问题出现的位置,我通常不会在函数内打印)。我想知道为什么在尝试从排列创建此列表时会发生这种情况?

如果我可以在此代码中使用排列方法,那将非常方便!否则,我将只需要为列表中的元素重新排序创建等效的东西。

我正在使用Python 2.7

3 个答案:

答案 0 :(得分:3)

您正在向itertools.permutations()提供字符串;这意味着它将为字符的所有可能组合生成排列:

>>> phrase = re.search(patt, text).group()
>>> phrase
'bacon, eggs, and milk'
>>> next(itertools.permutations(phrase))
('b', 'a', 'c', 'o', 'n', ',', ' ', 'e', 'g', 'g', 's', ',', ' ', 'a', 'n', 'd', ' ', 'm', 'i', 'l', 'k')

你的短语是21个字符长,结果是21个! (阶乘)== 51090942171709440000不同的排列;每个21个字符的元组。

一个这样的元组,在我的64位Mac上,共有21 * 38 + 224个字节= 1022个字节的内存。字符是实际的,所以你真的只需要每个元组的内存,并且可以忽略字符的768个字节。那就是51090942171709440000 * 224字节差不多是10 zebibytes

这是一个很好的记忆。

您可能不希望生成该短语的所有可能的21个字符的排列。您需要重新考虑要对方法执行的操作,生成更少的输出,并且只逐个循环生成的组合,而不是尝试将它们全部扩展为列表对象。

我怀疑你想为任何匹配的单词创建排列,但是你的正则表达式不会为你提供单独匹配的单词。您不能重复捕获组,您需要捕获整个,然后再拆分:

noun_patt = r'\b(?:' + '|'.join(nouns) + r')\b'
CC_patt = r'\b(' + '|'.join(CC) + r')\b'
patt = r'((?:{0},? )+){1} ({0})'.format(noun_patt, CC_patt)

(?:..)组是非捕获组,以避免混淆我们的结果。

现在提供两个捕获组,一个使用逗号分隔的名词,另一个使用最后一个名词。在空格和逗号上拆分第一个:

searched = re.search(patt, text) 
nouns = filter(None, re.split(r',\s*', searched.group(1))) + [searched.group(3)]

现在你可以置换那些名词:

for comb in itertools.permutations(nouns):
    # do something with this specific permutation

因为你的样本只有3个名词,所以6个排列可以安全地变成一个列表:

>>> nouns
['bacon', 'eggs', 'milk']
>>> list(itertools.permutations(nouns))
[('bacon', 'eggs', 'milk'), ('bacon', 'milk', 'eggs'), ('eggs', 'bacon', 'milk'), ('eggs', 'milk', 'bacon'), ('milk', 'bacon', 'eggs'), ('milk', 'eggs', 'bacon')]

我们或许可以将这些重新组合成句子:

>>> cc = searched.group(2)
>>> for comb in itertools.permutations(nouns):
...     print ', '.join(comb[:-1]), cc, comb[-1]
... 
bacon, eggs and milk
bacon, milk and eggs
eggs, bacon and milk
eggs, milk and bacon
milk, bacon and eggs
milk, eggs and bacon

答案 1 :(得分:1)

代码行perm_phrase_list = list(perm_phrase)将尝试构建列表。如果它非常大,可能需要大量内存,所以你不应该这样做。要“转储”结果,你应该迭代生成器:

for item in perm_phrase: print item #doesn't build the list

答案 2 :(得分:1)

首先,您没有任何理由将所有值存储在列表中;您可以像遍历列表一样轻松迭代迭代器。所以,只需返回perm_phrase。如果您只想打印出值,请写下以下内容:

def main():
    text = "I like bacon, eggs, and milk"
    for perm in search_and_replace(text):
        print perm

显然,您可以根据需要格式化内容,包括添加括号和逗号以及打印repr(perm)以使其看起来与列表完全相同。

通过遍历迭代器,您只能一次生成一个值,而不是一次生成所有值,因此没有内存存储问题。 (您也可以通过“管道化”每个排列的代码来提高速度,提高缓存命中率等。)


但与此同时,你的问题是你想要(4!= 24)个单词的排列,而不是(21!= 51090942171709440000)字符的排列。为此,您需要在某些时候将split字符串转换为单词。例如:

perm_phrase = itertools.permutations(phrase.split())

现在,您可以轻松地将这些全部放入内存中。但你最好还是使用迭代器,除非你有充分的理由让它们同时存在于内存中。