【问题标题】:Python sets versus arraysPython 集合与数组
【发布时间】:2019-09-09 23:37:02
【问题描述】:

我需要在内存中存储大量数字。然后我需要检查会员资格。在内存效率方面,数组比列表更好。集合比用于成员检查的列表更好。我两个都需要!所以我的问题是:

1) 数组的内存效率比集合高多少? (相反,请参阅下面的结果)。 2) 是否有一种数据结构可以在集合和数组之间取得更好的平衡?类似于带符号整数类型的集合?还是一些 numpy 构造?

我用下面的脚本检查了成员资格的时间差异。 (我知道 timeit 更好,但方差足够低以至于 time 没问题):

import array
import time 

class TimerContext:
    def __enter__(self):
        self.t0 = time.time()
    def __exit__(self, *args, **kwargs):
        print(time.time()-self.t0)

SIZE = 1000000

l = list([i for i in range(SIZE)])
a = array.array('I', l)
s = set(l)

print(type(l))
print(type(a))
print(type(s))

with TimerContext():
    x = 99999 in l
with TimerContext():
    x = 99999 in a
with TimerContext():
    x = 99999 in s

结果:

<class 'list'>
<class 'array.array'>
<class 'set'>
0.0012176036834716797
0.0024595260620117188
1.430511474609375e-06

因此,对于成员资格检查,集合要快很多(请注意科学记数法)。因此,如果它们的内存占用与数组没有什么不同,我会更喜欢使用一个集合。但我不知道如何检查内存占用。

我还应该补充一点,比较集合和列表有很多问题。但是比较数组和集合我没有看到任何好的答案。

【问题讨论】:

  • 如果有人知道您的真正问题,可能还有许多其他更好的解决方案。
  • sys.getsizeof 表明,对于您的示例,该集合比数组大约 8 倍。 docs.python.org/3/library/sys.html#sys.getsizeof.
  • 列表和数组成员资格测试都是 O(n),集合是 O(1)……也有一些近似算法,这可能会有所帮助(例如 Bloom 过滤器)。如果您有任何其他可以解决问题的限制条件,您可能会有更多选择
  • google 建议 pypi.org/project/intset 可能会有所帮助...
  • Python 还带有一个 timeit 模块,它与你的 TimerContext 做类似的事情,Jupyter/IPython 用一个很好的 %timeit magic 包装它

标签: python arrays list set


【解决方案1】:

如果可能的话,bisect 的成员资格检查性能接近set(包括列表和数组)。查看下面的结果

import array
from bisect import bisect
import sys
import time


class TimerContext:
    def __enter__(self):
        self.t0 = time.time()

    def __exit__(self, *args, **kwargs):
        print(time.time() - self.t0)


def get_size_in_megabytes(iterable):
    return round(sys.getsizeof(iterable) / (1024 ** 2), 2)


SIZE = 1000000

l = list([i for i in range(SIZE)])
a = array.array("I", l)
s = set(l)

print(type(l), get_size_in_megabytes(l))
print(type(a), get_size_in_megabytes(a))
print(type(s), get_size_in_megabytes(s))

with TimerContext():
    x = 99999 in l
with TimerContext():
    x = 99999 in a
with TimerContext():
    x = 99999 in s

print("list bisect")
with TimerContext():
    bisect(l, 99999)

print("array bisect")
with TimerContext():
    bisect(a, 99999)

结果:

<class 'list'> 8.58
<class 'array.array'> 3.81
<class 'set'> 32.0
0.0024390220642089844
0.0053005218505859375
3.814697265625e-06
list bisect
9.298324584960938e-06
array bisect
6.198883056640625e-06

sys.getsizeof 使用 @CristiFati 的信用。

【讨论】:

  • bisect 是 O(log_n)... 此外,getsizeof 不计算任何引用元素的大小,只计算对象本身...有一些食谱显示如何递归成物体。 int 是 28 字节,所以 1M 元素的列表应该是 35MB
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
  • 2010-10-09
  • 2016-12-24
相关资源
最近更新 更多