なぜPythonの無限のハッシュはπの桁を持つのですか?


241

Pythonの無限大のハッシュには、piに一致する数字があります。

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159

それは単なる偶然ですか、それとも意図的なものですか?


9
特定のが、私の推測では、それはのように意図的だということになりませんhash(float('nan'))もの0
cs95

1
うーん、それについては言及されていませんsys.hash_info。イースターエッグ?
wim

123
ティム・ピーターズに聞いてください。19年前に彼がこの定数を導入したコミットは次のとおりです:github.com/python/cpython/commit/…bugs.python.org/issue8188で
Mark Dickinson

8
@MarkDickinsonありがとう。Timは、元々-infのハッシュにeの数字も使用していたようです。
wim

17
@wimああ、そうです。そしてどうやら私はそれをに変更しました-314159。そのことを忘れてしまいました。
Mark Dickinson、

回答:


47

_PyHASH_INFはに等しい定数として定義され314159ます。

これについての議論や理由を示すコメントは見つかりません。多かれ少なかれ恣意的に選ばれたと思います。他のハッシュに同じ意味のある値を使用しない限り、それは問題ではないと思います。


6
小さなnitpick:定義上、同じ値が他のハッシュにも使用されることはほぼ避けられません。たとえば、この場合hash(314159)314159です。また、Pythonの3に、してみてくださいhash(2305843009214008110) == 314159(この入力がある314159 + sys.hash_info.modulusなど)
ShreevatsaR

3
@ShreevatsaR私は、定義によって他の値のハッシュになるようにこの値を選択しない限り、このような意味のある値を選択しても、ハッシュの衝突の可能性が高くならないことを意味しました
Patrick Haugh

220

概要:偶然ではありません。PythonのデフォルトのCPython実装では_PyHASH_INF314159としてハードコードされており、2000年にTim Petersによって(明らかにπの数字から)任意の値として選択されました。


値は、hash(float('inf'))数値型の組み込みハッシュ関数のシステムに依存するパラメータの一つであり、また、利用可能であるとしてsys.hash_info.infPythonの3:

>>> import sys
>>> 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)
>>> sys.hash_info.inf
314159

PyPyでも同じ結果になります。)


コードの点でhashは、は組み込み関数です。そのポインタによって与えられる関数のPythonフロートオブジェクトにそれを呼び出し、呼び出しtp_hashた属性内蔵フロートタイプ(のPyTypeObject PyFloat_Type)、あるfloat_hash関数、定義されたようにreturn _Py_HashDouble(v->ob_fval)順番に、持っているが

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;

どこ_PyHASH_INFされるように定義 314159:

#define _PyHASH_INF 314159

歴史の面では、の最初の言及314159Pythonコードでは、この文脈では、(あなたがこれを見つけることができますgit bisectgit log -S 314159 -pによって追加されました)ティム・ピーターズ今コミットされたもので、2000年8月に39dce293をしてcpythongitのリポジトリ。

コミットメッセージは言う:

http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470の修正。これは誤解を招くバグでした-真の「バグ」は、無限大のhash(x)場合にエラーを返すバグでしたx。修正しました。新しいPy_IS_INFINITYマクロをに追加しました pyport.h。浮動小数点数と複素数のハッシュの増加する重複を減らすためにコードを再配置し、トレントの初期の試みを論理的な結論に押し上げました。エラーが発生していなくても、floatのハッシュが-1を返すことがある非常にまれなバグを修正しました(テストケースを作成するのに時間を無駄にせず、コードからそれ発生すること明らかでした)。複雑なハッシュが改善されたため、 hash(complex(x, y))体系的に等しくなりhash(complex(y, x))ません。

特に、この中で彼はのコードリッピングコミットstatic long float_hash(PyFloatObject *v)中にObjects/floatobject.c、ちょうどそれを作ったreturn _Py_HashDouble(v->ob_fval);、との定義にlong _Py_HashDouble(double v)してObjects/object.c、彼はラインを追加しました:

        if (Py_IS_INFINITY(intpart))
            /* can't convert to long int -- arbitrary */
            v = v < 0 ? -271828.0 : 314159.0;

したがって、前述のように、それは任意の選択でした。271828はeの最初の数桁の10進数から形成されることに注意してください。

関連する後のコミット:


44
-Infに-271828を選択すると、pi関連付けが偶発的であったという疑いがなくなります。
ラッセルボロゴーブ

24
@RussellBorogoveいいえ、それはそれを約100万倍少なくします;)
パイプ

8
@cmaster:上、すなわちドキュメントのセクションそれは2010年5月と言う場所の上の部分を参照してください、数値型のハッシュ問題8188 -アイデアは、私たちが望むことであるhash(42.0)と同じになるようにhash(42)も、同じhash(Decimal(42))hash(complex(42))してhash(Fraction(42, 1))。(Mark Dickinsonによる)ソリューションはエレガントなIMOです。任意の有理数に対して機能する数学関数を定義し、浮動小数点数も有理数であるという事実を使用します。
ShreevatsaR

1
@ShreevatsaRあ、ありがとう。私はこれらの等式を保証するために世話をしていないだろうが、それの良い:-)一見複雑なコードのための良い、固体、および論理的な説明があることを知っている
REINSTATEモニカ- cmaster

2
@cmaster整数のハッシュ関数はhash(n) = n % M、M =(2 ^ 61-1 )です。これは、有理nに対して一般化さhash(p/q) = (p/q) mod Mれ、除算はMを法として解釈されます(つまり、hash(p/q) = (p * inverse(q, M)) % M)。これが必要な理由:dict dに入れd[x] = fooてからx==y(たとえば42.0 == 42)持ってd[y]いるが、と同じではないd[x]場合、問題が発生します。一見複雑に見えるコードのほとんどは、小数部を適切に回復し、inf値とNaN値の特殊なケースを必要とする、浮動小数点形式自体の性質に由来します。
ShreevatsaR

12

確かに、

sys.hash_info.inf

を返します314159。値は生成されず、ソースコードに組み込まれます。実際には、

hash(float('-inf'))

-271828Python 2では、または約-eを返します(現在は-314159です)。

史上最も有名な2つの不合理な数がハッシュ値として使用されるという事実は、偶然である可能性を非常に低くします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.