Pythonの無限大のハッシュには、piに一致する数字があります。
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
それは単なる偶然ですか、それとも意図的なものですか?
sys.hash_info
。イースターエッグ?
-314159
。そのことを忘れてしまいました。
Pythonの無限大のハッシュには、piに一致する数字があります。
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
それは単なる偶然ですか、それとも意図的なものですか?
sys.hash_info
。イースターエッグ?
-314159
。そのことを忘れてしまいました。
回答:
_PyHASH_INF
はに等しい定数として定義され314159
ます。
これについての議論や理由を示すコメントは見つかりません。多かれ少なかれ恣意的に選ばれたと思います。他のハッシュに同じ意味のある値を使用しない限り、それは問題ではないと思います。
hash(314159)
も314159
です。また、Pythonの3に、してみてくださいhash(2305843009214008110) == 314159
(この入力がある314159 + sys.hash_info.modulus
など)
概要:偶然ではありません。PythonのデフォルトのCPython実装では_PyHASH_INF
314159としてハードコードされており、2000年にTim Petersによって(明らかにπの数字から)任意の値として選択されました。
値は、hash(float('inf'))
数値型の組み込みハッシュ関数のシステムに依存するパラメータの一つであり、また、利用可能であるとしてsys.hash_info.inf
Pythonの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
コードの点で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
歴史の面では、の最初の言及314159
Pythonコードでは、この文脈では、(あなたがこれを見つけることができますgit bisect
かgit log -S 314159 -p
によって追加されました)ティム・ピーターズ今コミットされたもので、2000年8月に39dce293をしてcpython
gitのリポジトリ。
コミットメッセージは言う:
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進数から形成されることに注意してください。
関連する後のコミット:
2010年4月のMark Dickinson(また)によって、Decimal
型を同様に動作させる
2010年4月のMark Dickinson(また)による、このチェックを先頭に移動してテストケースを追加する
2010年5月にMark Dickinsonにより、問題8188としてハッシュ関数を完全に現在の実装に書き直しましたが、この特殊なケースを保持し、定数に名前を付けています_PyHASH_INF
(Pythonの場合とは異なり、Python 3のhash(float('-inf'))
戻り値-314159
である271828も削除さ-271828
れています)2)
2011年1月のレイモンドヘッティンガーによる、 Python 3.2の「新機能」にsys.hash_info
上記の値を示す明示的な例を追加。(こちらをご覧ください。)
2012年3月にStefan Krahにより Decimalモジュールを変更しましたが、このハッシュを保持しています。
2013年11月におけるキリスト教のHeimesすることにより、の定義を移動する_PyHASH_INF
からInclude/pyport.h
にInclude/pyhash.h
それが今住んでいるところ。
hash(42.0)
と同じになるようにhash(42)
も、同じhash(Decimal(42))
とhash(complex(42))
してhash(Fraction(42, 1))
。(Mark Dickinsonによる)ソリューションはエレガントなIMOです。任意の有理数に対して機能する数学関数を定義し、浮動小数点数も有理数であるという事実を使用します。
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値の特殊なケースを必要とする、浮動小数点形式自体の性質に由来します。
確かに、
sys.hash_info.inf
を返します314159
。値は生成されず、ソースコードに組み込まれます。実際には、
hash(float('-inf'))
-271828
Python 2では、または約-eを返します(現在は-314159です)。
史上最も有名な2つの不合理な数がハッシュ値として使用されるという事実は、偶然である可能性を非常に低くします。
hash(float('nan'))
もの0
。