Python'un sonsuz karması neden π rakamına sahip?


241

Python'daki sonsuzluğun karması pi ile eşleşen rakamlara sahiptir :

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

Bu sadece bir tesadüf mü yoksa kasıtlı mı?


9
Emin değilim, ama tahminim bunun kadar kasıtlı hash(float('nan'))olması 0.
cs95

1
Hmm, bundan bahsedilmiyor sys.hash_info. Paskalya yumurtası?
wim

123
Tim Peters'e sor. İşte bu sabiti 19 yıl önce tanıttığı taahhüt: github.com/python/cpython/commit/… . Bugs.python.org/issue8188
Mark Dickinson

8
@MarkDickinson Teşekkürler. Tim aynı zamanda rakamlarını kullanmış olabilir gibi görünüyor e aslen -inf ait karma için.
wim

17
@ wim Ah evet, doğru. Ve görünüşe göre bunu ben olarak değiştirdim -314159. Bunu unutmuştum.
Mark Dickinson

Yanıtlar:


47

_PyHASH_INFolan bir sabit olarak tanımlanır e eşit 314159.

Bununla ilgili herhangi bir tartışma bulamıyorum veya bir neden bildiren yorumlar bulamıyorum. Sanırım az çok keyfi olarak seçildi. Diğer karmalar için aynı anlamlı değeri kullanmadığı sürece bunun önemli olmadığını hayal ediyorum.


6
Küçük nitpick: tanımı gereği aynı değerin diğer karmalar için kullanılması neredeyse kaçınılmazdır, örneğin bu durumda hash(314159)da 314159. Ayrıca, Python 3'te hash(2305843009214008110) == 314159(bu giriş 314159 + sys.hash_info.modulus) vb.
Deneyin

3
@ShreevatsaR Ben sadece bu değeri tanım gereği diğer değerlerin karması olarak seçmedikleri sürece, bunun gibi anlamlı bir değer seçmenin karma çarpışma şansını artırmayacağını
kastediyorum

220

Özet: Bu bir tesadüf değil; Python'un varsayılan CPython uygulamasında _PyHASH_INF314159 olarak sabit kodlanmıştır ve 2000'de Tim Peters tarafından rasgele bir değer (açıkça π rakamlarından) seçilmiştir .


Değeri, hash(float('inf'))sayısal türler için yerleşik karma işlevinin sisteme bağlı parametrelerinden biridir ve Python 3'teki gibi de kullanılabilirsys.hash_info.inf :

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

( PyPy ile de aynı sonuçlar .)


Kod açısından, hashyerleşik bir işlevdir. Olan işaretçi tarafından verilen fonksiyon bir Python şamandıra nesne üzerinde çağırmaktadır çağrı tp_hashöznitelik dahili salınım türünde (arasında PyTypeObject PyFloat_Type), birfloat_hash işlev, tanımlandığı şekilde return _Py_HashDouble(v->ob_fval)da, var

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

burada _PyHASH_INFolduğu gibi tanımlanmıştır 314159:

#define _PyHASH_INF 314159

Tarih açısından, 314159bu bağlamda ilk kez Python kodunda (bunu git bisectveya ile bulabilirsiniz git log -S 314159 -p) Ağustos 2000'de Tim Peters tarafından , git deposunda 39dce293 taahhüt edilene eklenmiştircpython .

Taahhüt mesajı şunu söylüyor:

Http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470 için düzeltme . Bu yanıltıcı bir hataydı - gerçek "hata" bir sonsuzluk hash(x)olduğunda bir hata dönüş verdi oldu x. Bunu düzelttim. Alanına yeni Py_IS_INFINITYmakro eklendi pyport.h. Float ve karmaşık sayıların karma işlemlerinde artan çoğalmayı azaltmak için kodu yeniden düzenleyerek Trent'in önceki bıçağını mantıklı bir sonuca itti. Bir hata olmasa bile şamandıraların karmaının -1'e dönebileceği son derece nadir bir hata düzeltildi (bir test vakası oluşturmaya çalışırken zaman kaybetmedim, olabileceği koddan açıkça anlaşıldı ). Geliştirilmiş karmaşık karma artık hash(complex(x, y))sistematik olarak eşit hash(complex(y, x))değil.

Özellikle, bu o kodunu yırtık işlemek static long float_hash(PyFloatObject *v)içinde Objects/floatobject.cve sadece yapılan return _Py_HashDouble(v->ob_fval);ve tanımında long _Py_HashDouble(double v)içinde Objects/object.co satırları ekledi:

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

Daha önce de belirtildiği gibi, keyfi bir seçimdi. 271.828 ilk birkaç ondalık hane oluştuğunu ileri Not e .

İlgili daha sonraki taahhütler:


44
-Inf için -271828 seçimi, pi ilişkisinin kazara olduğu şüphesini ortadan kaldırır.
Russell Borogove

24
@RussellBorogove Hayır ama yaklaşık bir milyon kat daha az olası hale getiriyor;)
boru

8
@cmaster: üzerinde yani dokümantasyon bölüm, May 2010 yazan yere yukarıdaki bölümünü bakınız, sayısal türde karma ve konuya 8188 - Fikir istediğimiz olmasıdır hash(42.0)ile aynı olması hash(42)da, aynı hash(Decimal(42))ve hash(complex(42))ve hash(Fraction(42, 1)). Çözüm (Mark Dickinson tarafından) zarif bir IMO'dur: herhangi bir rasyonel sayı için çalışan bir matematiksel fonksiyon tanımlamak ve kayan nokta sayılarının rasyonel sayılar olduğu gerçeğini kullanmak.
ShreevatsaR

1
@ShreevatsaR Ah, teşekkür ederim. Bu eşitlikleri garanti
etmemekle ilgilenmese de

2
@cmaster Tamsayılar için sağlama işlevi basitçe hash(n) = n % MM = (2 ^ 61-1 ) 'dir. Bu, rasyonel n için hash(p/q) = (p/q) mod M, bölünme modulo M (başka bir deyişle:) olarak yorumlanır hash(p/q) = (p * inverse(q, M)) % M. Bunu istememizin nedeni: dkoyduğumuz bir dikte içine koyduk d[x] = foove sonra x==y(örneğin 42.0 == 42) var, ancak d[y]aynı değil d[x], o zaman bir sorunumuz olurdu. Görünüşte karmaşık olan kodun çoğu, kesiri düzgün bir şekilde kurtarmak ve inf ve NaN değerleri için özel durumlar gerektiren kayan nokta formatının kendisinden doğar.
ShreevatsaR

12

Aslında,

sys.hash_info.inf

döner 314159. Değer üretilmez, kaynak kodun içine yerleştirilir. Aslında,

hash(float('-inf'))

-271828veya python 2'de yaklaşık -e döndürür ( şimdi -314159'dur ).

Tüm zamanların en ünlü mantıksız sayılarının karma değerleri olarak kullanılması, tesadüf olmasını pek mümkün kılmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.