Хотя шаблон, изложенный в ответе Джона Скита, в целом хорошо работает как семейство хеш-функций, выбор констант важен, а начальное число 17
и коэффициент 31
, как отмечено в ответе, не работают должным образом. вообще для общих случаев использования. В большинстве случаев хэшированные значения намного ближе к нулю, чем int.MaxValue
, а количество совместно хэшируемых элементов составляет несколько десятков или меньше.
Для хеширования целочисленного кортежа {x, y}
, где -1000 <= x <= 1000
и -1000 <= y <= 1000
, он имеет ужасную частоту столкновений почти 98,5%. Например, {1, 0} -> {0, 31}
, {1, 1} -> {0, 32}
и т. Д. Если мы расширим покрытие, включив также n-кортежи, где 3 <= n <= 25
, оно будет менее страшным с частотой столкновений около 38%. Но мы можем сделать намного лучше.
public static int CustomHash(int seed, int factor, params int[] vals)
{
int hash = seed;
foreach (int i in vals)
{
hash = (hash * factor) + i;
}
return hash;
}
Я написал цикл поиска выборки по методу Монте-Карло, который проверял описанный выше метод с различными значениями начального числа и коэффициента для различных случайных n-кортежей случайных целых чисел i
. Допустимые диапазоны: 2 <= n <= 25
(где n
был случайным, но смещенным к нижнему пределу диапазона) и -1000 <= i <= 1000
. Для каждой пары семян и факторов было выполнено не менее 12 миллионов уникальных испытаний на столкновение.
Примерно через 7 часов работы лучшая найденная пара (где начальное число и коэффициент были ограничены 4 цифрами или менее) была: seed = 1009
, factor = 9176
с частотой столкновений 0,1131%. В 5- и 6-значных областях существуют даже лучшие варианты. Но для краткости я выбрал лучший 4-значный исполнитель, и он довольно хорошо работает во всех распространенных сценариях хеширования int
и char
. Кажется, он также отлично работает с целыми числами гораздо больших величин.
Стоит отметить, что «простота», похоже, не являлась общей предпосылкой для хорошей производительности в качестве семени и / или фактора, хотя это, вероятно, помогает. 1009
отмеченное выше на самом деле простое, а 9176
- нет. Я явно протестировал варианты этого, где я изменил factor
на различные простые числа около 9176
(оставляя seed = 1009
), и все они работали хуже, чем в приведенном выше решении.
Наконец, я также сравнил с общим набором функций рекомендации ReSharper hash = (hash * factor) ^ i;
и оригинальным CustomHash()
, как отмечалось выше, он значительно превосходит его. Стиль ReSharper XOR, по-видимому, имеет частоту столкновений в диапазоне 20-30% для общих предположений варианта использования и, по моему мнению, не должен использоваться.