web-dev-qa-db-ja.com

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

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のハッシュは負の値を取ることができます。

99
Colonel Panic

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
_
72
Kasrâmvd

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

値modの数をとるだけでハッシュを作成する必要がある場合は、大きなメルセンヌ素数が適切な選択です。計算が簡単で、可能性の均等な分布を保証します。 (私は個人的にこのようにハッシュを作成しませんが)

浮動小数点数のモジュラスを計算することは特に便利です。整数に2^xを掛ける指数成分があります。 2^61 = 1 mod 2^61-1なので、考慮する必要があるのは(exponent) mod 61だけです。

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

78
Matt Timmermans

ハッシュ関数は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。

9
Andriy Ivaneyko

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;
}
0
Jieter