Python Frozenset哈希算法实现

Python Frozenset哈希算法实现,第1张

Python Frozenset哈希算法/实现

除非Raymond
Hettinger(代码的作者)陷入困境,否则我们将永远无法确定;-)但是,这些东西中的“科学”通常比您期望的要少:您采用一些通用原则,一个测试套件,然后对它们进行弄乱。常量几乎是任意的,直到结果看起来“足够好”为止。

一些通用原则“显然”在这里起作用:

  1. 要获得所需的快速“位分散”,您需要乘以一个大整数。由于在许多平台上CPython的哈希结果必须适合32位,因此最好使用需要32位的整数。而且,的确

    (3644798167).bit_length() == 32

  2. 为避免系统地丢失低阶位,您需要乘以一个奇数整数。3644798167很奇怪。

  3. 更一般而言,为避免在输入哈希中增加模式,您需要乘以质数。3644798167是质数。

  4. 而且,您还需要一个乘法器,该乘法器的二进制表示形式没有明显的重复模式。

    bin(3644798167) == '0b11011001001111110011010011010111'
    。那真是一团糟,这是一件好事;-)

在我看来,其他常量完全是任意的。的

if h == -1:    h = 590923713

部分是由于另一个原因而需要的:在内部,CPython

-1
从整数值C函数获取一个返回值,表示“需要引发异常”;即,这是错误返回。因此,您将永远不会
-1
在CPython中看到任何对象的哈希码。返回的值而不是
-1
完全任意的-
每次只需要是 相同的 值(而不是-1)即可。

编辑:玩

我不知道雷蒙德用什么来测试。这是我将要使用的:查看一组连续整数的所有子集的哈希统计信息。这些是有问题的,因为

hash(i) == i
有很多整数
i

>>> all(hash(i) == i for i in range(1000000))True

简单地将异或散列在一起将对此类输入产生大量抵消。

因此,这里有一个小函数可以生成所有子集,另一个函数是对所有哈希码进行简单的异或运算:

def hashxor(xs):    h = 0    for x in xs:        h ^= hash(x)    return hdef genpowerset(xs):    from itertools import combinations    for length in range(len(xs) + 1):        for t in combinations(xs, length): yield t

然后是一个驱动程序,以及一个显示碰撞统计信息的小功能:

def show_stats(d):    total = sum(d.values())    print "total", total, "unique hashes", len(d),           "collisions", total - len(d)def drive(n, hasher=hashxor):    from collections import defaultdict    d = defaultdict(int)    for t in genpowerset(range(n)):        d[hasher(t)] += 1    show_stats(d)

使用简单易用的哈希器是灾难性的:

>> drive(20)total 1048576 unique hashes 32 collisions 1048544

kes!OTOH

_hash()
在这种情况下使用专为冷冻食品设计的产品可以完美地完成工作:

>>> drive(20, _hash)total 1048576 unique hashes 1048576 collisions 0

然后,您可以尝试使用该工具,看看究竟有什么作用-以及没有-真正的作用

_hash()
。例如,如果

    h = h * 69069 + 907133923

已移除。而且我不知道为什么会有那条线。同样,如果

^ 89869747
删除了内部循环中的,它将继续在这些输入上做完美的工作-
不知道为什么也是如此。初始化可以从以下方式更改:

    h = 1927868237 * (n + 1)

至:

    h = n

这里也没有伤害。所有这些都符合我的期望:由于已经说明的原因,至关重要的是内部循环中的乘法常数。例如,向其添加1(使用3644798168),然后它不再是质数或奇数,并且统计信息降级为:

total 1048576 unique hashes 851968 collisions 196608

仍然相当有用,但是绝对更糟。将其更改为较小的素数,例如13,更糟糕的是:

total 1048576 unique hashes 483968 collisions 564608

使用具有明显二进制模式(例如)的乘法器

0b01010101010101010101010101010101
,然后再次恶化:

total 1048576 unique hashes 163104 collisions 885472

玩吧!这些东西很有趣:-)



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5646009.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存