Способ хранения ключей в словаре без сохранения ключа?

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

Иногда ключи длинные и обычно представляют собой строки, поэтому каждый ключ может иметь размер более 40 байт по сравнению с тем, если бы это был просто хэш-код. Что, если сохраненный ключ был хешированным объектом, но с использованием немного другого алгоритма хеширования с другими простыми числами? Тогда вероятность столкновения будет (1/(2^32)) * (1/(2^32)).

Вы даже можете использовать другой алгоритм хеширования и вместо этого хранить этот хэш, поэтому шансы на коллизию будут (1/(2^32)) * (1/(2^32)) * (1/(2^32)). Очевидно, что коллизия ВСЕ ЕЩЕ может произойти, но шансы настолько малы, и вы экономите так много памяти, сохраняя только 4 байта для ключа вместо более 32 байтов.

Я предполагаю, что это по-прежнему неприемлемо, верно, потому что все еще есть ШАНС, но также есть шанс, что чья-то оперативная память может случайно немного перевернуться и появится синий экран, и это кажется настолько маловероятным, что заманчиво не реализовать. Есть ли какие-то альтернативы или маленький шанс все же не стоит того?


person John Smith    schedule 03.03.2015    source источник
comment
Смысл ключа в dictionary заключается в том, что у вас есть известный уникальный индексатор, и вы хотите найти значение, хранящееся в этом индексе. Если у вас есть нестандартный хэш, который вы можете получить только из самого объекта, вам, вероятно, не нужен словарь, а только список.   -  person ps2goat    schedule 03.03.2015
comment
Если ваш хэш 32 бита, вероятность коллизии будет намного выше, чем 1/2^32. И на каждую такую ​​хэш-функцию вам понадобится 4 байта, то есть 12 байт на 3 из них. Вполне вероятно, что это произойдет с такими маленькими хэшами, поэтому, если вы должны абсолютно избегать коллизий, я предлагаю вам сэкономить 40 байтов.   -  person IVlad    schedule 03.03.2015
comment
Вы также предполагаете идеальную хэш-функцию с идеально распределенной вероятностью каждого значения, чего в действительности никогда не бывает.   -  person Servy    schedule 03.03.2015
comment
Даже если бы вы хранили 6 хэшей, каждый из которых вычислялся с разными простыми числами, это все равно использовало бы меньше памяти, чем хэш строкового ключа, и вероятность столкновения была бы близка к 1 из 10^50. Я знаю, что еще есть шанс, и хеширование не идеально, но это кажется заманчивым.   -  person John Smith    schedule 03.03.2015
comment
Нет, это не будет 1 из 10^50. Откуда вы берете эти цифры? На практике это было бы намного более вероятно, в зависимости от фактических алгоритмов, используемых для хэшей.   -  person IVlad    schedule 03.03.2015
comment
Примерно так работает HashSet.   -  person JNYRanger    schedule 03.03.2015
comment
@IVlad: я получаю число из хэша, который является 32-битным ответом и с шестью различными алгоритмами хеширования, используемыми для одного и того же ключа, и каждый сохраненный хэш составляет около (1/(2^32)) * (1/(2^32)) * (1/(2^32)) * (1/(2^32)) * (1/(2^32)) * (1/(2^32)) или 1.59 × 10^-58 Очевидно, что хеширование не совсем однородно, поэтому я вынул много порядков, чтобы компенсировать.   -  person John Smith    schedule 03.03.2015


Ответы (2)


Это зависит.

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

Если нет: да, ваш подход будет работать. Обратите внимание, что из-за парадокса дня рождения вероятность столкновения составляет:

  • зависит от количества элементов, уже находящихся в коллекции; а также
  • выше, чем вы думаете.

Есть компромисс: теперь вам нужно вычислить (и сравнить) несколько хэшей, чтобы найти элементы.

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

Большая часть этого излишня для всех, кроме самых высокопроизводительных и/или приложений с ограниченным объемом памяти, но иногда полезно делать такие вещи :)

person candu    schedule 03.03.2015
comment
Пробы интересные. У меня может быть идея, основанная на этом. - person John Smith; 04.03.2015

Если вы хотите быть на 100% уверены, что нет никаких коллизий, нет способа проверить ключ перед вставкой. При этом нам повезло, потому что хорошо реализованный словарь — это именно то, что вам нужно для быстрого поиска ключа.

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

РЕДАКТИРОВАТЬ: Удалены некоторые глупости, которые я написал о GUID...

person Renaud Gauthier    schedule 03.03.2015
comment
Согласен: GUID — это гораздо лучшая идея, чем вручную комбинировать неизвестно какие хэш-функции. - person IVlad; 03.03.2015
comment
Как бы вы узнали, что ключ указывает на GUID? Нет никакой ассоциации. - person John Smith; 03.03.2015
comment
Вы правы, эта часть моего ответа была глупой и не поддающейся исправлению. Я утверждаю, что ваши ключи хранятся де-факто и их легко проверить. Позвольте мне отредактировать это... - person Renaud Gauthier; 03.03.2015