Получение хеша списка строк независимо от порядка

Вам необходимо заменить

//div[contains(@class='loader-overlay')]

на

//div[contains(@class, 'loader-overlay')]

Обратите внимание, что вы должны использовать синтаксис [@attr = "value"], если хотите проверить, является ли значение атрибута точно [ 113] , но contains синтаксис [contains(@attr, "value")]

62
задан nawfal 9 August 2014 в 11:40
поделиться

3 ответа

Существуют всевозможные подходы здесь под двумя основными категориями, каждый обычно с их собственными преимуществами и недостатками, с точки зрения эффективности и производительности. Вероятно, лучше выбрать самый простой алгоритм для любого приложения и только использовать более сложные варианты при необходимости для любой ситуации.

Примечание, которое эти примеры используют EqualityComparer<T>.Default, так как это будет иметь дело с пустыми элементами чисто. Вы могли добиться большего успеха, чем нуль для пустого указателя при желании. Если T ограничивается к структуре, это является также ненужным. Можно поднять EqualityComparer<T>.Default поиск из функции, раз так желаемой.

Коммутативные Операции

при использовании операций на хэш-кодах отдельных записей, которые являются коммутативные тогда, это приведет к тому же конечному результату независимо от порядка.

на числах существует несколько очевидных опций:

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

Одна оборотная сторона этого - то, что хеш для {"x", "x"} совпадает с хешем для {"y", "y"}. Если это не проблема для Вашей ситуации, хотя, это - вероятно, простое решение.

Дополнение

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

Переполнение прекрасно здесь, следовательно явное unchecked контекст.

существуют все еще некоторые противные случаи (например, {1,-1} и {2,-2}, но это, более вероятно, будет хорошо, особенно со строками. В случае списков, которые могут содержать такие целые числа, Вы могли всегда реализовывать пользовательскую хеш-функцию (возможно, тот, который берет индекс повторения определенного значения в качестве параметра и возвращает уникальный хэш-код соответственно).

Вот пример такого алгоритма, который обходит вышеупомянутую проблему довольно эффективным способом. Это также обладает преимуществом большого увеличения распределения сгенерированных хэш-кодов (см. статью, связанную в конце для некоторого объяснения). Математический / статистический анализ точно, как этот алгоритм производит "лучшие" хэш-коды, был бы вполне усовершенствован, но тестирование его через большой спектр входных значений и графического изображения результатов должно проверить его достаточно хорошо.

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

Умножение

, Который имеет немногих если преимущества по дополнению: небольшие числа и соединение положительных и отрицательных чисел они могут привести к лучшему распределению битов хеша. Поскольку отрицание для возмещения этого "1" становится бесполезной записью, вносящей ничто и любые нулевые результаты элемента в нуле. Вы можете нуль особого случая для не порождения этого главного дефекта.

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

Порядок сначала

другой базовый подход должен осуществить некоторое упорядочивание сначала, затем использовать любую функцию комбинации хеша, которую Вы любите. Упорядочивание себя является несущественным, пока это последовательно.

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

Это обладает некоторыми значительными преимуществами в этом, операции объединения, возможные в f, могут иметь значительно лучшие свойства хеширования (распределение битов, например), но это прибывает в значительно более высокую стоимость. Вид O(n log n), и необходимая копия набора является выделением памяти, которого Вы не можете избежать, учитывая требование постараться не изменять оригинал. GetHashCode реализации должны обычно избегать выделений полностью. Одна возможная реализация [1 111] была бы подобна данному в последнем примере под разделом Addition (например, любое постоянное количество сдвигов разряда, оставленных сопровождаемыми умножением началом - Вы могли даже использовать последовательные начала на каждом повторении без дополнительной платы, так как они только должны быть сгенерированными однажды).

Тем не менее, если Вы имели дело со случаями, где Вы могли вычислить и кэшировать хеш и амортизировать стоимость по многим вызовам к [1 112], этот подход может привести к превосходящему поведению. Также последний подход еще более гибок, так как он может избежать потребности использовать GetHashCode на элементах, если он знает их тип, и вместо этого используйте на операции байта на них для получения еще лучшего распределения хеша. Такой подход был бы, вероятно, полезен только в случаях, где производительность была определена как являющийся значительным узким местом.

Наконец, если Вы хотите довольно всесторонний и довольно нематематический обзор предмета хэш-кодов и их эффективности в целом, , эти сообщения в блоге были бы стоящими чтениями, в особенности Реализация простого алгоритма хеширования (pt II) сообщение.

74
ответ дан 23 revs, 4 users 42% 24 November 2019 в 16:50
поделиться

Альтернатива сортировке списков строк должна была бы получить хэш-коды строк и затем отсортировать хэш-коды. (Сравнение ints является менее дорогим, чем сравнение строк.) Можно тогда использовать алгоритм для слияния хэш-кодов, который (надо надеяться), дает лучшее распределение.

Пример:

GetHashCodeOfList<T>(IEnumerable<T> list) {
   List<int> codes = new List<int>();
   foreach (T item in list) {
      codes.Add(item.GetHashCode());
   }
   codes.Sort();
   int hash = 0;
   foreach (int code in codes) {
      unchecked {
         hash *= 251; // multiply by a prime number
         hash += code; // add next hash code
      }
   }
   return hash;
}
21
ответ дан TeaDrivenDev 24 November 2019 в 16:50
поделиться
    Dim list1 As ArrayList = New ArrayList()
    list1.Add("0")
    list1.Add("String1")
    list1.Add("String2")
    list1.Add("String3")
    list1.Add("abcdefghijklmnopqrstuvwxyz")

    Dim list2 As ArrayList = New ArrayList()
    list2.Add("0")
    list2.Add("String3")
    list2.Add("abcdefghijklmnopqrstuvwxyz")
    list2.Add("String2")
    list2.Add("String1")
    If GetHashCodeOfList(list1) = GetHashCodeOfList(list2) Then
        Stop
    Else
        Stop
    End If
    For x As Integer = list1.Count - 1 To 0 Step -1
        list1.RemoveAt(list1.Count - 1)
        list2.RemoveAt(list2.Count - 1)
        Debug.WriteLine(GetHashCodeOfList(list1).ToString)
        Debug.WriteLine(GetHashCodeOfList(list2).ToString)
        If list1.Count = 2 Then Stop
    Next


Private Function GetHashCodeOfList(ByVal aList As ArrayList) As UInt32
    Const mask As UInt16 = 32767, hashPrime As Integer = Integer.MaxValue
    Dim retval As UInt32
    Dim ch() As Char = New Char() {}
    For idx As Integer = 0 To aList.Count - 1
        ch = DirectCast(aList(idx), String).ToCharArray
        For idCH As Integer = 0 To ch.Length - 1
            retval = (retval And mask) + (Convert.ToUInt16(ch(idCH)) And mask)
        Next
    Next
    If retval > 0 Then retval = Convert.ToUInt32(hashPrime \ retval) 'Else ????
    Return retval
End Function
0
ответ дан dbasnett 24 November 2019 в 16:50
поделиться