【问题标题】:Python's underlying hash data structure for dictionariesPython 用于字典的底层哈希数据结构
【发布时间】:2011-05-15 19:52:14
【问题描述】:

我正在构建一个非常大的字典,我正在执行许多检查以查看一个键是否在结构中,然后添加它是否唯一,或者如果它相同则增加一个计数器。

Python 使用hash data structure 来存储字典(不要与加密哈希函数混淆)。查找是 O(1),但如果哈希表已满,则必须重新哈希,这非常昂贵。

我的问题是,我最好使用AVL Binary Search Tree 还是哈希表足够好?

【问题讨论】:

  • 请注意,如果您想知道哈希表和 AVL 树之间哪个性能更好,那么还有很多其他的参与者需要考虑。尝试和展开树浮现在脑海中。
  • 也许你甚至可以使用计数布隆过滤器。
  • from collections import Counter
  • @THC4k 是的,谷歌使用了布隆过滤器,有趣的东西。

标签: python algorithm performance data-structures


【解决方案1】:

唯一可以确定的方法是同时实现并检查,但我有根据的猜测是字典会更快,因为二叉搜索树的查找和插入成本为 O(log(n)),而我认为除了在最糟糕的情况下(例如大规模哈希冲突),哈希表的 O(1) 查找将超过偶尔调整大小。

如果您查看Python dictionary implementation,您会看到:

  1. 字典以 8 个条目开始 (PyDict_MINSIZE);
  2. 50,000 或更少条目的字典在增长时会翻两番;
  3. 超过 50,000 个条目的字典在增长时会翻倍;
  4. 键哈希值缓存在字典中,因此在调整字典大小时不会重新计算它们。

(“NOTES ON OPTIMIZING DICTIONARIES”也值得一读。)

因此,如果您的字典有 1,000,000 个条目,我相信它将被调整大小 11 次(8 → 32 → 128 → 512 → 2048 → 8192 → 32768 → 131072 → 262144 → 524288 → 1048576 → 2097152),成本为 2,009,768调整大小期间的额外插入。这似乎比在 AVL 树中插入 1,000,000 次所涉及的所有重新平衡成本要低得多。

【讨论】:

    【解决方案2】:

    物品与独特物品的比例是多少? 唯一项目的预期数量是多少?

    如果散列桶被填满,那么扩展应该只是一些内存重新分配的问题,而不是重新散列。

    测试一个计数字典应该非常快速和容易。

    还要注意自 python 2.7 以来可用的计数器类 http://docs.python.org/library/collections.html#counter-objects http://svn.python.org/view?view=rev&revision=68559

    【讨论】:

    • 是的,OP 说字典必须重新散列是错误的。
    • 我认为将项目重新分配到更大的存储桶数组中的操作是称为重新散列,即使您已经存储了完整的散列值,因此不需要重新计算它,只需取一个具有不同值的模数。无论哪种方式,与普通刀片相比,它都比较昂贵。
    • 增加桶大小和增加桶数量是有区别的。增加桶大小通常很便宜,尤其是在只存储指向对象的指针时。增加桶的数量是另一回事。因为它应该只发生“足够频繁”,所以它应该被认为是按插入次数摊销的。
    【解决方案3】:

    Python 字典经过高度优化。 Python 进行了 Python 开发人员在 CPython 字典实现中迎合的各种特殊情况优化。

    1. 在 CPython 中,所有 PyDictObject 都针对仅包含字符串键的字典进行了优化。
    2. Python 的字典尽量不超过 2/3。

    Beautiful Code》一书讨论了这一切。

    第十八章是 Python 的字典实现:Adrew Kuchling 的《Being All Things to All People》

    使用它比尝试实现手工制作的自定义实现要好得多,后者必须将所有这些优化复制到字典查找的主要 CPython 实现附近的任何地方。

    【讨论】:

    • +1:Python 非常依赖字典,这会广泛影响语言本身的性能。我敢打赌,他们的实施很难被击败。
    • @André Caron:当然!也请参阅 Gareth Rees 的回答。我们几乎是按照类似的思路写的。字典的Python实现和优化非常好。 Python 依赖于它。很难打败它。
    • 我只是在阅读“优化字典的说明”。这是很酷的文档。我喜欢人们跟踪实验和讨论,因为它避免了重复工作(就像这篇文章......)
    【解决方案4】:

    您必须在 C 中实现自己的数据结构,才能有合理的机会击败内置结构。

    您还可以通过使用get 来避免一些开销,避免两次查找现有元素。 或者 collections.Counter 如果您使用的是 python 2.7+。

    def increment(map, key):
        map[key] = map.get(key,0)+1
    

    【讨论】:

    • 这似乎没有增加值。
    • 我喜欢这个,但是你的增量函数没有增量:-)
    【解决方案5】:

    使用字典是 O(1)。随着字典的增长,有时需要重新分配,但这是摊销 O(1)

    如果您的其他算法是 O(log n),那么简单的 dict 将总是随着数据集变大而击败它。

    如果您使用任何类型的树,我希望在某处有一个 O(log n) 组件。

    哈希表不仅足够好,而且更好

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-12
      • 1970-01-01
      • 2011-01-07
      • 2010-12-07
      • 2021-06-02
      • 2010-10-29
      • 2018-05-20
      相关资源
      最近更新 更多