Yaklaşık eşitlikte şamandıra karma uygulaması


15

Diyelim şu Python sınıf var demek (sorun sadece aynı Java bulunmaktadır equalsve hashCode)

class Temperature:
    def __init__(self, degrees):
        self.degrees = degrees

degreesbir şamandıra olarak Kelvin sıcaklık nerede . Şimdi, eşitlik testi ve karma Temperatureyöntemini şu şekilde uygulamak istiyorum:

  • Direkt eşitlik testi yerine şamandıranın epsilon farkını karşılaştırır,
  • ve a == bima eden sözleşmeyi onurlandırır hash(a) == hash(b).
def __eq__(self, other):
    return abs(self.degrees - other.degrees) < EPSILON

def __hash__(self):
    return # What goes here?

Python belgeleri bunu sağlamak için rakamları biraz karıştırmaktan bahsediyor , hash(2) == hash(2.0)ancak bu aynı sorun değil.

Doğru yolda mıyım? Ve eğer öyleyse, bu durumda karma uygulamasının standart yolu nedir?

Güncelleme : Şimdi, şamandıralar için bu tip eşitlik testinin ==ve equals. Peki bu, yüzenlerin doğrudan karşılaştırılmaması gereken "ortak bilgi" ile nasıl birlikte gider? Şamandıraları karşılaştırarak bir eşitlik operatörü uygularsanız, statik analiz araçları şikayet edecektir. Bunu yapmak doğru mu?


9
sorunun neden Java'nın etiketi var?
Laiv

8
Güncellemeniz hakkında: Karma şamandıraların genellikle şüpheli bir şey olduğunu söyleyebilirim. Şamandıraları anahtar veya ayar öğesi olarak kullanmaktan kaçının.
J. Fabian Meier

6
@Neil: Aynı zamanda yuvarlama sesi tamsayı gibi değil mi? Demek istediğim: diyelim ki, bininci dereceye yuvarlanabilirseniz, o zaman sadece sabit nokta gösterimini kullanabilirsiniz - sıcaklığı bininci derecelerde ifade eden bir tam sayı. Kullanım kolaylığı için, isterseniz şeffaf bir şekilde yüzer olan / yüzen bir alıcı / ayarlayıcı olabilir ...
Matthieu M.

4
Kelvinler artık derece değildir. Dereceler de belirsizdir. Neden sadece aramıyorsun kelvin?
Solomon Ucko

5
Python'un az çok mükemmel sabit nokta desteği var , belki bu sizin için bir şey.
Jonas Schäfer

Yanıtlar:


41

Sıcaklık için eşitlik testi ve karma işlemini doğrudan eşitlik testi yerine bir epsilon farkıyla karşılaştıracak şekilde uygulamak,

Bulanık eşitlik, Java'nın equalsyönteme yerleştirdiği gereksinimleri , yani geçişliliği , yani eğer x == yve y == zsonra x == z. Ama daha sonra, örneğin bir bulanık eşitlik, 0.1'lik bir epsilonu, yaparsanız 0.1 == 0.2ve 0.2 == 0.3fakat 0.1 == 0.3tutmaz.

Python böyle bir gereksinimi belgelemese de, geçişsiz bir eşitliğe sahip olmanın sonuçları onu çok kötü bir fikir haline getirmektedir; bu türler hakkında akıl yürütme baş ağrısını tetikler.

Bu yüzden bunu yapmamanızı şiddetle tavsiye ederim.

Ya tam eşitlik sağlayın ve hash'ınızı bariz şekilde buna dayandırın ve bulanık eşlemeyi yapmak için ayrı bir yöntem sağlayın veya Kain tarafından önerilen eşdeğerlik sınıfı yaklaşımıyla devam edin. İkinci durumda, değerinizi yapıcıdaki eşdeğerlik sınıfının temsili bir üyesine sabitlemenizi ve geri kalanı için basit kesin eşitlik ve karma ile gitmenizi öneririm; bu şekilde türleri düşünmek çok daha kolay.

(Ancak bunu yaparsanız, kayan nokta yerine sabit bir nokta gösterimi de kullanabilirsiniz, yani bir derecenin binde birini veya istediğiniz hassasiyeti saymak için bir tam sayı kullanırsınız.)


2
ilginç düşünceler. Milyonlarca epsilon biriktirerek ve geçişle her şeyin başka herhangi bir şeye eşit olduğu sonucuna varabilirsiniz :-) Ancak bu matematiksel kısıtlama, çoğu durumda temsil etmeyi amaçladıkları sayının yaklaşıkları olan kayan noktaların ayrı temelini kabul ediyor mu?
Christophe

@Christophe İlginç bir soru. Bunu düşünürseniz, bu yaklaşımın çözünürlüğü epsilon'dan daha büyük olan (elbette 0, ortalanmış) şamandıralardan tek bir büyük denklik sınıfı oluşturacağını ve diğer şamandıraları kendi sınıflarında bırakacağını göreceksiniz. Ama asıl mesele bu değil, asıl sorun, 2 sayının eşit olduğu sonucuna varılmasının, karşılaştırılan üçüncü bir sayıya ve yapılma sırasına bağlı olmasıdır.
Sıradan

@ OP düzenlemesine hitap ederek, kayan noktanın yanlışlığının onları içeren türlere =="bulaşması" gerektiğini ekliyorum ==. Yani, tam bir eşitlik sağlama tavsiyenize uyuyorlarsa, statik analiz aracı eşitlik kullanıldığında uyarmak için ayrıca yapılandırılmalıdır Temperature. Gerçekten yapabileceğin tek şey bu.
HTNW

@HTNW: Bu çok basit olurdu. Oran sınıfının float approximationkatılmayan bir alanı olabilir ==. Ayrıca, statik analiz aracı, ==karşılaştırılan üyelerden biri bir floattür olduğunda sınıfların uygulanması içinde zaten bir uyarı verecektir .
MSalters

@MSalters? Muhtemelen, yeterince yapılandırılabilir statik analiz araçları önerdiğimi iyi yapabilir. Bir sınıfın floatkatılmayan bir alanı varsa ==, aracınızı ==bu sınıfta uyarılmak üzere yapılandırmayın . Sınıf yaparsa, sınıfın =="çok kesin" olarak işaretlenmesi , aracın uygulama içindeki bu tür hataları yok saymasına neden olur. Örneğin Java, eğer @Deprecated void foo(), o zaman void bar() { foo(); }bir uyarıdır, ama @Deprecated void bar() { foo(); }değil. Belki birçok araç bunu desteklemez, ancak bazıları bunu destekleyebilir.
HTNW

16

İyi şanslar

Karmalara aptal olmadan veya epsilondan ödün vermeden bunu başaramazsınız.

Misal:

Her noktanın kendi benzersiz karma değerine sahip olduğunu varsayın.

Kayan nokta sayıları sıralı olduğundan, belirli bir kayan nokta değerinden önce en fazla k sayısı ve belirli bir kayan nokta değerinden sonra en fazla k sayıları verilir.

  1. Aynı kare değerini paylaşmayan birbirlerinin epsilonlarındaki her iki nokta için.

    • Karma düzenini bu iki nokta karma değerini aynı değere ayarlayın.
  2. Tüm bu çiftler için tüm kayan nokta sayıları dizisi tek bir değer değerine doğru çökecektir.

Bunun doğru olmayacağı birkaç durum vardır:

  • Olumlu / Olumsuz Sonsuzluk
  • NaN
  • Belirli bir epsilon için ana aralığa bağlanamayan birkaç Normalleştirilmemiş aralık.
  • biçime özgü birkaç başka örnek

Bununla birlikte, kayan nokta aralığının> =% 99'u, belirli bir kayan nokta değerinin üstünde veya altında en az bir kayan nokta değeri içeren herhangi bir epsilon değeri için tek bir değere kadar karma olacaktır.

Sonuç

Ya>% 99 tüm kayan nokta aralığı, bir karma değerinin amacını ciddi şekilde zedeleyen tek bir değere (ve oldukça dağıtılmış bir düşük çarpışma karmaına dayanan herhangi bir cihaz / konteyner) hash eder.

Veya epsilon sadece kesin eşleşmelere izin verecek şekildedir.

granül

Elbette bunun yerine ayrıntılı bir yaklaşıma gidebilirsiniz.

Bu yaklaşım altında, belirli bir çözünürlüğe kadar kesin bölümler tanımlarsınız. yani:

[0.001, 0.002)
[0.002, 0.003)
[0.003, 0.004)
...
[122.999, 123.000)
...

Her bir kova benzersiz bir karmaya sahiptir ve kova içindeki herhangi bir kayan nokta, aynı kovadaki diğer herhangi bir şamandıra ile karşılaştırılır.

Ne yazık ki, iki şamandıranın epsilon mesafesinden uzak olması ve iki ayrı karması olması mümkündür.


2
OP'nin gereksinimlerine uyuyorsa, buradaki ayrıntılı yaklaşımın muhtemelen en iyi olacağını kabul ediyorum. Korkarım OP'nin +/-% 0,1 tipi gereksinimleri var, yani ayrıntılı olamaz.
Neil

4
@DocBrown "Mümkün değil" kısmı doğru. Epsilon tabanlı eşitlik, karma kodlarının eşit olduğunu ima etmesi gerekiyorsa, otomatik olarak tüm karma kodlarına eşit olursunuz, bu nedenle karma işlevi artık kullanışlı değildir. Kova yaklaşımı verimli olabilir, ancak rastgele birbirine yakın olan farklı karma kodlara sahip sayılarınız olacaktır.
J. Fabian Meier

2
Kova yaklaşımı, yalnızca tam karma anahtarına sahip kova değil, aynı zamanda içeriği için iki komşu kova (veya bunlardan en az biri) de kontrol edilerek değiştirilebilir. Bu, bu uç vakaların problemini, çalışma süresini en fazla iki kat artırma maliyeti (doğru uygulandığında) maliyetidir. Ancak, genel çalışma süresi sırasını değiştirmez.
Doc Brown

Ruh içinde haklı olduğunuzda, her şey çökmeyecek. Sabit bir küçük epsilon ile, çoğu sayı sadece kendilerine eşit olacaktır. Tabii ki, onlar için epsilon işe yaramaz olacak, bu yüzden yine, ruh olarak haklısınız.
Carsten S

1
@CarstenS Evet, tek bir karmaya ait aralık karmasının% 99'unun aslında tüm kayan aralığı kapsamadığını belirttim. Kendi benzersiz kovalarına hash yapacak olan epsilon'dan daha fazla ayrılmış birçok yüksek aralık değeri vardır.
Kain0_0

7

Sıcaklığınızı kaputun altında bir tamsayı olarak modelleyebilirsiniz. Sıcaklığın doğal bir alt sınırı vardır (-273,15 Santigrat). Yani, çift (-273.15 temel tamsayı için 0'a eşittir). İhtiyacınız olan ikinci öğe, eşlemenizin ayrıntı düzeyidir. Bu ayrıntı düzeyini dolaylı olarak kullanıyorsunuz; bu senin EPSILON.

Sadece sıcaklığınızı EPSILON'a bölün ve zemini alın, şimdi karma ve eşitiniz senkronize davranacaktır. Python 3'te tamsayı sınırsızdır, isterseniz EPSILON daha küçük olabilir.

DİKKAT EPSILON değerini değiştirirseniz ve nesneyi serileştirirseniz, bunlar uyumlu olmayacaktır!

#Pseudo code
class Temperature:
    def __init__(self, degrees):
        #CHECK INVALID VALUES HERE
        #TRANSFORM TO KELVIN HERE
        self.degrees = Math.floor(kelvin/EPSILON)

1

Belirli bir anahtara "yaklaşık olarak eşit" olan şeyleri bulabilen bir kayan nokta karma tablosu uygulamak için birkaç yaklaşım veya bunların bir kombinasyonunun kullanılması gerekir:

  1. Her değeri, karma tablosunda saklamadan önce "bulanık" aralıktan biraz daha büyük bir artışa yuvarlayın ve bir değer bulmaya çalışırken, karma tablonun istenen değerin üstünde ve altında yuvarlanmış değerler olup olmadığını kontrol edin.

  2. İstenen değerin üstünde ve altında olan tuşları kullanarak her öğeyi karma tablosunda saklayın.

Her iki yaklaşımın da kullanılması, büyük olasılıkla her bir anahtarla ilişkili birden fazla öğe olacağından, karma tablo girişlerinin öğeleri değil, listeleri tanımlamasını gerektireceğini unutmayın. Yukarıdaki ilk yaklaşım, gerekli karma tablo boyutunu en aza indirecektir, ancak tabloda olmayan bir öğe için her arama iki karma tablo araması gerektirecektir. İkinci yaklaşım, öğelerin tabloda bulunmadığını hızlı bir şekilde belirleyebilecektir, ancak genellikle tablonun aksi takdirde gerekli olandan iki kat daha fazla girdi içermesini gerektirecektir. 2B uzayda nesneler bulmaya çalışılıyorsa, X yönü için bir yaklaşım ve Y yönü için bir yaklaşım kullanmak yararlı olabilir, böylece her öğenin bir kez depolanması yerine her arama için dört sorgu işlemi gerektirmesi veya bir öğeyi bulmak için bir arama kullanabilir, ancak her öğeyi dört kez saklamak zorunda kalır,


0

Tabii ki mantisin son sekiz bitini silerek ve sonra karşılaştırarak ya da hash ederek “neredeyse eşit” diyebilirsiniz. Sorun şu ki birbirine çok yakın sayılar farklı olabilir .

Burada bir karışıklık var: eğer iki kayan nokta sayısı eşitse, eşittir. Eşit olup olmadığını kontrol etmek için “==“ kullanırsınız. Bazen eşitliği kontrol etmek istemezsiniz, ancak bunu yaptığınızda “==“ gidilecek yoldur.


0

Bu bir yanıt değil, yardımcı olabilecek genişletilmiş bir yorumdur.

MPFR (GNU MP tabanlı) kullanırken benzer bir sorun üzerinde çalışıyorum . @ Kain0_0 tarafından ana hatları çizilen "kova" yaklaşımı kabul edilebilir sonuçlar veriyor gibi görünüyor, ancak bu yanıtta vurgulanan sınırlamaların farkında olun.

Mathematica gibi bir "tam" ( uyarı emptor ) bilgisayar cebir sistemi kullanarak - ne yapmaya çalıştığınıza bağlı olarak - yanlış bir sayısal programı tamamlamaya veya doğrulamaya yardımcı olabilir eklemek istedim . Bu, yuvarlama konusunda endişelenmeden sonuçları hesaplamanıza izin verecektir, örneğin, yerine veya benzeri 7*√2 - 5*√2verim verecektir . Tabii ki, bu değebilecek veya olmayabilecek ek komplikasyonlar getirecektir.22.00000001

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.