为什么Python的无穷大散列具有π的数字?


241

Python中无穷大的哈​​希值具有与pi匹配的数字:

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159

这仅仅是巧合还是故意的?


9
不能肯定,但我的猜测是,这是故意的hash(float('nan'))存在0
cs95

1
嗯,在中没有提及sys.hash_info。复活节彩蛋?
WIM

123
问蒂姆·彼得斯。这是19年前他介绍此常数的地方:github.com/python/cpython/commit/…。我一直在这些特殊的值,当我在修改了数字散列bugs.python.org/issue8188
马克·迪金森

8
@MarkDickinson谢谢。看来Tim 最初可能也已将e的数字用于-inf的哈希。
20:42

17
@wim是的,是的。显然,我将其更改为-314159。我忘记了这一点。
马克·迪金森

Answers:


47

_PyHASH_INF定义为等于的常数314159

我找不到关于此的任何讨论,也没有提供原因的评论。我认为它或多或少是任意选择的。我想只要它们不将相同的有意义的值用于其他哈希,就没关系。


6
小nitpick:根据定义,几乎不可避免的是,将相同的值用于其他哈希,例如在这种情况下hash(314159)也是314159。也可以尝试在Python 3中hash(2305843009214008110) == 314159(此输入为314159 + sys.hash_info.modulus)等
。– ShreevatsaR

3
@ShreevatsaR我只是说,只要他们没有按照定义将其选择为其他值的哈希,然后选择一个有意义的值,就不会增加哈希冲突的可能性
Patrick Haugh

220

简介:这不是巧合;在Python的默认CPython实现中_PyHASH_INF被硬编码为314159,并在2000年被Tim Peters选为任意值(显然是从π的数字)。


的值hash(float('inf'))是数值类型内置散列函数的系统相关的参数中的一个,并且也可以作为sys.hash_info.inf在Python 3:

>>> import sys
>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
>>> sys.hash_info.inf
314159

与PyPy的结果相同。)


就代码而言,hash是一个内置函数。在Python float对象上调用它会调用函数,该函数的指针由内置float类型()的tp_hash属性给定,该类型定义为的函数,PyTypeObject PyFloat_Type而该函数又具有float_hashreturn _Py_HashDouble(v->ob_fval)

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;

其中_PyHASH_INF定义为 314159:

#define _PyHASH_INF 314159

从历史的角度来看,Tim Peters在2000年8月添加了314159此上下文中Python代码中的第一个提及(您可以使用git bisect或找到git log -S 314159 -p),现在在git存储库中提交了39dce293cpython

提交消息说:

修复了http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470的问题。这是一个令人误解的错误-真正的“错误”是hash(x)xinfinity为无限时返回错误。修复了。向添加了新的Py_IS_INFINITYpyport.h。重新排列了代码,以减少浮点数和复数的散列中越来越多的重复,从而将Trent之前的做法推到了合理的结论。修复了一个极其罕见的错误,即即使没有错误,浮点数的哈希也可能返回-1(并没有浪费时间来构造一个测试用例,从代码中可以明显看出它可能发生)。改进了复杂的哈希,因此 hash(complex(x, y))不再系统地相等hash(complex(y, x))

特别是,在此提交中,他撕掉了static long float_hash(PyFloatObject *v)in 的代码Objects/floatobject.c并使它成为just return _Py_HashDouble(v->ob_fval);,并在in的定义long _Py_HashDouble(double v)Objects/object.c添加了以下几行:

        if (Py_IS_INFINITY(intpart))
            /* can't convert to long int -- arbitrary */
            v = v < 0 ? -271828.0 : 314159.0;

因此,如上所述,这是一个任意选择。请注意,271828由e的前几个十进制数字形成。

相关的以后的提交:


44
为-Inf选择-271828可以消除对pi关联是偶然的怀疑。
罗素·博罗戈夫

24
@RussellBorogove没有,但它使大约一百万次的可能性较小;)

8
@cmaster:在那里说:2010年5月见上面的部分,即对文档部分数值类型的散列问题8188 -的想法是,我们希望hash(42.0)是一样的hash(42),也一样hash(Decimal(42))hash(complex(42))hash(Fraction(42, 1))。该解决方案(由Mark Dickinson提出)是一种优雅的IMO:定义适用于任何有理数的数学函数,并利用浮点数也为有理数这一事实。
ShreevatsaR

1
@ShreevatsaR啊,谢谢。尽管我不愿保证这些平等,但很高兴知道对于看似复杂的代码有一个良好,可靠且合乎逻辑的解释:-)
cmaster-恢复莫妮卡

2
@cmaster整数的哈希函数只是hash(n) = n % M其中M =(2 ^ 61-1 )。对于有理数n,这是广义的,hash(p/q) = (p/q) mod M除法以M为模(换句话说:)hash(p/q) = (p * inverse(q, M)) % M。我们想要这样做的原因是:如果放入dict中,然后d放入(例如42.0 == 42),但是与不一致,那么我们就会遇到问题。大多数看似复杂的代码都来自浮点格式本身的性质,以便正确恢复小数部分,并且需要特殊情况下的inf和NaN值。d[x] = foox==yd[y]d[x]
ShreevatsaR

12

确实,

sys.hash_info.inf

返回314159。该值不会生成,而是内置在源代码中。事实上,

hash(float('-inf'))

-271828在python 2中返回或大约为-e(现在为-314159)。

将所有时间中两个最著名的无理数用作哈希值的事实使得它不太可能是巧合。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.