Pythonのhash(n)== nはいつですか?


100

私は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を使用しています。Python2で同じバイナリ検索を実行し、異なる結果2147483648を取得しました。 sys.maxint+1

また[hash(random.random()) for i in range(10**6)]、ハッシュ関数の範囲を推定するために遊んだ。最大値は常に上記のn未満です。最小値を比較すると、Python 3のハッシュは常に正の値であるように見えますが、Python 2のハッシュは負の値を取ることができます。


9
数値のバイナリ表現を確認しましたか?
John Dvorak

3
'0b1111111111111111111111111111111111111111111111111111111111111'好奇心が強い!だから n+1 == 2**61-1
大佐パニック

2
システムに依存しているようです。私のpythonでは、ハッシュはn64ビットの整数範囲全体に対するものです。
ダニエル

1
ハッシュ値の明記された目的に注意してください。これらは、辞書ルックアップ中に辞書キーをすばやく比較するために使用されます。言い換えれば、実装定義であり、ハッシュ値を持つことができる多くの値よりも短いため、妥当な入力スペースでも衝突が発生する可能性があります。
CVn 2016年

2
ええと、(ではない)2147483647と等しくsys.maxintないsys.maxint+1、そして 'n = 0b1111111111111111111111111111111111111111111111111111111111111'の場合、等しくないn+1 == 2**61n == 2**61-1(ではないn+1 == 2**61-1)?
phoog 2016年

回答:


73

pyhash.cファイル内のpythonドキュメントに基づく:

数値型の場合、数値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

したがって、まず第一に、それはあなたのプラットフォームに基づいています。たとえば、私の64ビットLinuxプラットフォームでは、削減は2 61 -1です2305843009213693951

>>> 2**61 - 1
2305843009213693951

またmath.frexp、仮数と指数を取得するために使用できますsys.maxint。64ビットマシンの場合、最大整数は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

3
sys.hash_info完全を期すために、と言っておくとよいでしょう。
Mark Dickinson、2016年

78

2305843009213693951です2^61 - 1。これは64ビットに収まる最大のメルセンヌ素数です。

値modをいくつかの数値にするだけでハッシュを作成する必要がある場合は、大きなメルセンヌ素数が適切です。計算が簡単で、可能性の均一な分布が保証されます。(私は個人的にはこの方法でハッシュを作成することはありませんが)

浮動小数点数の係数を計算すると特に便利です。それらには、整数を掛ける指数コンポーネントがあり2^xます。以来2^61 = 1 mod 2^61-1、あなただけを考慮する必要があります(exponent) mod 61

参照:https : //en.wikipedia.org/wiki/Mersenne_prime


8
あなたはこの方法でハッシュを作ることは決してないと言っています。あなたはそれがint型、浮動小数点数、小数、画分のために計算することが合理的に効率的になりますな方法で行うことができる方法のための代替提案を持っていますし、確実にx == y保証hash(x) == hash(y)タイプ間を?(Decimal('1e99999999')たとえば、のような数値は特に問題があります:ハッシュする前に対応する整数に展開する必要はありません。)
Mark Dickinson

@MarkDickinson私は彼がこの単純で高速な高速ハッシュと、出力をランダムに見えるようにすることに関心がある暗号ハッシュとを区別しようとしているのではないかと思います。
マイクウンズワース2016年

4
@MarkDickinsonモジュラスは良い出発点ですが、私はそれをさらに混ぜ合わせ、特にいくつかの高いビットを低いビットに混ぜます。2の累乗で割り切れる整数のシーケンスを確認することは珍しいことではありません。2の累乗の容量を持つハッシュテーブルを確認することも珍しくありません。たとえば、Javaで、16で割り切れる整数のシーケンスがある場合、それらをHashMapのキーとして使用すると、バケットの1/16のみが使用されます(少なくとも、私が調べているソースのバージョンでは)。これらの問題を回避するために、ハッシュは少なくとも少しランダムに見えるべきです
Matt Timmermans

はい、ビット混合スタイルのハッシュは、数学にヒントを得たものよりはるかに優れています。ビット混合命令は非常に安価なので、同じコストで多くを使用できます。また、実際のデータには、ビットミキシングではうまく機能しないパターンがないようです。しかし、モジュラスにとって恐ろしいパターンがあります。
usr

9
@usr:確かに、少し混合ハッシュがここに実現不可能である:要件のハッシュ作業することをintfloatDecimalおよびFractionオブジェクトとそれがx == y意味しhash(x) == hash(y)ても、xおよびyいくつかのかなり厳しい制約を課すさまざまなタイプがあります。他の型を気にせずに整数のハッシュ関数を書くだけの場合は、まったく別の問題になります。
Mark Dickinson

9

ハッシュ関数は単純な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**200nそれよりも1 倍大きいですsys.maxint-私の推測では、ハッシュは-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に当てはまります。


8
これはPython 2には当てはまるかもしれませんがsys.maxint、Python 3(これにはがなく、別のハッシュ関数を使用しています)には当てはまりません。
インタージェイ2016年

0

cpythonのint型実装はここにあります。

を除いて-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;
}

6
これには、ではPyLongなくによって実装される大きな値は含まれませんPyInt
interjay
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.