#c# #gethashcode #hash-code-uniqueness
#c# #gethashcode #уникальность хэш-кода
Вопрос:
Является
public override int GetHashCode()
{
return Word.GetHashCode();
}
Действительно то же самое для
public override int GetHashCode()
{
return (int) Word.GetHashCode() * 7;
}
что касается уникальности?
Word
имеет тип String
РЕДАКТИРОВАТЬ: я забыл сказать, какой из них лучше реализовать в программе, вариант 1 или 2?
Комментарии:
1. Поскольку хэш-коды не являются обязательными и не могут быть уникальными, ответ на ваш вопрос «да» в том смысле, что обе реализации создают неуникальные хэш-коды.
2. Любые столкновения с
Word.GetHashCode()
все равно будут сталкиваться после умножения на 7. Также приведение бессмысленно.3. Расширяя комментарий Джухарра, if
World.GetHashCode()
выдает 6 для worldA и worldB, затемWorld.GetHashCode() * 7
выдает 42 для worldA и worldB…4. Что именно вы имеете в виду? если вы получите два разных уникальных результата для двух слов в первом, вы получите два разных уникальных результата из второго. Аналогично, если вы получите два идентичных результата из двух слов в первом, то второе также даст два идентичных результата. Это кажется несколько очевидным при взгляде на код, хотя кажется, что для вас есть что-то большее, чем этот вопрос, который, я думаю, мог бы быть доработан.
5.
(3 * 7) == (3 * 7)
Действительно ли это то же3 == 3
самое, что и ?
Ответ №1:
Понятно, что любые коллизии в Word.GetHashCode()
реализации приведут к коллизии в (int) Word.GetHashCode() * 7
реализации, потому что умножение одинаковых чисел дает идентичные результаты.
Более интересный вопрос заключается в том, приведут ли непротиворечивые хэш-коды из первой реализации к коллизиям во второй реализации. Оказывается, что ответ «нет», потому что диапазон int
и 7
являются взаимно простыми числами. Следовательно, умножение создает уникальное сопоставление после удаления переполнения.
Вы можете запустить небольшой тест с двухбайтовыми хэш-кодами, чтобы посмотреть, что произойдет:
const int Max = 1<<16;
var count = new int[Max];
for (int i = 0 ; i != Max ; i ) {
count[(i * 7) amp; (Max-1)] ;
}
var notOne = 0;
for (int i = 0 ; i != Max ; i ) {
if (count[i] != 1) {
notOne ;
}
}
Console.WriteLine("Count of duplicate mappings found: {0}", notOne);
Эта программа сопоставляет i
значение хэш-кода по 7 * i
модулю 2 16 и проверяет, что каждое число из диапазона создается ровно один раз.
Count of duplicate mappings found: 0
Если вы 7
замените четным числом, результат будет совсем другим. Теперь несколько хэш-кодов из исходного набора будут сопоставлены с одним хэш-кодом в целевом наборе. Вы можете понять это интуитивно, если вспомните, что при умножении на четное число младший значащий бит всегда равен нулю. Следовательно, некоторая информация теряется, в зависимости от того, сколько раз четное число можно разделить на два.
какой из них лучше?
Разницы нет.
Примечание: Вышеизложенное предполагает, что вы игнорируете переполнение целых чисел.
Комментарии:
1. Да, я забыл сказать, что номер pime был указан специально. Итак, какой из них лучше вариант A или B?
2. Поскольку это показывает, что разницы нет, используйте более простой вариант.
3. @HendrikBreezy Поскольку .NET тщательно использует количество основных сегментов, разницы нет.
Ответ №2:
Поскольку вы не запускаете код в unchecked
контексте, то последний будет генерировать исключение каждый раз, когда происходит переполнение, что достаточно вероятно (будет генерироваться 6/7 диапазона хэшей, поэтому обычно равномерно распределенный хэш-код имеет вероятность ~ 6/7 исключения).
Комментарии:
1. Глядя на msdn.microsoft.com/en-gb/library/a569z7k8.aspx в нем говорится, что «Выражения, содержащие непостоянные термины, по умолчанию не отмечены во время компиляции и выполнения», так не означает ли это, что они были бы сняты, если бы не были отмечены явно? Я признаю, что на самом деле я не играл ни с чем, где мне нужно было беспокоиться о checked / unchecked, поэтому я могу легко ошибаться…
2. @Chris Компилятор C # и VS по умолчанию не отмечены.