SORU
21 Mart 2009, CUMARTESİ


Sırası ne olursa olsun dizeleri bir listesini bulmak karma

Sırası ne olursa olsun, karma-kod dizeleri bir liste döndüren bir fonksiyon GetHashCodeOfList() yazmak istiyorum. Verilen 2 Aynı dizeleri listeler aynı karma kodu döndürür.

ArrayList list1 = new ArrayList()    
list1.Add("String1");
list1.Add("String2");
list1.Add("String3");    

ArrayList list2 = new ArrayList()    
list2.Add("String3");    
list2.Add("String2"); 
list2.Add("String1");

GetHashCodeOfList(list1) = GetHashCodeOfList(list2) //this should be equal.

Birkaç düşünce vardı:

  1. İlk liste sıralama, 1 uzun içine sıralanmış listesini birleştirmek dizgisi ve GetHashCode() Ara verebilirim. Ancak sıralama yavaş bir işlem.

  2. Listedeki her bireyin string (string.GetHashCode() çağırarak) karma, sonra tüm karma çarpma ve Mod UInt32.MaxValue çağrı alabilirim. Örneğin: "String1".GetHashCode() * "String2".GetHashCode * … MOD UInt32.MaxValue. Ama bu sayı taşmasına neden.

Düşüncesi olan var mı?

Yardımlarınız için şimdiden teşekkürler.

CEVAP
21 Mart 2009, CUMARTESİ


Altında iki ana kategoride, kendi avantajları ve dezavantajları ile her genellikle burada çeşitli farklı yaklaşımlar, etkililik ve performans açısından. En iyisi her uygulama için basit bir algoritma seçin ve sadece ne olursa olsun durum için gerekirse daha karmaşık türevleri kullanın.

Bu örnekler o boş elemanları ile temiz bir şekilde ilgileneceğiz beri EqualityComparer<T>.Default kullandığını unutmayın. İsterseniz daha iyi sıfır boş yapabilirsin. Eğer T yapı için kısıtlı ise de gereksizdir. Eğer arzu ederseniz fonksiyonu EqualityComparer<T>.Default arama kaldırma olabilir.

Değişmeli İşlemleri

Eğer commutative tek tek girişleri hashcodes işlemleri kullanıyorsanız, bu aynı sonuca sırası ne olursa olsun açacaktır.

Sayıları birkaç bariz seçenek vardır:

XOR

public static int GetOrderIndependentHashCode<T>(IEnumerable<T> source)
{
    int hash = 0;
    foreach (T element in source)
    {
        hash = hash ^ EqualityComparer<T>.Default.GetHashCode(element);
    }
    return hash;
}

Bunun bir dezavantajı için karma, { "x", "x" } karma aynıdır { "", "" } y. y Eğer bu durum sizin için bir sorun değilse, muhtemelen en basit çözüm.

Ayrıca

public static int GetOrderIndependentHashCode<T>(IEnumerable<T> source)
{
    int hash = 0;
    foreach (T element in source)
    {
        hash = unchecked (hash   
            EqualityComparer<T>.Default.GetHashCode(element));
    }
    return hash;
}

Taşma burada iyiyim, bu yüzden unchecked açık içerik.

Hala bazı kötü durumlarda (örneğin {1, -1} ve {2, -2} ama sorun, özellikle telli olması daha büyük bir ihtimal. Söz konusu listeler içerebilir gibi tamsayılar, her zaman uygulamaya özel bir karma fonksiyon (belki bunu alır dizin tekrarlama belirli bir değeri parametre olarak verir ve benzersiz bir hash kodu buna göre).

Burada oldukça verimli bir şekilde söz konusu sorun olur etrafında böyle bir algoritma örneğidir. Bu da büyük ölçüde karma kodları oluşturulan dağılımı artırma yararı (madde bazı açıklama için sonunda bağlı) vardır. Matematiksel/istatistiksel analiz tam olarak nasıl bu algoritma üretir "daha iyi" karma kodları olurdu oldukça gelişmiş, ama test boyunca büyük aralığı giriş değerleri ve komplo sonuçlarını doğrulamak gerekir o kadar iyi.

public static int GetOrderIndependentHashCode<T>(IEnumerable<T> source)
{
    int hash = 0;
    int curHash;
    int bitOffset = 0;
    // Stores number of occurences so far of each value.
    var valueCounts = new Dictionary<T, int>();

    foreach (T element in source)
    {
        curHash = EqualityComparer<T>.Default.GetHashCode(element);
        if (valueCounts.TryGetValue(element, out bitOffset))
            valueCounts[element] = bitOffset   1;
        else
            valueCounts.Add(element, bitOffset);

        // The current hash code is shifted (with wrapping) one bit
        // further left on each successive recurrence of a certain
        // value to widen the distribution.
        // 37 is an arbitrary low prime number that helps the
        // algorithm to smooth out the distribution.
        hash = unchecked(hash   ((curHash << bitOffset) |
            (curHash >> (32 - bitOffset))) * 37);
    }

    return hash;
}

Çarpma

Birkaç sahip olan ayrıca üzerinde faydaları: küçük sayılar ve karma bit daha iyi bir dağıtım yol açabilir pozitif ve negatif sayılar bir karışımı. "1" işe yaramaz bir şey girdi ve bir sıfır sıfır herhangi bir unsur sonuçları katkısı olur.bu ofset bir olumsuz olarak Özel durum sıfır bu büyük kusuru sebebi değil.

public static int GetOrderIndependentHashCode<T>(IEnumerable<T> source)
{
    int hash = 17;
    foreach (T element in source)
    {
        int h = EqualityComparer<T>.Default.GetHashCode(element);
        if (h != 0)
            hash = unchecked (hash * h);
    }
    return hash;
}

Sipariş önce

Diğer temel yaklaşım biraz sipariş ilk Uygula, istediğin karma kombinasyon işlevi kullanın. Manevi tutarlı olduğu sürece, kendisi sipariş.

public static int GetOrderIndependentHashCode<T>(IEnumerable<T> source)
{
    int hash = 0;
    foreach (T element in source.OrderBy(x => x, Comparer<T>.Default))
    {
        // f is any function/code you like returning int
        hash = f(hash, element);
    }
    return hash;
}

Bu birleştirme işlemleri f Olası önemli ölçüde daha iyi karma özelliklere sahip olabilir bazı önemli avantajlar vardır (örneğin bit dağıtım) ama bu önemli ölçüde daha yüksek maliyetle geliyor. Sıralama O(n log n) ve toplama gerekli kopya orijinal değiştirmek önlemek için arzu belirli kaçamazsın bir bellek ayırma. GetHashCode uygulamaları normalde ayırmalarını tamamen kaçınmalısınız. Olası bir uygulama f olurdu benzer için verilen son örnek altında Ek bölüm (örneğin herhangi bir sabit sayıda bit sola kaydırır ve ardından bir çarpma ile bir prime - bile kullanmak ardışık asal sayılar üzerinde her tekrarında hiçbir ekstra maliyet, çünkü tek ihtiyacı oluşturulabilir bir kez).

Bu, eğer hesaplamak ve önbellek karma ve 24 ** birçok arama maliyet üzerinden amorti edebilirsin durumlar ile ilgili bu yaklaşım, üstün davranışları getirebilecek " dedi. Ayrıca ikinci yaklaşım daha esnek beri olabilir önlemek gerek kullanmak için GetHashCode üzerinde unsurlar varsa bilir, kendi türüne ve yerine kullanım başına bayt işlemleri onlara boyun daha da iyi karma dağıtım. Böyle bir yaklaşım büyük olasılıkla sadece performans önemli bir darboğaz olarak tespit edilmiştir durumlarda faydalı olurdu.

Eğer hash kodları konusu ve genel olarak etkinliği oldukça kapsamlı olan ve olmayan matematiksel adil bir bakış istiyorsanız son olarak, these blog posts faydalı olacaktır, özellikle okurBasit bir karma algoritma (pt II) uygulamapost.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • fireflame65

    fireflame65

    27 Mart 2007
  • Justin Schenck

    Justin Schen

    24 Kasım 2006
  • SaMmM123

    SaMmM123

    3 Mayıs 2006