Tenho brincado com a função hash do Python . Para números inteiros pequenos, aparece hash(n) == n
sempre. No entanto, isso não se estende a grandes números:
>>> hash(2**100) == 2**100
False
Não estou surpreso, entendo que o hash assume uma faixa finita de valores. Qual é esse alcance?
Tentei usar a pesquisa binária para encontrar o menor númerohash(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
O que há de especial em 2305843009213693951? Eu noto que é menos quesys.maxsize == 9223372036854775807
Edit: Estou usando Python 3. Eu executei a mesma pesquisa binária no Python 2 e obtive um resultado diferente 2147483648, que observo é sys.maxint+1
Eu também brinquei com [hash(random.random()) for i in range(10**6)]
para estimar o intervalo da função hash. O máximo está consistentemente abaixo de n acima. Comparando o min, parece que o hash do Python 3 é sempre valorizado positivamente, enquanto o hash do Python 2 pode assumir valores negativos.
fonte
n+1 == 2**61-1
n
para todo o intervalo int de 64 bits.2147483647
igual asys.maxint
(nãosys.maxint+1
) e se 'n = 0b11111111111111111111111111111111111111111111111111111111111111111' então não én+1 == 2**61
oun == 2**61-1
(nãon+1 == 2**61-1
)?Respostas:
Com base na documentação do Python no
pyhash.c
arquivo:Portanto, para uma máquina de 64/32 bits, a redução seria 2 _PyHASH_BITS - 1, mas o que é
_PyHASH_BITS
?Você pode encontrá-lo no
pyhash.h
arquivo de cabeçalho, que para uma máquina de 64 bits foi definido como 61 (você pode ler mais explicações nopyconfig.h
arquivo).Então, em primeiro lugar, é baseado na sua plataforma, por exemplo, na minha plataforma Linux de 64 bits, a redução é 2 61 -1, que é
2305843009213693951
:Você também pode usar
math.frexp
para obter a mantissa e o expoente dasys.maxint
qual, para uma máquina de 64 bits, mostra que o int máximo é 2 63 :E você pode ver a diferença por um teste simples:
Leia a documentação completa sobre o algoritmo de hash Python https://github.com/python/cpython/blob/master/Python/pyhash.c#L34
Conforme mencionado no comentário, você pode usar
sys.hash_info
(em python 3.X), que fornecerá uma sequência de estrutura de parâmetros usados para calcular hashes.Juntamente com o módulo que descrevi nas linhas anteriores, você também pode obter o
inf
valor da seguinte maneira:fonte
sys.hash_info
, para completar.2305843009213693951
é2^61 - 1
. É o maior número primo de Mersenne que cabe em 64 bits.Se você tiver que fazer um hash apenas tomando o valor mod algum número, então um grande número primo de Mersenne é uma boa escolha - é fácil de calcular e garante uma distribuição uniforme de possibilidades. (Embora eu pessoalmente nunca fizesse um hash dessa forma)
É especialmente conveniente calcular o módulo para números de ponto flutuante. Eles têm um componente exponencial que multiplica o número inteiro por
2^x
. Uma vez que2^61 = 1 mod 2^61-1
você só precisa considerar o(exponent) mod 61
.Veja: https://en.wikipedia.org/wiki/Mersenne_prime
fonte
x == y
garantiashash(x) == hash(y)
entre os tipos? (Números comoDecimal('1e99999999')
são especialmente problemáticos, por exemplo: você não quer ter que expandi-los para o número inteiro correspondente antes do hash.)int
,float
,Decimal
eFraction
objetos e quex == y
implicahash(x) == hash(y)
, mesmo quandox
ey
ter tipos diferentes impõe algumas restrições ao invés graves. Se fosse apenas uma questão de escrever uma função hash para inteiros, sem se preocupar com os outros tipos, seria uma questão totalmente diferente.A função hash retorna um int simples, o que significa que o valor retornado é maior
-sys.maxint
e menor quesys.maxint
, o que significa que se você passarsys.maxint + x
para ele o resultado será-sys.maxint + (x - 2)
.Enquanto isso,
2**200
é umn
vezes maior do quesys.maxint
- meu palpite é que o hash ultrapassaria o intervalo-sys.maxint..+sys.maxint
n vezes até que parasse em um inteiro simples nesse intervalo, como nos trechos de código acima.Então, geralmente, para qualquer n <= sys.maxint :
Observação: isso é verdade para o python 2.
fonte
sys.maxint
, e que usa uma função hash diferente).A implementação para o tipo int em cpython pode ser encontrada aqui.
Ele apenas retorna o valor, exceto por
-1
, que retorna-2
:fonte
PyLong
vez dePyInt
.