在 Python 中对不可变字典进行哈希处理

2023-12-29

简洁版本:对于作为无序项字典实现的多重集,最好的哈希算法是什么?

我正在尝试对作为字典实现的不可变多重集(在其他语言中是一个包或多重集:就像一个数学集,除了它可以容纳多个元素)进行散列。我创建了标准库类的子类collections.Counter,类似于这里的建议:Python 可哈希字典 https://stackoverflow.com/questions/1151658/python-hashable-dicts,它推荐像这样的哈希函数:

class FrozenCounter(collections.Counter):
    # ...
    def __hash__(self):
        return hash(tuple(sorted(self.items())))

创建完整的项目元组会占用大量内存(例如,相对于使用生成器),并且散列将发生在我的应用程序的内存极其密集的部分中。更重要的是,我的字典键(多集元素)可能无法订购。

我正在考虑使用这个算法:

def __hash__(self):
    return functools.reduce(lambda a, b: a ^ b, self.items(), 0)

我认为使用按位异或意味着与元组的散列不同,顺序对于散列值并不重要?我想我可以在数据的无序元组流上半实现 Python 元组哈希算法。看https://github.com/jonashaag/cpython/blob/master/Include/tupleobject.h https://github.com/jonashaag/cpython/blob/master/Include/tupleobject.h(在页面中搜索“哈希”一词)——但我几乎不知道足够的 C 语言来阅读它。

想法?建议?谢谢。


(If you're wondering why I'm messing around with trying to hash a multiset: The input data for my problem are sets of multisets, and within each set of multisets, each multiset must be unique. I'm working on a deadline and I'm not an experienced coder, so I wanted to avoid inventing new algorithms where possible. It seems like the most Pythonic way to make sure I have unique of a bunch of things is to put them in a set(), but the things must be hashable.)

我从评论中收集到的内容

@marcin 和 @senderle 给出了几乎相同的答案:使用hash(frozenset(self.items()))。这是有道理的,因为items()“视图”是类似集合的 http://docs.python.org/py3k/library/stdtypes.html#dictionary-view-objects。 @marcin 是第一个,但我给了 @senderle 复选标记,因为对不同解决方案的 big-O 运行时间进行了很好的研究。 @marcin 还提醒我包括一个__eq__ method http://docs.python.org/py3k/reference/datamodel.html#object.__hash__——但是继承自dict会工作得很好。这就是我实现一切的方式——欢迎基于此代码的进一步评论和建议:

class FrozenCounter(collections.Counter):
    # Edit: A previous version of this code included a __slots__ definition.
    # But, from the Python documentation: "When inheriting from a class without
    # __slots__, the __dict__ attribute of that class will always be accessible,
    # so a __slots__ definition in the subclass is meaningless."
    # http://docs.python.org/py3k/reference/datamodel.html#notes-on-using-slots
    # ...
    def __hash__(self):
        "Implements hash(self) -> int"
        if not hasattr(self, '_hash'):
            self._hash = hash(frozenset(self.items()))
        return self._hash

由于字典是不可变的,因此可以在创建字典时创建哈希并直接返回它。我的建议是创建一个frozenset http://docs.python.org/library/stdtypes.html#frozenset from items(3+;iteritems2.7),对其进行散列,并存储散列。

提供一个明确的例子:

>>>> frozenset(Counter([1, 1, 1, 2, 3, 3, 4]).iteritems())
frozenset([(3, 2), (1, 3), (4, 1), (2, 1)])
>>>> hash(frozenset(Counter([1, 1, 1, 2, 3, 3, 4]).iteritems()))
-3071743570178645657
>>>> hash(frozenset(Counter([1, 1, 1, 2, 3, 4]).iteritems()))
-6559486438209652990

为了澄清为什么我更喜欢frozenset到已排序项目的元组:afrozenset不必对项目进行排序,因此初始哈希在 O(n) 时间内完成,而不是 O(n log n) 时间。这可以从frozenset_hash http://hg.python.org/cpython/file/5fd1ac1c9474/Objects/setobject.c#l766 and set_next http://hg.python.org/cpython/file/5fd1ac1c9474/Objects/setobject.c#l531实施。

另请参阅此很好的答案 https://stackoverflow.com/a/20931478Raymond Hettinger 描述了他的实施frozenset哈希函数。在那里,他明确解释了哈希函数如何避免对值进行排序以获得稳定的、顺序不敏感的值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Python 中对不可变字典进行哈希处理 的相关文章

随机推荐