Python中的hash(n)== n是什么时候?

Col*_*nic 98 python hash python-2.7 python-3.x python-internals

我一直在玩Python的哈希函数.对于小整数,它hash(n) == n总是出现.然而,这并没有扩展到大数:

>>> hash(2**100) == 2**100
False
Run Code Online (Sandbox Code Playgroud)

我并不感到惊讶,我理解哈希需要一个有限范围的值.这个范围是多少?

我尝试使用二进制搜索来找到最小的数字hash(n) != n

>>> import codejamhelpers # pip install codejamhelpers
>>> help(codejamhelpers.binary_search)
Help on function binary_search in module codejamhelpers.binary_search:

binary_search(f, t)
    Given an increasing function :math:`f`, find the greatest non-negative integer :math:`n` such that :math:`f(n) \le t`. If :math:`f(n) > t` for all :math:`n \ge 0`, return None.

>>> f = lambda n: int(hash(n) != n)
>>> n = codejamhelpers.binary_search(f, 0)
>>> hash(n)
2305843009213693950
>>> hash(n+1)
0
Run Code Online (Sandbox Code Playgroud)

关于2305843009213693951有什么特别之处?我注意到它不到sys.maxsize == 9223372036854775807

编辑:我正在使用Python 3.我在Python 2上运行相同的二进制搜索并获得了不同的结果2147483648,我注意到的是 sys.maxint+1

我也玩了[hash(random.random()) for i in range(10**6)]估计哈希函数的范围.最大值始终低于n以上.比较min,似乎Python 3的散列总是正值,而Python 2的散列可以取负值.

Mat*_*ans 77

23058430092136939512^61 - 1.它是最大的Mersenne素数,适合64位.

如果你只需要通过取值mod来制作一个哈希值,那么一个大的梅森素数是一个不错的选择 - 它很容易计算并确保均匀分布的可能性.(虽然我个人不会这样做哈希)

计算浮点数的模数特别方便.它们具有指数成分,将整数乘以2^x.既然如此2^61 = 1 mod 2^61-1,你只需要考虑(exponent) mod 61.

请参阅:https://en.wikipedia.org/wiki/Mersenne_prime

  • @usr:当然,但是混合哈希在这里是不可行的:要求散列适用于`int`,`float`,`Decimal`和`Fraction`对象以及`x == y`暗示`hash(哈希值) x)== hash(y)`即使```和`y`有不同的类型,也会产生一些相当严格的约束.如果只是为整数编写哈希函数而不用担心其他类型,那将是完全不同的事情. (9认同)
  • 你说你永远不会这样做哈希.你是否有另外的建议,可以通过一种方式来完成它,使得计算整数,浮点数,小数,分数的合理有效率_and_确保`x == y`保证`hash(x)== hash(y) `跨越类型?(像`Decimal('1e99999999')这样的数字特别成问题,例如:您不希望在散列之前将它们扩展为相应的整数.) (8认同)
  • @MarkDickinson模数是一个很好的开始,但我会把它混合起来,特别是将一些高位混合到低位.看到整数序列被2的幂整除是很常见的.看到容量为2的哈希表也是常见的.例如,在Java中,如果你有一个可被16整除的整数序列,并且你将它们用作HashMap中的键,你只会使用1/16的桶(至少在我正在查看的源代码版本中)!我认为哈希应该至少有点随机,以避免这些问题 (4认同)

Kas*_*mvd 72

基于文件中的python文档pyhash.c:

对于数字类型,数字x的散列基于x模数的减少P = 2**_PyHASH_BITS - 1.它的设计使得 hash(x) == hash(y)只要x和y在数值上相等,即使x和y具有不同的类型.

因此,对于64/32位机器,减少量将是2 _PyHASH_BITS - 1,但是这是_PyHASH_BITS什么?

您可以在pyhash.h头文件中找到它,对于64位机器已定义为61(您可以在pyconfig.h文件中阅读更多说明).

#if SIZEOF_VOID_P >= 8
#  define _PyHASH_BITS 61
#else
#  define _PyHASH_BITS 31
#endif
Run Code Online (Sandbox Code Playgroud)

首先,它基于您的平台,例如在我的64位Linux平台上,减少为2 61 -1,即2305843009213693951:

>>> 2**61 - 1
2305843009213693951
Run Code Online (Sandbox Code Playgroud)

你也可以使用math.frexp它来获得sys.maxint64位机器的尾数和指数,表明max int是2 63:

>>> import math
>>> math.frexp(sys.maxint)
(0.5, 64)
Run Code Online (Sandbox Code Playgroud)

你可以通过简单的测试看出差异:

>>> hash(2**62) == 2**62
True
>>> hash(2**63) == 2**63
False
Run Code Online (Sandbox Code Playgroud)

阅读有关python哈希算法的完整文档https://github.com/python/cpython/blob/master/Python/pyhash.c#L34

正如评论中所提到的,您可以使用sys.hash_info(在python 3.X中),它将为您提供用于计算哈希的参数的结构序列.

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

除了我在前面inf几行中描述的模数外,您还可以获得如下值:

>>> hash(float('inf'))
314159
>>> sys.hash_info.inf
314159
Run Code Online (Sandbox Code Playgroud)

  • 为了完整性,提及`sys.hash_info`会很高兴. (3认同)

And*_*yko 9

散列函数返回plain int表示返回值大于-sys.maxint和小于sys.maxint,这意味着如果传递sys.maxint + x给它,结果将是-sys.maxint + (x - 2).

hash(sys.maxint + 1) == sys.maxint + 1 # False
hash(sys.maxint + 1) == - sys.maxint -1 # True
hash(sys.maxint + sys.maxint) == -sys.maxint + sys.maxint - 2 # True
Run Code Online (Sandbox Code Playgroud)

同时2**200n大于sys.maxint- 我的猜测是哈希将超过范围-sys.maxint..+sys.maxintn次,直到它停止在该范围内的普通整数,如上面的代码片段.

所以通常,对于任何n <= sys.maxint:

hash(sys.maxint*n) == -sys.maxint*(n%2) +  2*(n%2)*sys.maxint - n/2 - (n + 1)%2 ## True
Run Code Online (Sandbox Code Playgroud)

注意:这对于python 2来说是正确的.

  • 这可能适用于Python 2,但绝对不适用于Python 3(它没有`sys.maxint`,并且使用不同的散列函数). (8认同)