【问题标题】:Python sorted consumes tons of memory? (from a power set generator)Python排序消耗大量内存? (来自发电机组)
【发布时间】:2013-04-16 19:48:12
【问题描述】:

我基本上是在寻找可能对此有意见的其他人的一些反馈。以下不是我正在处理的内容,但示例代码确实重现了该问题。

如果我发送的基本列表传入,我有一个幂集生成器,它返回所有排列。我需要对生成的集合进行排序(在我的实际情况下,返回的集合是具有我想要排序的值的元组通过,下面的示例演示了没有它的问题)

问题是当我在电源组生成器上使用 sorted() 时,它会增加内存使用量。我意识到 2^50 是一个非常大的数字,但是没有排序的内存使用量是相当平坦的,所以我想知道是否有更好的方法可以在一两分钟内对大量集合进行排序而不会耗尽内存。这是在带有 Python 2.6.5 的 Ubuntu 上运行的。 (在这种情况下也需要)

def gen_powerset(seq):
    if len(seq) <= 1:
        yield seq
        yield []
    else:
        for i in gen_powerset(seq[1:]):
            yield [seq[0]]+i
            yield i

def main():
    initialSet = range(50)
    powerset = sorted(gen_powerset(initialSet))
    for i in powerset:
        print i

if __name__ == "__main__":
    main()

免责声明:如果您尝试运行此示例,请注意您的内存使用情况。如果样本接近 90%,请按 Ctrl-C,因为您的操作系统将开始将内存交换到磁盘。如果示例仍在运行,您的磁盘负载会飙升并真正减慢速度,因此很难从一开始就杀死示例。

【问题讨论】:

  • 你正在阻塞你的进程。你这样做有什么特别的目标吗?
  • 在示例中,range(50) 将生成 50 个项目的 2^50 个组合。那是 1.1258999e+15 排列,太大而无法合理地放入内存中。我期待更大的套装。请注意,该范围仅适用于此示例,这是从包含我需要的数据的文件中加载的数据集。生成的集合是循环的,所以我可以找到有价值的项目并处理它们。如果已排序,则价值最高的项目应位于列表顶部。我知道 sorted 调用生成器是一个关键点,基本上会导致 sorted() 一次加载整个数据集。
  • 也许应该使用数据库来存储生成的列表以进行排序?

标签: python performance sorting memory tuples


【解决方案1】:

如果没有sorted,您一次不需要存储超过 1 或 2 个值 - 因为您使用的是生成器 (yield),所以它们会根据需要进行计算。不幸的是,在不了解全部内容的情况下,没有好的方法可以对列表进行排序(在您查看所有项目以确保您的项目之前,您无法从排序中产生一个值有是最小的)。

当然,如果您有 2 个已排序的子列表,您可以懒惰地合并它们,因此您可以构建一个排序,它不会基于合并排序将所有内容一次存储在内存中,但是它在一般情况下会非常低效。

【讨论】:

  • 这就是我使用生成器的原因,这样我就可以一次获取一个值(在我的实际情况下,这些值部分在生成器中计算并包含在产量中),而无需全部保存一次。可以使用合并排序,但我认为最终它仍然需要太多内存而不将列表交换到磁盘,然后根据需要将它们拉回以与其他列表进行比较。
  • @garlicman -- 是的,我说的是从磁盘来回交换列表。 FWIW,这就是人们需要对巨大的文件进行排序时所做的事情。他们对文件的较小块进行一系列连续排序,然后将其重新合并在一起。
  • 好的,我给你接受。我正在寻找可以在帖子中回答的更大挑战的替代建议。
【解决方案2】:

sorted 的内存使用率较高的原因是它必须一次将所有项目加载到内存中。由于您编写了一个生成器,它一次只产生一个元素,并且您使用它的方式一次只使用一个值,因此 Python 不需要同时保留所有项目。但是如果没有全部可用,您就无法对它们进行排序。

只要你在进行排序,你就无法绕过这个问题,因为排序必须有所有可用的元素。

解决此问题的唯一方法是重写您的 powerset 生成器以按您想要的顺序生成项目。这可能会也可能不会,具体取决于您想要的顺序。

【讨论】:

  • 唯一的问题是,在生成器中,端集是未知的,因此对它们进行排序需要我保留之前生成的所有内容,对其进行排序,然后从生成器返回,这不是t 生成器打算做什么。例如,在我看到所有生成集之前,我无法知道生成集的最高值是多少,那么生成器如何首先返回该集?
  • @garlicman:一般来说是这样,但是对于 powerset,您提前知道生成器的元素会是什么样子:它们是原始序列的子集。因此,例如,您可以预先对seq 进行排序,和/或使用具有不同大小的itertools.combinations 来按大小顺序生成集合,等等。
  • 您对所提供的示例是正确的。如果我按照我想要的顺序传入一个范围,然后我在每个嵌套递归返回时对其进行排序,我就不必一次对整个返回的幂集进行排序。然而,在我的真实情况下,我生成了一个包含集合和值的元组。该值是该集合中每个项目所代表的属性的总和。将是每个返回的元组的一个值。当返回初始序列项时,排序需要对该总值进行排序。我看着尝试对生成器本身进行排序,但我认为我不能在那里做到。谢谢。
【解决方案3】:

您使用的生成器在消耗之前一次只创建一个值,这非常节省内存。 sorted 函数需要将其转换为列表,以便它一次全部驻留在内存中。没有办法解决。

【讨论】:

  • 同意。我希望关于如何处理这个问题的建议,或者如果有一些我不知道的 Python 支持会有所帮助。
猜你喜欢
  • 2022-12-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 2014-01-04
  • 2012-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多