Ich habe mit Pythons Hash-Funktion gespielt . Bei kleinen Ganzzahlen wird es hash(n) == n
immer angezeigt . Dies erstreckt sich jedoch nicht auf große Zahlen:
>>> hash(2**100) == 2**100
False
Ich bin nicht überrascht, ich verstehe, dass Hash einen endlichen Wertebereich hat. Was ist das für ein Bereich?
Ich habe versucht, mithilfe der binären Suche die kleinste Zahl zu findenhash(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
Was ist das Besondere an 2305843009213693951? Ich stelle fest, es ist weniger alssys.maxsize == 9223372036854775807
Bearbeiten: Ich verwende Python 3. Ich habe die gleiche binäre Suche in Python 2 ausgeführt und ein anderes Ergebnis 2147483648 erhalten, das ich notiere sys.maxint+1
Ich habe auch mit gespielt [hash(random.random()) for i in range(10**6)]
, um den Bereich der Hash-Funktion abzuschätzen. Das Maximum liegt konstant unter n über. Wenn man die min vergleicht, scheint der Hash von Python 3 immer positiv bewertet zu sein, während der Hash von Python 2 negative Werte annehmen kann.
quelle
n+1 == 2**61-1
n
für den gesamten 64-Bit-Int-Bereich.2147483647
gleichsys.maxint
(nichtsys.maxint+1
), und wenn 'n = 0b1111111111111111111111111111111111111111111111111111111111111111' dann nichtn+1 == 2**61
odern == 2**61-1
(nichtn+1 == 2**61-1
)?Antworten:
Basierend auf der Python-Dokumentation in der
pyhash.c
Datei:Für eine 64/32-Bit-Maschine wäre die Reduzierung also 2 _PyHASH_BITS - 1, aber was ist das
_PyHASH_BITS
?Sie können es in finden
pyhash.h
Header - Datei , die für eine 64 - Bit - Maschine ist als 61 definiert (Sie können mehr Erklärung in Lesen -pyconfig.h
Datei).Zunächst einmal basiert es auf Ihrer Plattform, zum Beispiel auf meiner 64-Bit-Linux-Plattform beträgt die Reduzierung 2 61 -1, was bedeutet
2305843009213693951
:Sie können auch verwenden
math.frexp
, um die Mantisse und den Exponenten zu erhalten, vonsys.maxint
denen für eine 64-Bit-Maschine angezeigt wird, dass max int 2 63 ist :Und Sie können den Unterschied durch einen einfachen Test erkennen:
Lesen Sie die vollständige Dokumentation zum Python-Hashing-Algorithmus https://github.com/python/cpython/blob/master/Python/pyhash.c#L34
Wie im Kommentar erwähnt, können Sie
sys.hash_info
(in Python 3.X) eine Struktursequenz von Parametern verwenden, die für die Berechnung von Hashes verwendet werden.Neben dem Modul, das ich in den vorhergehenden Zeilen beschrieben habe, können Sie auch den
inf
folgenden Wert erhalten:quelle
sys.hash_info
Vollständigkeit halber wäre es schön zu erwähnen .2305843009213693951
ist2^61 - 1
. Es ist die größte Mersenne-Primzahl, die in 64 Bit passt.Wenn Sie einen Hash erstellen müssen, indem Sie den Wert mod auf eine bestimmte Zahl setzen, ist eine große Mersenne-Primzahl eine gute Wahl - sie ist einfach zu berechnen und sorgt für eine gleichmäßige Verteilung der Möglichkeiten. (Obwohl ich persönlich niemals einen Hash auf diese Weise machen würde)
Es ist besonders praktisch, den Modul für Gleitkommazahlen zu berechnen. Sie haben eine exponentielle Komponente, die die ganze Zahl mit multipliziert
2^x
. Da2^61 = 1 mod 2^61-1
müssen Sie nur die berücksichtigen(exponent) mod 61
.Siehe: https://en.wikipedia.org/wiki/Mersenne_prime
quelle
x == y
Garantienhash(x) == hash(y)
für Typen? (Zahlen wieDecimal('1e99999999')
sind besonders problematisch, zum Beispiel: Sie möchten sie nicht vor dem Hashing auf die entsprechende Ganzzahl erweitern müssen.)int
,float
,Decimal
undFraction
Objekte und dasx == y
bedeutet ,hash(x) == hash(y)
auch wennx
undy
haben verschiedene Arten , einige ziemlich strenge Beschränkungen auferlegt. Wenn es nur darum ginge, eine Hash-Funktion für ganze Zahlen zu schreiben, ohne sich um die anderen Typen zu kümmern, wäre es eine ganz andere Sache.Die Hash-Funktion gibt plain int zurück. Dies bedeutet, dass der zurückgegebene Wert größer
-sys.maxint
und kleiner alssys.maxint
ist. Wenn Sie alsosys.maxint + x
an ihn übergeben, ist das Ergebnis-sys.maxint + (x - 2)
.In der Zwischenzeit
2**200
ist es einn
Mal größer alssys.maxint
- ich vermute, dass Hash-sys.maxint..+sys.maxint
n-mal über den Bereich gehen würde, bis er auf einer einfachen Ganzzahl in diesem Bereich stoppt, wie in den obigen Code-Schnipsel.Im Allgemeinen gilt für jedes n <= sys.maxint :
Hinweis: Dies gilt für Python 2.
quelle
sys.maxint
und eine andere Hash-Funktion verwendet).Die Implementierung für den int-Typ in cpython finden Sie hier.
Es wird nur der Wert zurückgegeben, mit Ausnahme von
-1
: Dann wird Folgendes zurückgegeben-2
:quelle
PyLong
eher von als implementiert werdenPyInt
.