Ответ 1
Основы, отмеченные Марксом и Джоном, не плохи, но они далеки от оптимальных с точки зрения их равномерности распределения результатов. К сожалению, подход "умножить на простые", скопированный таким количеством людей из Кнута, не лучший выбор во многих случаях лучшее распределение может быть достигнуто более дешевым вычислять функции (хотя это очень мало на современном оборудовании). Фактически бросание простых чисел во многие аспекты хэширования нет панацеи.
Если эти данные используются для хэш-таблиц значительного размера, я рекомендую читать Брет Малвеи, отличное исследование и объяснение различных современных (и не очень современных) методов хэширования с помощью С#.
Обратите внимание, что поведение со строками различных хэш-функций сильно смещено в сторону того, чтобы строки были короткими (грубо говоря, сколько символов хэшируется до того, как бит начнет перетекать) или долго.
Один из самых простых и простых в реализации - также один из лучших, Jenkins One за один раз.
private static unsafe void Hash(byte* d, int len, ref uint h)
{
for (int i = 0; i < len; i++)
{
h += d[i];
h += (h << 10);
h ^= (h >> 6);
}
}
public unsafe static void Hash(ref uint h, string s)
{
fixed (char* c = s)
{
byte* b = (byte*)(void*)c;
Hash(b, s.Length * 2, ref h);
}
}
public unsafe static int Avalanche(uint h)
{
h += (h<< 3);
h ^= (h>> 11);
h += (h<< 15);
return *((int*)(void*)&h);
}
вы можете использовать его так:
uint h = 0;
foreach(string item in collection)
{
Hash(ref h, item);
}
return Avalanche(h);
вы можете объединить несколько разных типов:
public unsafe static void Hash(ref uint h, int data)
{
byte* d = (byte*)(void*)&data;
AddToHash(d, sizeof(int), ref h);
}
public unsafe static void Hash(ref uint h, long data)
{
byte* d= (byte*)(void*)&data;
Hash(d, sizeof(long), ref h);
}
Если у вас есть только доступ к полю как объект без знания внутренних элементов, вы можете просто вызвать GetHashCode() на каждом из них и объединить это значение следующим образом:
uint h = 0;
foreach(var item in collection)
{
Hash(ref h, item.GetHashCode());
}
return Avalanche(h);
К сожалению, вы не можете делать sizeof (T), поэтому вы должны делать каждую структуру индивидуально.
Если вы хотите использовать отражение, вы можете построить на основе каждого типа функцию, которая выполняет структурную идентичность и хеширование во всех полях.
Если вы хотите избежать небезопасного кода, вы можете использовать методы маскирования бит, чтобы вытаскивать отдельные биты из ints (и символы, если они имеют дело со строками), при этом не слишком много лишних хлопот.