Python:itertools.product消耗太多资源

Python:itertools.product消耗太多资源,python,permutation,itertools,Python,Permutation,Itertools,我创建了一个Python脚本,它通过字符排列生成一个单词列表。我正在使用itertools.product生成排列。我的字符列表由字母和数字组成01234567890abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz。这是我的密码: #!/usr/bin/python import itertools, hashlib, math class Words: chars = '01234567890abcdefghijklmnopqrs

我创建了一个Python脚本,它通过字符排列生成一个单词列表。我正在使用
itertools.product
生成排列。我的字符列表由字母和数字组成01234567890abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz。这是我的密码:

#!/usr/bin/python
import itertools, hashlib, math

class Words:

    chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ'

    def __init__(self, size):
        self.make(size)

    def getLenght(self, size):
        res = []
        for i in range(1, size+1):
            res.append(math.pow(len(self.chars), i))
        return sum(res)

    def getMD5(self, text):
        m = hashlib.md5()
        m.update(text.encode('utf-8'))
        return m.hexdigest()

    def make(self, size):
        file = open('res.txt', 'w+')
        res = []
        i = 1
        for i in range(1, size+1):
            prod = list(itertools.product(self.chars, repeat=i))
            res = res + prod
        j = 1
        for r in res:
            text = ''.join(r)
            md5 = self.getMD5(text)
            res = text+'\t'+md5
            print(res + ' %.3f%%' % (j/float(self.getLenght(size))*100))
            file.write(res+'\n')
            j = j + 1
        file.close()

Words(3)
此脚本适用于最多4个字符的单词列表。如果我尝试5或6个字符,我的计算机将消耗100%的CPU、100%的RAM并冻结


有没有办法限制这些资源的使用或优化这一繁重的处理过程?

这能满足您的需要吗

我对make方法进行了所有更改:

def make(self, size):

    with open('res.txt', 'w+') as file_: # file is a builtin function in python 2
        # also, use with statements for files used on only a small block, it handles file closure even if an error is raised.
        for i in range(1, size+1):
            prod = itertools.product(self.chars, repeat=i)

            for j, r in enumerate(prod):
                text = ''.join(r)
                md5 = self.getMD5(text)
                res = text+'\t'+md5
                print(res + ' %.3f%%' % ((j+1)/float(self.get_length(size))*100))
                file_.write(res+'\n')
请注意,这仍然会占用千兆字节的内存,但不会占用虚拟内存

编辑:正如Padraic所指出的,Python3中没有file关键字,而且由于它是一个“坏的内置项”,所以不必太担心重写它。不过,我会在这里给它命名为file

编辑2:

要解释为什么这比以前的原始版本工作得更快更好,您需要知道惰性评估是如何工作的

假设我们有一个简单的表达式,如下所示(对于Python 3)(对于Python 2使用xrange):

这会立即将1万亿个元素计算到内存中,使内存溢出

因此,我们可以使用生成器来解决此问题:

a = (i for i in range(1e12))
在这里,没有对任何值进行评估,只是给出了解释程序关于如何评估它的说明。然后我们可以一个一个地迭代每个项目,并分别处理每个项目,因此在给定时间内存中几乎没有任何内容(一次只有一个整数)。这使得看似不可能的任务变得非常容易管理

itertools也是如此:它允许您通过使用迭代器而不是列表或数组来执行操作,从而实现高效、快速的内存操作

在您的示例中,您有62个字符,并且希望以5个重复或62**5(接近十亿个元素,或超过30 GB的ram)进行笛卡尔乘积。这个数字太大了。”

为了解决这个问题,我们可以使用迭代器

chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ'
for i in itertools.product(chars, repeat=5):
    print(i)
在这里,在给定的时间内,只有笛卡尔乘积中的一个项在内存中,这意味着它非常高效

但是,如果您使用list()计算完整的迭代器,它会耗尽迭代器并将其添加到列表中,这意味着近10亿个组合突然再次出现在内存中。我们不需要一次将所有元素都存储在内存中:只需1。这是迭代器的强大功能

chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ'
for i in itertools.product(chars, repeat=5):
    print(i)

以下是上的链接和另一个解释(对于3,大多数是正确的)。

这是否符合您的要求

我对make方法进行了所有更改:

def make(self, size):

    with open('res.txt', 'w+') as file_: # file is a builtin function in python 2
        # also, use with statements for files used on only a small block, it handles file closure even if an error is raised.
        for i in range(1, size+1):
            prod = itertools.product(self.chars, repeat=i)

            for j, r in enumerate(prod):
                text = ''.join(r)
                md5 = self.getMD5(text)
                res = text+'\t'+md5
                print(res + ' %.3f%%' % ((j+1)/float(self.get_length(size))*100))
                file_.write(res+'\n')
请注意,这仍然会占用千兆字节的内存,但不会占用虚拟内存

编辑:正如Padraic所指出的,Python3中没有file关键字,而且由于它是一个“坏的内置项”,所以不必太担心重写它。尽管如此,我还是在这里给它命名为file

编辑2:

要解释为什么这比以前的原始版本工作得更快更好,您需要知道惰性评估是如何工作的

假设我们有一个简单的表达式,如下所示(对于Python 3)(对于Python 2使用xrange):

这会立即将1万亿个元素计算到内存中,使内存溢出

因此,我们可以使用生成器来解决此问题:

a = (i for i in range(1e12))
在这里,没有对任何值进行计算,只是给出了解释程序如何计算的说明。然后我们可以逐个迭代每个项,并分别对每个项进行处理,因此在给定时间内存中几乎没有任何内容(一次只有1个整数)。这使得看似不可能的任务非常容易管理

itertools也是如此:它允许您通过使用迭代器而不是列表或数组来执行操作,从而实现高效、快速的内存操作

在您的示例中,您有62个字符,并且希望以5个重复或62**5(接近10亿个元素,或超过30 GB的ram)进行笛卡尔乘积。这太大了。”

为了解决这个问题,我们可以使用迭代器

chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ'
for i in itertools.product(chars, repeat=5):
    print(i)
在这里,在给定的时间内,只有笛卡尔乘积中的一个项在内存中,这意味着它非常高效

但是,如果您使用list()计算完整的迭代器,它将耗尽迭代器并将其添加到列表中,这意味着近10亿个组合突然再次出现在内存中。我们不需要同时在内存中存储所有元素:只有1个。这就是迭代器的威力

chars = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ'
for i in itertools.product(chars, repeat=5):
    print(i)

以下是上的链接和另一个解释(对于3,大多数是正确的)。

您的问题是呼叫列表,然后它会评估所有这些问题。Itertools非常适合惰性迭代,逐个评估它们以避免内存问题。您正在尝试使用5个字符(62**5)的近10亿个组合,这就是您的计算机冻结的原因。它消耗了所有可用的内存。你的建议是手动迭代?我会发布一个解决方案。您应该迭代每个元素。Itertools的工作原理与yield类似(虽然级别较低),但您可以这样想:它被赋予一组指令,然后计算一个值,并返回该值并冻结(因此没有内存积累)。然后你可以做任何事情,直到你需要下一个元素。将所有内容立即放在内存中会破坏使用itertools的目的,特别是对于这样的数据大小(~38 Gig的ram)。您应该在生成每个元素时对其进行评估,为每个元素执行工作,然后只保留您需要的内容。。您的问题是调用列表,然后该列表将对所有调用列表进行评估。Itertools非常适合惰性迭代,逐个评估它们以避免内存问题。您正在尝试使用5个字符(62**5)的近10亿个组合,这就是您的计算机冻结的原因。它消耗了所有可用的内存。你的建议是手动迭代?我会发布一个解决方案。您应该迭代每个元素。Itertools的工作原理与收益率相似(尽管较低)