Der Hash der Unendlichkeit in Python hat übereinstimmende Ziffern Pi:
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Ist das nur Zufall oder Absicht?
Wim
Der Hash der Unendlichkeit in Python hat übereinstimmende Ziffern Pi:
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Ist das nur Zufall oder Absicht?
ShreevatsaR
Zusammenfassung: Es ist kein Zufall; _PyHASH_INF
ist als 314159 fest codiert in der standardmäßigen CPython-Implementierung von Python und wurde als willkürlicher Wert ausgewählt (offensichtlich aus den Ziffern von π) von Tim Peters im Jahr 2000.
Der Wert von hash(float('inf'))
ist einer der systemabhängigen Parameter der eingebauten Hash-Funktion für numerische Typen und ist ebenfalls verfügbar wie sys.hash_info.inf
in Python3:
>>> 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
(Gleiche Ergebnisse mit PyPy zu.)
In Bezug auf Code, hash
ist eine eingebaute Funktion. Der Aufruf für ein Python-Float-Objekt ruft die Funktion auf, deren Zeiger durch die gegeben ist tp_hash
Attribut vom eingebauten Schwimmertyp (PyTypeObject PyFloat_Type
), die ist das float_hash
Funktion, definiert wie return _Py_HashDouble(v->ob_fval)
was wiederum hat
if (Py_IS_INFINITY(v))
return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
wo _PyHASH_INF
ist definiert als 314159:
#define _PyHASH_INF 314159
In Bezug auf die Geschichte, die erste Erwähnung von 314159
in diesem Zusammenhang im Python-Code (findest du mit git bisect
oder git log -S 314159 -p
) wurde hinzugefügt von Tim Peters im August 2000, in dem, was jetzt begangen wird 39dce293 in dem cpython
Git-Repository.
Die Commit-Nachricht sagt:
Fix für http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470. Das war ein irreführender Fehler – der wahre „Fehler“ war das
hash(x)
gab einen Fehler zurück, wennx
ist eine Unendlichkeit. Das wurde behoben. Neu hinzugefügtPy_IS_INFINITY
Makro zu
pyport.h
. Der Code wurde neu angeordnet, um die zunehmende Duplizierung beim Hashing von Gleitkommazahlen und komplexen Zahlen zu reduzieren, wodurch Trents früherer Versuch, dies zu tun, zu einer logischen Schlussfolgerung geführt wurde. Äußerst seltener Fehler behoben, bei dem das Hashing von Floats -1 zurückgeben konnte, selbst wenn kein Fehler vorlag (habe keine Zeit damit verschwendet, einen Testfall zu erstellen, es war einfach aus dem Code ersichtlich, dass es könnte passieren). Verbesserter komplexer Hash, damit
hash(complex(x, y))
nicht systematisch gleichhash(complex(y, x))
mehr.
Insbesondere bei diesem Commit riss er den Code heraus static long float_hash(PyFloatObject *v)
in Objects/floatobject.c
und machte es einfach return _Py_HashDouble(v->ob_fval);
und in der Definition von long _Py_HashDouble(double v)
in Objects/object.c
er fügte die Zeilen hinzu:
if (Py_IS_INFINITY(intpart))
/* can't convert to long int -- arbitrary */
v = v < 0 ? -271828.0 : 314159.0;
Also, wie gesagt, es war eine willkürliche Wahl. Beachten Sie, dass 271828 aus den ersten Dezimalstellen von gebildet wird e.
Zugehörige spätere Commits:
Von Mark Dickinson im April 2010 (Auch), machen die Decimal
Typ verhalten sich ähnlich
Von Mark Dickinson im April 2010 (Auch), dieses Häkchen nach oben verschieben und Testfälle hinzufügen
Von Mark Dickinson im Mai 2010 wie Ausgabe 8188komplett umschreiben der Hash-Funktion zu seine aktuelle Umsetzungbehält aber diesen Sonderfall bei und gibt der Konstante einen Namen _PyHASH_INF
(Entfernt auch die 271828, weshalb in Python 3 hash(float('-inf'))
kehrt zurück -314159
statt -271828
wie in Python 2)
Von Raymond Hettinger im Januar 2011Hinzufügen eines expliziten Beispiels in den “Was ist neu” für Python 3.2 von sys.hash_info
zeigt den oben genannten Wert. (Sehen hier.)
Von Stefan Krah im März 2012 Ändern des Decimal-Moduls, aber Beibehalten dieses Hashs.
Von Christian Heimes im November 2013verschoben die Definition von _PyHASH_INF
aus Include/pyport.h
zu Include/pyhash.h
wo es jetzt lebt.
Die Wahl von -271828 für -Inf beseitigt jeden Zweifel, dass die pi-Verknüpfung zufällig war.
– Russell Borogove
21. Mai 2019 um 4:30 Uhr
@RussellBorogove Nein, aber es macht es ungefähr eine Million Mal unwahrscheinlicher;)
– Rohr
21. Mai 2019 um 15:01 Uhr
@cmaster: Siehe den Teil oben, wo Mai 2010 steht, nämlich den Dokumentationsabschnitt Hashing numerischer Typen und Ausgabe 8188 – Die Idee ist, dass wir wollen hash(42.0)
gleich sein wie hash(42)
auch das gleiche wie hash(Decimal(42))
und hash(complex(42))
und hash(Fraction(42, 1))
. Die Lösung (von Mark Dickinson) ist meines Erachtens elegant: Definieren Sie eine mathematische Funktion, die für jede rationale Zahl funktioniert, und nutzen Sie die Tatsache, dass Gleitkommazahlen auch rationale Zahlen sind.
– ShreevatsaR
22. Mai 2019 um 13:22 Uhr
@ShreevatsaR Ah, danke. Obwohl ich diese Gleichheiten nicht garantieren wollte, ist es gut zu wissen, dass es eine gute, solide und logische Erklärung für den scheinbar komplexen Code gibt 🙂
– Cmaster – Wiedereinsetzung von Monica
22. Mai 2019 um 14:00 Uhr
@cmaster Die Hash-Funktion für ganze Zahlen ist einfach hash(n) = n % M
wobei M = (2^61 – 1). Dies wird für rationales n zu verallgemeinert hash(p/q) = (p/q) mod M
wobei die Division modulo M interpretiert wird (mit anderen Worten: hash(p/q) = (p * inverse(q, M)) % M
). Der Grund, warum wir das wollen: wenn in ein Diktat d
wir stellen d[x] = foo
und dann haben wir x==y
(zB 42.0==42) aber d[y]
ist nicht dasselbe wie d[x]
, dann hätten wir ein Problem. Der größte Teil des scheinbar komplexen Codes stammt aus der Natur des Fließkommaformats selbst, um den Bruch ordnungsgemäß wiederherzustellen, und benötigt Sonderfälle für inf- und NaN-Werte.
– ShreevatsaR
22. Mai 2019 um 23:22 Uhr
Patrick Hagh
_PyHASH_INF
ist als Konstante definiert gleicht 314159
.
Ich kann keine Diskussion darüber oder Kommentare mit Begründung finden. Ich denke, es wurde mehr oder weniger willkürlich gewählt. Ich stelle mir vor, dass es keine Rolle spielen sollte, solange sie nicht denselben aussagekräftigen Wert für andere Hashes verwenden.
Kleine Spitzfindigkeit: Es ist per Definition fast unvermeidlich, dass derselbe Wert für andere Hashes verwendet wird, z. B. in diesem Fall hash(314159)
ist auch 314159
. Versuchen Sie auch in Python 3, hash(2305843009214008110) == 314159
(Diese Eingabe ist 314159 + sys.hash_info.modulus
) etc.
– ShreevatsaR
21. Mai 2019 um 11:43 Uhr
@ShreevatsaR Ich meinte nur, dass die Auswahl eines sinnvollen Werts wie diesem die Wahrscheinlichkeit von Hash-Kollisionen nicht erhöht, solange sie diesen Wert nicht per Definition als Hash anderer Werte auswählen
– Patrick Hagh
21. Mai 2019 um 13:37 Uhr
Alec
In der Tat,
sys.hash_info.inf
kehrt zurück 314159
. Der Wert wird nicht generiert, sondern in den Quellcode eingebaut. In der Tat,
hash(float('-inf'))
kehrt zurück -271828
oder ungefähr -e, in Python 2 (es ist jetzt -314159).
Die Tatsache, dass die beiden berühmtesten irrationalen Zahlen aller Zeiten als Hash-Werte verwendet werden, macht es sehr unwahrscheinlich, dass es sich um einen Zufall handelt.
Nicht sicher, aber ich vermute, dass es so beabsichtigt ist wie
hash(float('nan'))
Sein0
.– cs95
20. Mai 2019 um 20:04 Uhr
Hmm, keine Erwähnung darüber in
sys.hash_info
. Osterei?– Wim
20. Mai 2019 um 20:09 Uhr
Fragen Sie Tim Peters. Hier ist der Commit, in dem er diese Konstante vor 19 Jahren eingeführt hat: github.com/python/cpython/commit/…. Ich habe diese speziellen Werte beibehalten, als ich den numerischen Hash in überarbeitet habe bugs.python.org/issue8188
– Mark Dickinson
20. Mai 2019 um 20:38 Uhr
@MarkDickinson Danke. Es sieht so aus, als hätte Tim auch die Ziffern von verwendet e ursprünglich für Hash von -inf.
– Wim
20. Mai 2019 um 20:42 Uhr
@wim Ah ja, stimmt. Und anscheinend habe ich das geändert
-314159
. Das hatte ich vergessen.– Mark Dickinson
20. Mai 2019 um 20:44 Uhr