Saya telah bermain dengan fungsi hash Python . Untuk bilangan bulat kecil, itu hash(n) == n
selalu muncul . Namun ini tidak berlaku untuk jumlah besar:
>>> hash(2**100) == 2**100
False
Saya tidak terkejut, saya mengerti hash membutuhkan rentang nilai yang terbatas. Berapa kisaran itu?
Saya mencoba menggunakan pencarian biner untuk menemukan angka terkecilhash(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
Apa yang spesial dari 2305843009213693951? Saya perhatikan itu kurang darisys.maxsize == 9223372036854775807
Sunting: Saya menggunakan Python 3. Saya menjalankan pencarian biner yang sama pada Python 2 dan mendapatkan hasil yang berbeda 2147483648, yang saya perhatikan adalah sys.maxint+1
Saya juga bermain-main dengan [hash(random.random()) for i in range(10**6)]
memperkirakan kisaran fungsi hash. Maksimal secara konsisten di bawah n di atas. Membandingkan min, tampaknya hash Python 3 selalu bernilai positif, sedangkan hash Python 2 dapat mengambil nilai negatif.
sumber
n+1 == 2**61-1
n
untuk seluruh kisaran int 64bit.2147483647
sama dengansys.maxint
(tidaksys.maxint+1
), dan jika 'n = 0b1111111111111111111111111111111111111111111111111111111111111' maka bukann+1 == 2**61
ataun == 2**61-1
(tidakn+1 == 2**61-1
)?Jawaban:
Berdasarkan dokumentasi python di
pyhash.c
file:Jadi untuk mesin 64/32 bit, pengurangannya adalah 2 _PyHASH_BITS - 1, tapi apa itu
_PyHASH_BITS
?Anda dapat menemukannya di
pyhash.h
file header yang untuk mesin 64 bit telah didefinisikan sebagai 61 (Anda dapat membaca penjelasan lebih lanjut dipyconfig.h
file).Jadi pertama-tama ini berdasarkan platform Anda misalnya di platform Linux 64bit saya pengurangannya adalah 2 61 -1, yaitu
2305843009213693951
:Anda juga dapat menggunakan
math.frexp
untuk mendapatkan mantissa dan eksponensys.maxint
yang untuk mesin 64 bit menunjukkan bahwa max int adalah 2 63 :Dan Anda dapat melihat perbedaannya dengan tes sederhana:
Baca dokumentasi lengkap tentang algoritma hashing python https://github.com/python/cpython/blob/master/Python/pyhash.c#L34
Seperti disebutkan dalam komentar, Anda dapat menggunakan
sys.hash_info
(dalam python 3.X) yang akan memberi Anda urutan struct parameter yang digunakan untuk menghitung hash.Di samping modulus yang saya jelaskan di baris sebelumnya, Anda juga bisa mendapatkan
inf
nilai sebagai berikut:sumber
sys.hash_info
, untuk kelengkapan.2305843009213693951
adalah2^61 - 1
. Ini adalah bilangan prima Mersenne terbesar yang cocok dengan 64 bit.Jika Anda harus membuat hash hanya dengan mengambil mod nilai beberapa, maka bilangan prima Mersenne besar adalah pilihan yang baik - mudah untuk menghitung dan memastikan distribusi kemungkinan yang merata. (Meskipun saya pribadi tidak akan pernah membuat hash seperti ini)
Sangat mudah untuk menghitung modulus untuk bilangan floating point. Mereka memiliki komponen eksponensial yang mengalikan bilangan bulat dengan
2^x
. Karena2^61 = 1 mod 2^61-1
, Anda hanya perlu mempertimbangkan file(exponent) mod 61
.Lihat: https://en.wikipedia.org/wiki/Mersenne_prime
sumber
x == y
jaminanhash(x) == hash(y)
di seluruh jenis? (Angka-angka sepertiDecimal('1e99999999')
sangat bermasalah, misalnya: Anda tidak ingin harus mengembangkannya ke bilangan bulat yang sesuai sebelum melakukan hashing.)int
,float
,Decimal
danFraction
benda-benda danx == y
menyiratkanhash(x) == hash(y)
bahkan ketikax
dany
memiliki berbagai jenis membebankan beberapa kendala yang agak parah. Jika ini hanya masalah menulis fungsi hash untuk integer, tanpa mengkhawatirkan jenis lainnya, itu akan menjadi masalah yang sama sekali berbeda.Fungsi hash mengembalikan int polos yang berarti nilai yang dikembalikan lebih besar dari
-sys.maxint
dan lebih rendah darisys.maxint
, yang berarti jika Anda meneruskannyasys.maxint + x
hasilnya akan menjadi-sys.maxint + (x - 2)
.Sementara
2**200
itun
kali lebih besar darisys.maxint
- tebakan saya adalah bahwa hash akan melewati rentang-sys.maxint..+sys.maxint
n kali sampai berhenti pada integer biasa dalam kisaran itu, seperti dalam cuplikan kode di atas ..Jadi secara umum, untuk n <= sys.maxint apa pun :
Catatan: ini benar untuk python 2.
sumber
sys.maxint
, dan yang menggunakan fungsi hash yang berbeda).The implementasi untuk tipe int di cpython dapat ditemukan di sini.
Itu hanya mengembalikan nilai, kecuali
-1
, daripada yang dikembalikan-2
:sumber
PyLong
daripadaPyInt
.