Pythonの ハッシュ関数 で遊んでいます。小さな整数の場合、常にhash(n) == n
と表示されます。ただし、これは多数には適用されません。
>>> hash(2**100) == 2**100
False
ハッシュは値の範囲が有限であることを理解しています。その範囲は何ですか?
バイナリ検索 を使用して最小数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
2305843009213693951の特別な点は何ですか? sys.maxsize == 9223372036854775807
より小さいことに注意してください
編集:Python 3を使用しています。Python 2で同じバイナリ検索を実行し、異なる結果2147483648を取得しました。これはsys.maxint+1
です
また、[hash(random.random()) for i in range(10**6)]
で遊んで、ハッシュ関数の範囲を推定しました。最大値は、常に上記のn未満です。最小を比較すると、Python 3のハッシュは常に正の値であるように見えますが、Python 2のハッシュは負の値を取ることができます。
python _pyhash.c
_)のドキュメントに基づく ファイル:
数値型の場合、数値xのハッシュは、素数_
P = 2**_PyHASH_BITS - 1
_を法とするxの減少に基づいています。 xとyの型が異なっていても、xとyが数値的に等しい場合は常にhash(x) == hash(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
_
たとえば、64ビットLinuxプラットフォームでは、まずプラットフォームに基づいてすべて削減されます。61-1、_2305843009213693951
_:
_>>> 2**61 - 1
2305843009213693951
_
また、_math.frexp
_を使用して、_sys.maxint
_の仮数と指数を取得できます。これは、64ビットマシンの場合、max intが2であることを示します。63:
_>>> import math
>>> math.frexp(sys.maxint)
(0.5, 64)
_
そして、簡単なテストで違いを確認できます。
_>>> hash(2**62) == 2**62
True
>>> hash(2**63) == 2**63
False
_
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)
>>>
_
前の行で説明したモジュラスの他に、次のようにinf
値を取得することもできます。
_>>> hash(float('inf'))
314159
>>> sys.hash_info.inf
314159
_
2305843009213693951
は2^61 - 1
です。 64ビットに収まる最大のメルセンヌ素数です。
値modの数をとるだけでハッシュを作成する必要がある場合は、大きなメルセンヌ素数が適切な選択です。計算が簡単で、可能性の均等な分布を保証します。 (私は個人的にこのようにハッシュを作成しませんが)
浮動小数点数のモジュラスを計算することは特に便利です。整数に2^x
を掛ける指数成分があります。 2^61 = 1 mod 2^61-1
なので、考慮する必要があるのは(exponent) mod 61
だけです。
ハッシュ関数は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
_
一方、_2**200
_は_sys.maxint
_のn
倍です-私の推測では、ハッシュはその範囲のプレーン整数で停止するまで_-sys.maxint..+sys.maxint
_の範囲をn回超えます上記のコードスニペットで。
したがって、一般的に、n <= sys.maxint:
_hash(sys.maxint*n) == -sys.maxint*(n%2) + 2*(n%2)*sys.maxint - n/2 - (n + 1)%2 ## True
_
注:これはpython 2。
-1
以外の値を返すだけで、-2
を返します。
static long
int_hash(PyIntObject *v)
{
/* XXX If this is changed, you also need to change the way
Python's long, float and complex types are hashed. */
long x = v -> ob_ival;
if (x == -1)
x = -2;
return x;
}