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

wim*_*wim 236 python math floating-point hash pi

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

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Run Code Online (Sandbox Code Playgroud)

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

Shr*_*saR 214

简介:这不是巧合;在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
Run Code Online (Sandbox Code Playgroud)

与PyPy的结果相同。)


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

>>> 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
Run Code Online (Sandbox Code Playgroud)

其中_PyHASH_INF定义为 314159:

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
Run Code Online (Sandbox Code Playgroud)

从历史的角度来看,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添加了以下几行:

#define _PyHASH_INF 314159
Run Code Online (Sandbox Code Playgroud)

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

相关的以后的提交:

  • 为-Inf选择-271828可以消除对pi关联是偶然的怀疑。 (43认同)
  • @RussellBorogove不,但是它使它的可能性降低了大约一百万倍;) (24认同)
  • @cmaster:请参见上面所说的2010年5月的部分,即有关[散列数字类型]的文档部分(https://docs.python.org/3/library/stdtypes.html#hashing-of-numeric-types )和[issue 8188](https://bugs.python.org/issue8188)—想法是我们希望`hash(42.0)`与`hash(42)`相同,也与`hash相同(Decimal(42))`和`hash(complex(42))`和`hash(Fraction(42,1))` 该解决方案(由Mark Dickinson提出)是一种优雅的IMO:定义适用于任何有理数的数学函数,并利用浮点数也是有理数这一事实。 (8认同)
  • @RussellBorogove好吧,在源代码中看到“ 314159”之后,我的想法毫无疑问:-)我的意思是,概率P(程序员想要任意的东西,并选择了几位知名常数)>> P(程序员随机输入一些数字)* P(碰巧是一个特定的六位数字序列),其中第二个因子是1/1000000,但即使第一个因子也可能已经小于LHS!(我已经多次写过“随机”数字,例如123456或314159,但从未签入数字的随机字符串。)但是可以肯定的是,看到另一个常量也很不错。 (3认同)
  • @pipe让我们说“消除任何合理的疑问”并称其为“一天”。 (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`)。我们想要这样做的原因是:如果将d [x] = foo放入字典d中,则有x == y(例如42.0 == 42),但d [y]不是和d [x]一样,那么我们会有一个问题。大多数看似复杂的代码都来自浮点格式本身的性质,以正确地恢复小数部分,并且需要特殊情况下的inf和NaN值。 (2认同)

Pat*_*ugh 45

_PyHASH_INF定义为等于的常数314159

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

  • 小nitpick:从定义上讲,几乎不可避免的是,将相同的值用于其他哈希,例如,在这种情况下,“ hash(314159)”也是“ 314159”。也可以在Python 3中尝试使用hash(2305843009214008110)== 314159(此输入为314159 + sys.hash_info.modulus))等。 (3认同)
  • @ShreevatsaR我只是说,只要他们没有按照定义将这个值选择为其他值的哈希,那么选择一个有意义的值就不会增加哈希冲突的机会 (2认同)

Ale*_*ine 11

确实,

sys.hash_info.inf
Run Code Online (Sandbox Code Playgroud)

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

hash(float('-inf'))
Run Code Online (Sandbox Code Playgroud)

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

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