Во-первых, я думаю, что требования не совсем ясны. Если вы хэшируете три набора данных c1, c2 и c3. Затем, если вы переключитесь, c1.copyNumber и c2.copyNumber и снова хешируйте. Должен ли это дать тот же результат или нет?
Если вы переключите c1.startLocation с c1.endLocation. Должно ли это привести к тому же хешу или нет?
Я собираюсь предположить, что вы хотели бы иметь разные результаты хеширования в обоих случаях и что единственная перестановка, которая не должна изменять результат хеширования, это перестановки наборов данных c1, c2, c3.
Если это так, то я бы предложил сначала хэшировать три набора данных независимо от меньших значений. То есть
h1 = H (c1)
h2 = H (c2)
h3 = H (c3)
где H может быть любой хеш-функцией (например, CRC32, Adler32, SHA1 и т. д.), в зависимости от того, как сильно вы хотите избежать коллизий.
Следующим шагом будет вычисление коммутативного хеша h1, h2, h3. Если вы хотите избежать коллизий, если только h1, h2, h3 не переставлены, тогда работает следующее.
Вычислить полином
- P (x) = (x-h1) (x-h2) (x-h3)
затем хеширует полином (rsp. Его коэффициенты) с любой хорошей хеш-функцией. То есть тот
будет
- H (h1 + h2 + h3 || h1 * h2 + h1 * h3 + h2 * h3 || h1 * h2 * h3), где || это конкатенация.
Если вы хотите избежать любой ненужной коллизии любой ценой, то коэффициенты должны быть вычислены как целые числа с множественной точностью, и должна использоваться хеш-функция, устойчивая к коллизиям, такая как SHA1. Из-за уникального факторизационного свойства многочленов следует, что коэффициенты многочлена различны, если h1, h2 и h3 различны.
Но кажется, что избегать столкновений любой ценой в вашем случае - излишне.
Таким образом, вместо того, чтобы вычислять полином P (x) символически, можно просто оценить его по произвольному значению R. I.e. если h1, h2, h3 являются просто 32-битными значениями, тогда вычисляется следующее
может быть достаточно: (какой-то псевдокод типа C следует. Я не знаю, что C # использует для 64-битных целых чисел)
const long long R = SOME_RANDOM_64_BIT_CONSTANT;
long long hash0 = (R - h1) * (R - h2) * (R - h3);
int hash = (int) (hash0 >> 32);
Я здесь использую 64-битное умножение, потому что они достаточно быстрые на современных процессорах, и я использую верхний 32-битный хэш-код, а не 32-битный, потому что младшие 32-битные смещены. Т.е. младший значащий бит с большей вероятностью будет равен 0, чем 1.