我想使用Python
hash()
函数从对象中获取整数哈希值。但内置的 hash()
可以给出负值,而我只想要正值。我希望它能够在 32 位和 64 位平台上正常工作。
即在 32 位 Python 上,
hash()
可以返回 -2**31
到 2**31 - 1
范围内的整数。
在 64 位系统上,hash()
可以返回 -2**63
到 2**63 - 1
范围内的整数。
但我想要 32 位系统上
0
到 2**32-1
范围内的哈希值,以及 64 位系统上 0
到 2**64-1
范围内的哈希值。
在 32 位或 64 位目标平台范围内,将哈希值转换为其等效正值的最佳方法是什么?
(上下文:我正在尝试创建一个新的
random.Random
样式类。根据 random.Random.seed()
文档,种子“可选参数 x 可以是任何可哈希对象。”所以我想复制该功能,除了我的种子算法无法处理负整数值,只能处理正整数值。)
sys.maxsize
:
>>> import sys
>>> sys.maxsize
9223372036854775807L
>>> hash('asdf')
-618826466
>>> hash('asdf') % ((sys.maxsize + 1) * 2)
18446744073090725150L
ctypes.c_size_t
:
>>> import ctypes
>>> ctypes.c_size_t(hash('asdf')).value
18446744073090725150L
由于显而易见的原因,仅使用
sys.maxsize
是错误的(它是 `2*n-1 而不是 2*n),但修复很简单:
h = hash(obj)
h += sys.maxsize + 1
出于性能原因,您可能希望将 sys.maxsize + 1 拆分为两个单独的分配,以避免为大多数负数临时创建长整数。虽然我怀疑这会很重要
(编辑:一开始我以为你一直想要一个 32 位值)
只需将其与所需尺寸的蒙版进行“与”即可。一般来说
sys.maxsize
已经是这样的掩码,因为它是 2 减 1 的幂。
import sys
assert (sys.maxsize & (sys.maxsize+1)) == 0 # checks that maxsize+1 is a power of 2
new_hash = hash & sys.maxsize
要支持带签名和未签名的平台
hash()
,您可以使用
hash(x) % 2**sys.hash_info.width
这将使用 Python 报告的实际哈希宽度,而不是根据 Python 认为的平台上列表的最大大小进行猜测。
注意,如果
x
是接近 0 的整数,则 hash(x)
是恒等函数,即仅传递该值。一般来说,在使用 Python 3.6 的 64 位上,似乎可以计算
(abs(x) % m) * (-1 if x<0 else 1)
与
m=2**61-1
,第九个梅森素数。这在某些应用程序中可能会出现问题。