J'ai joué avec la fonction de hachage de Python . Pour les petits entiers, il apparaît hash(n) == n
toujours. Cependant, cela ne s'étend pas aux grands nombres:
>>> hash(2**100) == 2**100
False
Je ne suis pas surpris, je comprends que le hachage prend une plage finie de valeurs. Quelle est cette plage?
J'ai essayé d'utiliser la recherche binaire pour trouver le plus petit nombrehash(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
Quelle est la particularité du 2305843009213693951? Je note que c'est moins quesys.maxsize == 9223372036854775807
Edit: J'utilise Python 3. J'ai exécuté la même recherche binaire sur Python 2 et j'ai obtenu un résultat différent 2147483648, que je note est sys.maxint+1
J'ai également joué avec [hash(random.random()) for i in range(10**6)]
pour estimer la plage de fonction de hachage. Le max est systématiquement inférieur à n ci-dessus. En comparant le min, il semble que le hachage de Python 3 a toujours une valeur positive, alors que le hachage de Python 2 peut prendre des valeurs négatives.
la source
n+1 == 2**61-1
n
concerne toute la plage 64 bits int.2147483647
égal àsys.maxint
(nonsys.maxint+1
), et si 'n = 0b11111111111111111111111111111111111111111111111111111111111' alors nonn+1 == 2**61
oun == 2**61-1
(pasn+1 == 2**61-1
)?Réponses:
Basé sur la documentation python dans le
pyhash.c
fichier:Donc pour une machine 64/32 bits, la réduction serait de 2 _PyHASH_BITS - 1, mais qu'est-ce que c'est
_PyHASH_BITS
?Vous pouvez le trouver dans le
pyhash.h
fichier d' en- tête qui pour une machine 64 bits a été défini comme 61 (vous pouvez lire plus d'explications dans lepyconfig.h
fichier).Donc, tout d'abord, c'est basé sur votre plate-forme par exemple dans ma plate-forme Linux 64 bits, la réduction est de 2 61 -1, ce qui est
2305843009213693951
:Vous pouvez également utiliser
math.frexp
pour obtenir la mantisse et l'exposantsys.maxint
dont pour une machine 64 bits montre que max int est 2 63 :Et vous pouvez voir la différence par un simple test:
Lisez la documentation complète sur l'algorithme de hachage python https://github.com/python/cpython/blob/master/Python/pyhash.c#L34
Comme mentionné dans le commentaire, vous pouvez utiliser
sys.hash_info
(en python 3.X) qui vous donnera une séquence struct de paramètres utilisés pour le calcul des hachages.En plus du module que j'ai décrit dans les lignes précédentes, vous pouvez également obtenir la
inf
valeur comme suit:la source
sys.hash_info
, par souci d'exhaustivité.2305843009213693951
est2^61 - 1
. C'est le plus grand Mersenne prime qui tient sur 64 bits.Si vous devez faire un hachage simplement en prenant la valeur mod un certain nombre, alors un grand Mersenne prime est un bon choix - il est facile à calculer et assure une distribution uniforme des possibilités. (Bien que personnellement je ne ferais jamais de hasch de cette façon)
Il est particulièrement pratique de calculer le module pour les nombres à virgule flottante. Ils ont une composante exponentielle qui multiplie le nombre entier par
2^x
. Depuis2^61 = 1 mod 2^61-1
, il vous suffit de considérer le(exponent) mod 61
.Voir: https://en.wikipedia.org/wiki/Mersenne_prime
la source
x == y
garantieshash(x) == hash(y)
entre les types? (Les nombres commeDecimal('1e99999999')
sont particulièrement problématiques, par exemple: vous ne voulez pas avoir à les étendre à l'entier correspondant avant le hachage.)int
,float
,Decimal
et desFraction
objets et quix == y
impliquehash(x) == hash(y)
même quandx
ety
avoir différents types impose certaines contraintes assez sévères. S'il s'agissait simplement d'écrire une fonction de hachage pour les entiers, sans se soucier des autres types, ce serait une tout autre affaire.La fonction de hachage renvoie un entier simple, ce qui signifie que la valeur renvoyée est supérieure à
-sys.maxint
et inférieure àsys.maxint
, ce qui signifie que si vous lui passezsys.maxint + x
le résultat, ce serait-sys.maxint + (x - 2)
.En attendant,
2**200
c'est unn
fois plus grand quesys.maxint
- je suppose que le hachage dépasserait la plage-sys.maxint..+sys.maxint
n fois jusqu'à ce qu'il s'arrête sur un entier brut dans cette plage, comme dans les extraits de code ci-dessus.Donc généralement, pour tout n <= sys.maxint :
Remarque: cela est vrai pour python 2.
la source
sys.maxint
et qui utilise une fonction de hachage différente).L' implémentation du type int dans cpython peut être trouvée ici.
Il renvoie simplement la valeur, sauf pour
-1
, puis renvoie-2
:la source
PyLong
plutôt que parPyInt
.