Mengapa hash infinity Python memiliki angka π?


241

Hash tak terhingga dalam Python memiliki digit pi yang cocok :

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159

Apakah itu hanya kebetulan atau disengaja?


9
Tidak yakin, tapi saya duga akan bahwa itu sebagai disengaja sebagai hash(float('nan'))menjadi 0.
cs95

1
Hmm, tidak disebutkan tentang itu di sys.hash_info. Telur Paskah?
wim

123
Tanya Tim Peters. Inilah komit tempat ia memperkenalkan konstanta ini, 19 tahun lalu: github.com/python/cpython/commit/… . Saya menyimpan nilai-nilai khusus ketika saya mengerjakan ulang hash numerik di bugs.python.org/issue8188
Mark Dickinson

8
@MarkDickinson Terima kasih. Sepertinya Tim mungkin juga menggunakan digit e untuk hash -inf pada awalnya.
wim

17
@ Ah, ya, benar. Dan ternyata saya mengubahnya menjadi -314159. Saya sudah lupa tentang itu.
Mark Dickinson

Jawaban:


47

_PyHASH_INFadalah didefinisikan sebagai konstan sama untuk 314159.

Saya tidak dapat menemukan diskusi tentang ini, atau komentar yang memberi alasan. Saya pikir itu dipilih kurang lebih sewenang-wenang. Saya membayangkan bahwa selama mereka tidak menggunakan nilai bermakna yang sama untuk hash lainnya, itu tidak masalah.


6
Nitpick kecil: hampir tidak bisa dihindari dengan definisi bahwa nilai yang sama akan digunakan untuk hash lainnya, misalnya dalam kasus hash(314159)ini juga 314159. Coba juga, dengan Python 3, hash(2305843009214008110) == 314159(input ini 314159 + sys.hash_info.modulus) dll.
ShreevatsaR

3
@ShreevatsaR Saya hanya bermaksud bahwa selama mereka tidak memilih nilai ini sebagai hash dari nilai lain menurut definisi, maka memilih nilai yang bermakna seperti ini tidak meningkatkan peluang tabrakan hash
Patrick Haugh

220

Ringkasan: Ini bukan kebetulan; _PyHASH_INFhardcoded sebagai 314159 dalam implementasi CPython default Python, dan dipilih sebagai nilai arbitrer (jelas dari angka π) oleh Tim Peters pada tahun 2000 .


Nilai hash(float('inf'))adalah salah satu parameter yang bergantung pada sistem dari fungsi hash bawaan untuk tipe numerik, dan juga tersedia seperti sys.hash_info.infpada Python 3:

>>> import sys
>>> 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)
>>> sys.hash_info.inf
314159

(Hasil yang sama dengan PyPy juga.)


Dalam hal kode, hashadalah fungsi bawaan. Menyebutnya pada objek mengambang Python memanggil fungsi yang pointer diberikan oleh tp_hashatribut tipe built-in mengambang ( PyTypeObject PyFloat_Type), yang merupakan satu float_hashfungsi, yang didefinisikan sebagai return _Py_HashDouble(v->ob_fval), yang pada gilirannya memiliki

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;

mana _PyHASH_INFyang didefinisikan sebagai 314.159:

#define _PyHASH_INF 314159

Dalam hal sejarah, penyebutan pertama 314159dalam konteks ini dalam kode Python (Anda dapat menemukan ini dengan git bisectatau git log -S 314159 -p) ditambahkan oleh Tim Peters pada Agustus 2000, dalam apa yang sekarang dilakukan 39dce293 dalam cpythonrepositori git.

Pesan komit mengatakan:

Perbaiki untuk http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470 . Ini adalah bug yang menyesatkan - "bug" yang sebenarnya adalah yang hash(x)memberikan return error ketika xinfinity. Memperbaiki itu. Menambahkan Py_IS_INFINITYmakro baru ke pyport.h. Menata ulang kode untuk mengurangi duplikasi yang berkembang dalam hashing float dan angka kompleks, mendorong penusukan Trent sebelumnya pada kesimpulan logis. Memperbaiki bug yang sangat langka di mana hashing floats dapat mengembalikan -1 bahkan jika tidak ada kesalahan (tidak membuang waktu mencoba membangun kasus uji, itu hanya jelas dari kode yang dapat terjadi). Peningkatan hash kompleks sehingga hash(complex(x, y))tidak hash(complex(y, x))lagi sama secara sistematis .

Secara khusus, dalam komit ini dia merobek kode static long float_hash(PyFloatObject *v)in Objects/floatobject.cdan membuatnya adil return _Py_HashDouble(v->ob_fval);, dan dalam definisi long _Py_HashDouble(double v)in Objects/object.cdia menambahkan baris:

        if (Py_IS_INFINITY(intpart))
            /* can't convert to long int -- arbitrary */
            v = v < 0 ? -271828.0 : 314159.0;

Jadi seperti yang disebutkan, itu adalah pilihan yang sewenang-wenang. Perhatikan bahwa 271828 terbentuk dari beberapa digit pertama desimal dari e .

Berkomitmen selanjutnya terkait:


44
Pilihan -271828 untuk -Inf menghilangkan keraguan bahwa asosiasi pi adalah kebetulan.
Russell Borogove

24
@RussellBorogove Tidak, tapi itu membuatnya sekitar satu juta kali lebih kecil kemungkinannya;)
pipa

8
@ cmaster: Lihat bagian di atas di mana dikatakan Mei 2010, yaitu bagian dokumentasi tentang hashing tipe numerik dan edisi 8188 - idenya adalah kita ingin hash(42.0)sama hash(42), juga sama dengan hash(Decimal(42))dan hash(complex(42))dan hash(Fraction(42, 1)). Solusinya (oleh Mark Dickinson) adalah IMO yang elegan: mendefinisikan fungsi matematika yang bekerja untuk bilangan rasional apa pun, dan menggunakan fakta bahwa bilangan floating-point adalah bilangan rasional juga.
ShreevatsaR

1
@ShreevatsaR Ah, terima kasih. Meskipun saya tidak akan peduli untuk menjamin persamaan ini, ada baiknya untuk mengetahui bahwa ada penjelasan yang baik, solid, dan logis untuk kode yang kelihatannya kompleks :-)
cmaster - monstore pemulihan kembali

2
@ cmaster Fungsi hash untuk bilangan bulat adalah hash(n) = n % Mtempat M = (2 ^ 61 - 1). Ini digeneralisasikan untuk rasional hash(p/q) = (p/q) mod Mdengan pembagian yang ditafsirkan modulo M (dengan kata lain:) hash(p/q) = (p * inverse(q, M)) % M. Alasan kita menginginkan ini: jika dalam dict dkita meletakkan d[x] = foodan kemudian kita miliki x==y(mis. 42.0 == 42) tetapi d[y]tidak sama dengan d[x], maka kita akan memiliki masalah. Sebagian besar kode yang tampaknya kompleks berasal dari sifat format floating-point itu sendiri, untuk memulihkan fraksi dengan benar dan memerlukan kasus khusus untuk nilai inf dan NaN.
ShreevatsaR

12

Memang,

sys.hash_info.inf

kembali 314159. Nilai tidak dihasilkan, itu dibangun ke dalam kode sumber. Faktanya,

hash(float('-inf'))

mengembalikan -271828, atau kira-kira -e, dalam python 2 ( sekarang -314159 ).

Fakta bahwa dua bilangan irasional paling terkenal sepanjang masa digunakan sebagai nilai hash membuatnya sangat tidak mungkin menjadi kebetulan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.