Хеш-функция в списке не зависит от порядка элементов в нем

Я хочу иметь словарь, который присваивает значение набору целых чисел.

Например, key равно [1 2 3], а value будет иметь определенное значение.

Дело в том, что [3 2 1] в моем случае нужно обрабатывать одинаково, поэтому хэш должен быть равен, если я использую хэш-подход.

В наборе будет от 2 до 10 предметов.

Сумма элементов обычно фиксирована, поэтому мы не можем сделать хэш-код в соответствии с суммой, что является здесь первой естественной идеей.

Не домашнее задание, на самом деле столкнулся с этой проблемой в моем коде.

Этот набор в основном IEnumerable<int> в C#, поэтому любая структура данных подходит для их хранения.

Любая помощь приветствуется. Здесь также важна производительность.

Сразу мысль: можно было бы подвести итоги items^2 и уже получить какой-то более качественный хэш, но все же хотелось бы услышать некоторые мысли.

РЕДАКТИРОВАТЬ: хм очень жаль, ребята, все предлагают заказать, мне не пришло в голову, что мне нужно сказать, что на самом деле заказ и хеширование - это текущее решение, которое я использую, и я рассматриваю более быстрые альтернативы.


person Valentin Kuzub    schedule 18.11.2011    source источник
comment
Рассматривали ли вы использование упорядоченного набора в качестве ключа вместо ienumerable?   -  person asawyer    schedule 19.11.2011
comment
заказ стоит дорого, поэтому да, но он не соответствует желаемой производительности, я бы не сортировал 10 элементов перед их хешированием.   -  person Valentin Kuzub    schedule 19.11.2011
comment
сортировка неплохая на 10 шт.   -  person Daniel A. White    schedule 19.11.2011
comment
Каков будет типичный диапазон ваших значений?   -  person Henk Holterman    schedule 19.11.2011
comment
@DanielA.White, ну, я думаю, все зависит от определения производительности. Если бы я мог избежать проверок и свопов, необходимых для сортировки 10 элементов и немедленного хеширования с хорошим распределением, очевидно, это было бы лучше, верно?   -  person Valentin Kuzub    schedule 19.11.2011
comment
Предметы @HenkHolterman могут быть примерно от 1 до 300000, сумма примерно от 10000 до 10000000   -  person Valentin Kuzub    schedule 19.11.2011
comment
поскольку в идеале ключ будет неизменным, вы можете вычислить его один раз и сохранить результат.   -  person Daniel A. White    schedule 19.11.2011
comment
была мысль, что этот ключ действительно неизменяем, однако эти списки/наборы генерируются где-то еще, они не являются легковесами, и хранение вычисленного хэш-ключа будет бесполезным, потому что обычно он не будет вызываться более 1 раза.   -  person Valentin Kuzub    schedule 19.11.2011
comment
@HenkHolterman также часто различает предметы (например, в 95% случаев)   -  person Valentin Kuzub    schedule 19.11.2011
comment
С диапазоном 300 тыс. и небольшими наборами (~ 10) я бы перестал беспокоиться и просто суммировал элементы. В любом случае вы не собираетесь полностью избегать столкновений, и скорость будет неплохой.   -  person Henk Holterman    schedule 19.11.2011
comment
sum обычно является константой, как я говорю в вопросе: сумма элементов обычно фиксирована, поэтому их сложение гарантирует коллизию. Он может просто находиться в этом диапазоне, но во время работы функции он обычно имеет дело с большим набором наборов с одинаковой суммой.   -  person Valentin Kuzub    schedule 19.11.2011


Ответы (9)


В основном все подходы здесь являются экземплярами одного и того же шаблона. Сопоставьте x1, …, xn с f(x1) op … op f(xn) , где op — коммутативно-ассоциативная операция над некоторым множеством X, а f — отображение элементов в X. Этот шаблон использовался пару раз и доказуемо хорош.

  • Выберите случайное большое простое число p и случайный остаток b из [1, p - 1]. Пусть f(x) = bx mod p и op — сложение. По сути, мы интерпретируем множество как многочлен и используем лемму Шварца–Циппеля, чтобы ограничить вероятность столкновения (= вероятность того, что ненулевой многочлен имеет корень b по модулю p).

  • Пусть op будет XOR, а f будет случайно выбранной таблицей. Это хеширование Зобриста, которое минимизирует ожидаемое количество коллизий с помощью простых линейно-алгебраических аргументов.

Модульное возведение в степень выполняется медленно, поэтому не используйте его. Что касается хеширования Зобриста, то с 3 миллионами элементов таблица f, вероятно, не поместится в L2, хотя и устанавливает верхнюю границу одного обращения к основной памяти.

Вместо этого я бы взял хеширование Зобриста в качестве отправной точки и поискал дешевую функцию f, которая ведет себя как случайная функция. По сути, это описание работы некриптографического генератора псевдослучайных чисел — я бы попытался вычислить f, заполнив быструю PRG значением x и сгенерировав одно значение.

РЕДАКТИРОВАТЬ: учитывая, что все наборы имеют одинаковые суммы, не выбирайте f как полином степени 1 (например, ступенчатую функцию линейного конгруэнтного генератора).

person Per    schedule 18.11.2011
comment
Фильтры Блума можно рассматривать как еще одну хэш-функцию для наборов, хотя это, конечно, не является их основным применением. Здесь op = побитовое ИЛИ, а f(x) — разреженный массив битов 0-1. - person Per; 19.11.2011
comment
@ Хенк Холтерман Я понятия не имею, для чего нужны пугающие кавычки (доказуемое есть доказуемое), но я сделал примечание о том, что не использую полином степени 1 для f. - person Per; 19.11.2011

Используйте HashSet<T> и HashSet<T>.CreateSetComparer(), которые возвращают IEqualityComparer<HashSet<T>>.

person SLaks    schedule 18.11.2011
comment
только что пришла такая мысль. но есть вероятность, что он сортирует предметы или не очень эффективен, я считаю. Я не знаю, какой алгоритм он использует, а вы? - person Valentin Kuzub; 19.11.2011
comment
проверил код, мне он не очень нравится: foreach (T local in obj) { num ^= this.m_comparer.GetHashCode(local) & 0x7ffffffff; } - person Valentin Kuzub; 19.11.2011
comment
@ValentinKuzub, он использует XOR всех элементов (.NET 4.0) - person Ivan Bianko; 19.11.2011

Я думаю, что то, что упоминается в этой статье, определенно поможет:

http://people.csail.mit.edu/devadas/pubs/mhashes.pdf

Инкрементные мультимножественные хэш-функции и их применение для проверки целостности памяти

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

person derekhh    schedule 18.11.2011
comment
Из вашего описания видно, что основное внимание уделяется функции, которая является кумулятивной таким образом, что при увеличении размера набора хэш не нужно полностью пересчитывать. Я не уверен, что это относится к моей проблеме или? - person Valentin Kuzub; 19.11.2011
comment
Я бы подумал, что хэши криптокласса слишком медленные. - person Per; 19.11.2011
comment
@ValentinKuzub: еще одна важная особенность хеш-функций, упомянутых в этой статье, заключается в том, что они определены в наборах, а не в строках, что делает значения инвариантными по отношению к порядку элементов в наборе, ИМХО. - person derekhh; 19.11.2011
comment
@Per: Да, это действительно проблема... кстати, вы Пер Острин? - person derekhh; 19.11.2011
comment
@derekhh Нет, я не Пер Остин. - person Per; 19.11.2011

Я думаю, что ваша идея возведения в квадрат движется в правильном направлении, но это плохой выбор функции. Я бы попробовал что-то более похожее на функции PRNG или просто умножение на большое простое число, за которым следует XOR всех полученных значений.

person phkahler    schedule 18.11.2011

Если диапазон значений в key ограничен небольшими положительными целыми числами, вы можете сопоставить каждое из них с простым числом, используя простой поиск, а затем перемножить их вместе, чтобы получить value.

Используя пример в вопросе:

[1, 2, 3] maps to 2 x 3 x 5 = 30
[3, 2, 1] maps to 5 x 3 x 2 = 30
person James Droscha    schedule 24.03.2017

Одна из возможностей: отсортировать элементы в списке, а затем хешировать их.

person Joe    schedule 18.11.2011

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

person perreal    schedule 18.11.2011

Почему бы не что-то вроде

public int GetOrderIndependantHashCode(IEnumerable<int> source)
{
    return (source.Select(x => x*x).Sum()
            + source.Select(x => x*x*x).Sum()
            + source.Select(x => x*x*x*x).Sum()) & 0x7FFFFF;
}
person Ivan Bianko    schedule 18.11.2011
comment
помните, что мы боремся с подходом сортировки. у нас здесь много умножений и суммирования, сортировка может превзойти это. - person Valentin Kuzub; 19.11.2011

Создайте свой собственный тип, реализующий IEnumerable<T>.

Переопределить GetHashCode. В нем разбери свою коллекцию, позвони и верни ToArray().GetHashCode().

person Daniel A. White    schedule 18.11.2011