2015-06-28 20 views
9

Standartlara göre std::hash sınıfında kapsayıcılar için destek verilmez (sıralı olmayanlar olsun). Bunu nasıl yapacağımı merak ediyorum. Ne var olduğunu:std :: unordered_map için karma değer

std::unordered_map<std::wstring, std::wstring> _properties; 
std::wstring _class; 

I (std::hash<std::wstring> aracılığıyla) anahtarları ve değerleri için bireysel karmaları bilgisayar, girdileri yineleme düşündü ve bir şekilde sonuçları bitiştirmek.

Bunu yapmanın iyi bir yolu ne olurdu ve haritadaki düzen tanımlanmamışsa sorun olur mu?

Not: Destek kullanmak istemiyorum.

size_t MyClass::GetHashCode() 
{ 
    std::hash<std::wstring> stringHash; 
    size_t mapHash = 0; 
    for (auto property : _properties) 
    mapHash ^= stringHash(property.first)^stringHash(property.second); 

    return ((_class.empty() ? 0 : stringHash(_class)) * 397)^mapHash; 
} 

: Böyle olacağını böylece

Basit XOR

, önerildi?

Bu basit XOR'un yeterli olup olmadığından emin değilim.

+0

's// XOR' bitiştirmek ve gitmek iyi olmalıdır. Daha sonra sadece bir karma işlevinin yapabilmesi gereken şeyler, iki semantik olarak eşdeğer değerler için aynı karmayı üretip, çıktılarını tüm olası karma değerleri kümesine eşit olarak eşit bir şekilde dağıtmaktır. –

+0

@dyp OP kapsayıcıyı kendine has etmek istiyor. –

+0

Temelde soru nasıl bir değerler (sırasız) aralığı için bir karma elde etmektir ve aslında 'std :: unordered_map' özgü değildir? – inf

cevap

7

Tepki

yeterli tarafından olursa, istediğiniz çıkışı, kardinalite 2^64 sahiptir muhakeme tüm karma seti fonksiyonunu değerleri yani, cevap sayılı senin fonksiyon birebirdir olsun veya olmasın demek iken Girişlerinizin alanı çok büyük büyüktür. Bununla birlikte, bu gerçekten önemli değildir, çünkü girdilerinizin niteliği göz önünde bulundurulduğunda bir karma karma işlevine sahip olamazsınız. İyi bir karma işlevi şu niteliklere sahiptir:

  • Kolayca tersine çevrilemez. Çıktı k verildiği zaman, evrenin yaşam süresi içinde, h (m) = k olacak şekilde bulamaması mümkün değildir.
  • Aralık, çıktı alanı üzerinden eşit olarak dağıtılır.
  • İki giriş m ve m bulmak zor '(Tabii

, bunların kapsamlarını gerçekten kriptografik olan bir şeyi isteyip bağlıdır, ya da h (m) = s d) böyle' Bazı keyfi veri parçalarını almak ve sadece biraz 64 bitlik tamsayı göndermek istiyorsunuz. Kriptografik olarak güvenli bir şey istiyorsanız, kendiniz yazmak iyi bir fikir değildir. Bu durumda, işlevin girişteki küçük değişikliklere duyarlı olduğunu garanti etmelisiniz. std::hash işlev nesnesinin kriptografik olarak güvenli olması gerekmez. Karma tablolar için isomorphic kullanım durumları için var. CPP Rerefence diyor ki:

iki farklı parametreler eşit değildir k1 ve k2 için

, olasılık std::hash<Key>()(k1) == std::hash<Key>()(k2) çok küçük olması gerektiğini, 1.0/std::numeric_limits<size_t>::max() yaklaşan.

Şu anki çözümünüzün bunu nasıl garanti etmediğini aşağıda göstereceğim. Ben senin çözümün bir varyantı üzerine sen benim gözlemlerin birkaç vereceğiz

Çarpışmalar

(Ben senin _class üyesidir bilmiyorum).

std::size_t hash_code(const std::unordered_map<std::string, std::string>& m) { 
    std::hash<std::string> h; 
    std::size_t result = 0; 
    for (auto&& p : m) { 
     result ^= h(p.first)^h(p.second); 
    } 
    return result; 
} 

Çarpışmalar oluşturmak kolaydır.Aşağıdaki haritalar düşünün:

std::unordered_map<std::string, std::string> container0; 
std::unordered_map<std::string, std::string> container1; 
container0["123"] = "456"; 
container1["456"] = "123"; 
std::cout << hash_code(container0) << '\n'; 
std::cout << hash_code(container1) << '\n'; 

benim makinede 4.9.1 ++ g ile derleme, bu çıkışlar:

1225586629984767119 
1225586629984767119 

soru bu konularda olsun veya olmasın doğar üzere. Neyin önemli olduğu, anahtarların ve değerlerin ters çevrildiği haritalara ne sıklıkla sahip olacağınızdır. Bu çarpışmalar, anahtar ve değer kümelerinin aynı olduğu iki harita arasında gerçekleşecektir. Yineleme aynı anahtar-değer çiftleri zorunlu tekrarda aynı sırada değildir sahip

iki unordered_map örneklerini

al. CPP Rerefence diyor ki: iki parametre k1 ve eşit k2, std::hash<Key>()(k1) == std::hash<Key>()(k2) için

.

Bu karma işlev için önemsiz bir gerekliliktir. Çözümünüz bunu engeller çünkü XOR değişmez olduğundan yineleme sırası önemli değildir. Eğer kriptografik bir şeyi gerekmiyorsa

A Olası Çözüm

, sen simetriyi öldürmek için hafifçe çözüm değiştirebilir. Bu yaklaşım, karma masalar ve benzerleri için pratikte tamamdır. Bu çözüm ayrıca bir unordered_map numaralı siparişin tanımsız olduğu gerçeğinden bağımsızdır. Kullandığınız çözeltinin aynısını kullanır (XOR'un Değişkenliği).

std::size_t hash_code(const std::unordered_map<std::string, std::string>& m) { 
    const std::size_t prime = 19937; 
    std::hash<std::string> h; 
    std::size_t result = 0; 
    for (auto&& p : m) { 
     result ^= prime*h(p.first) + h(p.second); 
    } 
    return result; 
} 

Bu durumda bir hash fonksiyonu içinde gereken tek şey

keyfi iyi karma değeri için bir anahtar-değer çifti eşlemek için bir yol ve bir değişmeli kullanarak anahtar-değer çiftlerinin karmaları birleştirmek için bir yoldur operasyon. Böylece sipariş önemli değil. Örneğin yazdım hash_code, anahtar değer çifti karma değeri anahtarının karma ve değer karma bir doğrusal kombinasyonudur. Biraz daha karmaşık bir şey inşa edebilirsiniz, ancak buna gerek yok.

+0

Aha, beklediğim şeye yakın. "taban" muhtemelen bir asal sayı ve keyfi, doğru mu? Elbette bu herhangi bir şifreleme desteği değildir. Bunun, std :: hash'ın kullanımından açıkça anlaşılacağını varsaydım. –

+0

Evet, 19937'yi seçtim çünkü 2^19937 - 1 benim favori Mersenne primerleri. –

+0

ben karışabilir, ancak aynı sırada yinelenilen olmasaydı bu iki eşit haritalar için size iki farklı karma değerlerini veremedim? (yani bu karma siparişe bağlı değil mi?) – Hasturkun

İlgili konular