Python 解决字谜时出现内存错误

Python 解决字谜时出现内存错误,python,memory,anagram,Python,Memory,Anagram,我试图解决以下问题: 字谜是一种文字游戏,是将一个单词或短语的字母重新排列以产生一个新单词或短语的结果,使用所有原始字母一次;e、 乐队=马车。使用处的单词列表,编写一个程序,查找共享相同字符且包含最多单词的单词集 即使只有1000字节的文件大小,它也会失败。而且每次创建一个新列表时,Python为什么要将旧列表保留在内存中?我得到下面的错误 l=list(map(''.join, itertools.permutations(i))) 给我: MemoryError 这是我的密码: imp

我试图解决以下问题:

字谜是一种文字游戏,是将一个单词或短语的字母重新排列以产生一个新单词或短语的结果,使用所有原始字母一次;e、 乐队=马车。使用处的单词列表,编写一个程序,查找共享相同字符且包含最多单词的单词集

即使只有1000字节的文件大小,它也会失败。而且每次创建一个新列表时,Python为什么要将旧列表保留在内存中?我得到下面的错误

l=list(map(''.join, itertools.permutations(i)))
给我:

MemoryError
这是我的密码:

import itertools
def anagram():
    f=open('unixdict.txt')
    f2=open('result_anagram.txt','w')
    words = f.read(1000).split('\n')
    for i in words:
        l=[]
        l=list(map(''.join, itertools.permutations(i)))
        l.remove(i)
        for anagram in l:
            if l==i:
                f2.write(i + "\n")
    return True

anagram()
根据建议,将上述代码更改为。但是仍然得到内存错误

import itertools

def anagram():
    f=open('unixdict.txt')
    f2=open('result_anagram.txt','w')
    words = set(line.rstrip('\n') for line in f)
    for i in words:
        l= map(''.join, itertools.permutations(i))
        l =(x for x in l if x!=i)
        for anagram in l:
            if anagram in words:
                f2.write(i + "\n")
    return True

anagram()
记忆者
[在22.2s中完成]

无论你做什么,这个程序都会非常低效

但是你可以修复这个
内存错误
,这样它只需要永远运行而不会失败

首先,注意一个12个字母的单词有479001600个排列。将所有这些存储在内存中需要超过2GB的内存。那么,你如何解决这个问题呢?只是不要把它们都存储在内存中。将迭代器保留为迭代器,而不是创建列表,然后一次只需适应一个迭代器,而不是所有迭代器

这里有一个问题:如果l==i:行中实际使用了该列表。但很明显,这是一个错误。字符串列表不可能等于单个字符串。您还可以将该行替换为
raise TypeError
,此时您只需替换整个循环,就可以更快地失败。:)

我想你想要的是
如果字谜是用词:
。在这种情况下,您不需要
l
,除了
for
循环之外,这意味着您可以安全地将其作为惰性迭代器:

for i in words:
    l = map(''.join, itertools.permutations(i))
    l = (x for x in l if x != i)
    for anagram in l:
        if anagram in words:
            f2.write(i + "\n")
我在这里假设Python3.x,因为在其他情况下,
list
调用完全没有必要。如果您使用的是2.x,请将
map
替换为
itertools.imap


作为旁注,
f.read(1000)
通常会在末尾得到额外单词的一部分,在下一个循环中得到剩余部分。尝试虽然没有论据是没有用的,但有论据是非常有用的:

读取并返回流中的行列表。可以指定hint来控制读取的行数:如果到目前为止所有行的总大小(以字节/字符为单位)超过hint,则不会读取更多的行

因此,
f.readlines(1000)
将允许您一次读取大约1K的缓冲区,而不会得到部分行。当然,现在,您不必在换行符上进行拆分,而必须对它们进行拆分:

words = [line.rstrip('\n') for line in f.readlines(1000)]

然而,你还有另一个问题。如果你一次只读大约100个单词,那么找到一个字谜的机会非常渺茫。例如,
orchestra
在字典中不会接近
carthorse
,因此除非记住整个文件,否则无法找到。但这应该是好的;像web2这样的典型Unix字典大约有200K行;您可以轻松地将其读入内存,并将其作为一个
保存,而不会对2GB造成任何影响。因此:

words = set(line.rstrip('\n') for line in f)

另外,请注意,您正试图打印出字典中每个有字谜的单词(如果有多个字谜,请多次打印)。即使有一个高效的算法,这也需要很长的时间,并且会输出比您可能想要的更多的数据。更有用的程序可能是接收输入单词(例如,通过
input
sys.argv[1]
)并仅输出该单词的字谜


最后:


即使在使用l作为生成器之后,它占用了太多的休息时间,尽管没有内存错误而失败。你能解释一下单词作为一个集合而不是一个列表的重要性吗。[在137.4s中完成]仅200字节,您之前已经提到过,但是如何使用设置的单词来克服它

正如我在顶部所说,“无论你做什么,这个程序都将是非常低效的。”

为了找到一个12个字母的单词的字谜,你需要进行4.79亿次排列,并对照一本约20万个单词的字典检查每一个单词,这就是每一个单词的479M*200K=95万亿次检查。有两种方法可以改进这一点,第一种方法涉及为作业使用正确的数据结构,第二种方法涉及为作业使用正确的算法

将要迭代的内容集合从列表更改为生成器(惰性iterable)将占用线性空间(479M字符串)的内容转换为占用常量空间的内容(某些固定大小的迭代器状态,每次加上一个字符串)。类似地,将要检查的单词集合从列表更改为集合会将需要线性时间的内容(将字符串与列表中的每个元素进行比较)转换为需要固定时间的内容(对字符串进行散列,然后查看集合中是否有具有该散列值的内容)。因此,这就解决了问题的
*200K
部分

但问题仍然存在
479M
部分。你不能用一个更好的数据结构来解决这个问题。相反,你必须重新思考这个问题。在不尝试所有排列的情况下,如何检查一个单词的任何排列是否与任何其他单词匹配

单词X的某些排列与单词Y匹配,当且仅当X和Y具有相同的字母。X中的字母顺序不重要;如果集合相同,则至少有一个匹配排列(或者正好有一个,取决于重复字母的计数方式),如果没有,则正好有0个。因此,与其遍历单词中的所有排列进行查找,不如查找其集合。但是,如果存在重复项,这也很重要,因此您不能仅在此处使用
set
。您可以使用某种多集(lettersets = collections.defaultdict(set) for word in words: lettersets[''.join(sorted(word))].add(word)
anagrams = lettersets[''.join(sorted(word))]
for _, words in lettersets.items():
    for word in words:
        print('{} is an anagram of {}'.format(word, ', '.join(words - {word})))
import urllib

def anagram():
    f=urllib.urlopen('http://www.puzzlers.org/pub/wordlists/unixdict.txt')
    words = f.read().split('\n')

    d={''.join(sorted(x)):[]  for x in words}   #create dic with empty list as default
    for x in words:
        d[''.join(sorted(x))].append(x)

    max_len= max( len(v) for k,v in d.iteritems())

    for k,v in d.iteritems():
        if len(v)>=max_len:
            print v

anagram()
['abel', 'able', 'bale', 'bela', 'elba']
['alger', 'glare', 'lager', 'large', 'regal']
['angel', 'angle', 'galen', 'glean', 'lange']
['evil', 'levi', 'live', 'veil', 'vile']
['caret', 'carte', 'cater', 'crate', 'trace']
['elan', 'lane', 'lean', 'lena', 'neal']