science modules for data commands python python-2.7 python-3.x hash python-internals

modules - python libraries list



¿Cuándo es hash(n)== n en Python? (4)

He estado jugando con la función hash de Python. Para enteros pequeños, parece hash(n) == n siempre. Sin embargo, esto no se extiende a grandes cantidades:

>>> hash(2**100) == 2**100 False

No me sorprende, entiendo que el hash toma un rango finito de valores. ¿Cuál es ese rango?

Intenté usar la búsqueda binaria para encontrar el número más pequeño hash(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

¿Qué tiene de especial 2305843009213693951? sys.maxsize == 9223372036854775807 que es menor que sys.maxsize == 9223372036854775807

Editar: estoy usando Python 3. Ejecuté la misma búsqueda binaria en Python 2 y obtuve un resultado diferente 2147483648, que noto es sys.maxint+1

También jugué con [hash(random.random()) for i in range(10**6)] para estimar el rango de la función hash. El máximo está constantemente por debajo de n arriba. Comparando el mínimo, parece que el hash de Python 3 siempre se valora positivamente, mientras que el hash de Python 2 puede tomar valores negativos.


Basado en la documentación de Python en el archivo pyhash.c :

Para los tipos numéricos, el hash de un número x se basa en la reducción del módulo x el primo P = 2**_PyHASH_BITS - 1 . Está diseñado para que hash(x) == hash(y) siempre que x e y sean numéricamente iguales, incluso si x e y tienen diferentes tipos.

Entonces, para una máquina de 64/32 bits, la reducción sería 2 _PyHASH_BITS - 1, pero ¿qué es _PyHASH_BITS ?

Puede encontrarlo en el archivo de encabezado pyhash.h que para una máquina de 64 bits se ha definido como 61 (puede leer más explicaciones en el archivo pyconfig.h ).

#if SIZEOF_VOID_P >= 8 # define _PyHASH_BITS 61 #else # define _PyHASH_BITS 31 #endif

En primer lugar, se basa en su plataforma, por ejemplo, en mi plataforma Linux de 64 bits, la reducción es 2 61 -1, que es 2305843009213693951 :

>>> 2**61 - 1 2305843009213693951

También puede usar math.frexp para obtener la mantisa y el exponente de sys.maxint que para una máquina de 64 bits muestra que max int es 2 63 :

>>> import math >>> math.frexp(sys.maxint) (0.5, 64)

Y puedes ver la diferencia con una simple prueba:

>>> hash(2**62) == 2**62 True >>> hash(2**63) == 2**63 False

Lea la documentación completa sobre el algoritmo de hash de Python https://github.com/python/cpython/blob/master/Python/pyhash.c#L34

Como se mencionó en el comentario, puede usar sys.hash_info (en python 3.X) que le dará una secuencia de estructura de parámetros utilizados para calcular hashes.

>>> 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) >>>

Junto con el módulo que he descrito en las líneas anteriores, también puede obtener el valor inf siguiente manera:

>>> hash(float(''inf'')) 314159 >>> sys.hash_info.inf 314159



La función hash devuelve plain int que significa que el valor devuelto es mayor que -sys.maxint y menor que sys.maxint , lo que significa que si pasa sys.maxint + x a su resultado sería -sys.maxint + (x - 2) .

hash(sys.maxint + 1) == sys.maxint + 1 # False hash(sys.maxint + 1) == - sys.maxint -1 # True hash(sys.maxint + sys.maxint) == -sys.maxint + sys.maxint - 2 # True

Mientras tanto, 2**200 es n veces mayor que sys.maxint : supongo que el hash sobrepasará el rango -sys.maxint..+sys.maxint n veces hasta que se detenga en un entero simple en ese rango, como en los fragmentos de código encima..

Entonces, generalmente, para cualquier n <= sys.maxint :

hash(sys.maxint*n) == -sys.maxint*(n%2) + 2*(n%2)*sys.maxint - n/2 - (n + 1)%2 ## True

Nota: esto es cierto para Python 2.


2305843009213693951 es 2^61 - 1 2305843009213693951 . Es el primer Mersenne más grande que cabe en 64 bits.

Si tiene que hacer un hash simplemente tomando el valor mod algún número, entonces un gran Mersenne prime es una buena opción: es fácil de calcular y garantiza una distribución uniforme de posibilidades. (Aunque personalmente nunca haría un hash de esta manera)

Es especialmente conveniente calcular el módulo para números de coma flotante. Tienen un componente exponencial que multiplica el número entero por 2^x . Dado que 2^61 = 1 mod 2^61-1 , solo necesita considerar el (exponent) mod 61 .

Ver: https://en.wikipedia.org/wiki/Mersenne_prime