Хорошая хэш-функция для перестановок?
у меня есть цифры в определенном диапазоне (обычно от 0 до около 1000). Алгоритм выбирает некоторые числа из этого диапазона (от 3 до 10 чисел). Этот выбор делается довольно часто, и мне нужно проверить, была ли уже выбрана перестановка выбранных чисел.
e.g один шаг выбирает [1, 10, 3, 18]
и еще один [10, 18, 3, 1]
тогда второй выбор может быть отброшен, потому что это перестановка.
мне нужно сделать эту проверку очень быстро. Прямо сейчас я ставлю все массивы в HashMap и использовать пользовательские хэш-функция: просто суммирует все элементы, так 1+10+3+18=32, а также 10+18+3+1=32. Для equals я использую битовый набор, чтобы быстро проверить, находятся ли элементы в обоих наборах (мне не нужна сортировка при использовании битового набора, но он работает только тогда, когда диапазон чисел известен и не слишком большой).
это работает нормально, но может генерировать много столкновений, поэтому метод equals() вызывается довольно часто. Мне было интересно, есть ли более быстрый способ проверить перестановки?
есть ли хорошие хэш-функции для перестановок?
обновление
Я сделал небольшой тест: создайте все комбинации чисел в диапазоне от 0 до 6 и длину массива от 1 до 9. Существует 3003 возможных перестановок, и хороший хэш должен генерироваться близко к этому множеству различных хэшей (я использую 32-битные числа для хэша):
- 41 различные хэши для простого добавления (так что есть много наездом)
- 8 различных хэшей для значений XOR'ING вместе
- 286 различных хэшей для умножения
- 3003 различных хэшей для (R + 2e) и умножения, как предложил abc (используя 1779033703 для R)
таким образом, хэш abc может быть рассчитан очень быстро и намного лучше, чем все остальные. Спасибо!
PS: Я не хочу сортировать значения, когда мне не нужно, потому что это будет слишком медленно.
7 ответов
один потенциальный кандидат может быть в этом. Исправить нечетное целое число Р. Для каждого элемента e вы хотите хэшировать вычислить коэффициент (R + 2*e). Вычислите произведение всех этих факторов. Наконец разделить изделие на 2, чтобы получить хэш.
Фактор 2 в (R + 2e) гарантирует, что все факторы нечетны, следовательно, избегая что продукт когда-нибудь станет 0. Деление на 2 в конце, потому что продукт всегда будет нечетным, поэтому разделение просто удаляет константу немного.
например. Я выбираю R = 1779033703. Это произвольный выбор, некоторые эксперименты должны показать, является ли данный R хорошим или плохим. Предположим, Ваши ценности [1, 10, 3, 18]. Продукт (вычисляется с использованием 32-разрядных ints) -
(R + 2) * (R + 20) * (R + 6) * (R + 36) = 3376724311
, следовательно, хэш будет
3376724311/2 = 1688362155.
суммирование элементов уже является одним из самых простых вещей, которые вы могли бы сделать. Но я не думаю, что это особенно хорошая хэш-функция w.r.т. псевдослучайность.
Если вы вроде ваши массивы перед их хранением или вычислением хэшей, каждая хорошая хэш-функция будет делать.
Если речь идет о скорости: есть измерение, где узкое место? Если ваша хэш-функция дает вам много коллизий, и вам приходится тратить большую часть времени на сравнение массивов бит за битом хэш-функция, очевидно, не хороша в том, что она должна делать. Сортировка + лучший хэш может быть решением.
Если я правильно понимаю ваш вопрос, вы хотите проверить равенство между наборами, где элементы не упорядочены. Это именно то, что фильтр Bloom сделает для вас. За счет небольшого количества ложных срабатываний (в этом случае вам нужно будет сделать вызов сравнения наборов грубой силы) вы сможете сравнить такие наборы, проверив, равен ли их хэш фильтра Блума.
алгебраическая причина, по которой это справедливо, заключается в том, что операция или коммутативна. Это для других полукровок тоже.
в зависимости от того, есть ли у вас много коллизий (так что тот же хэш, но не перестановка), вы можете предварительно поместить массивы во время их хэширования. В этом случае вы можете сделать более агрессивный вид хэширования, где вы не только добавляете числа, но и добавляете к нему немного bitmagick, чтобы получить совершенно разные хэши.
Это только полезно, если вы получаете множество нежелательных столкновений, потому что хэш, который вы делаете сейчас, слишком беден. Если вы вряд ли получите какие-либо столкновения, метод, который вы используете кажется, штраф в размере
Я бы предложил этот: 1. Проверьте, одинаковы ли длины перестановок (если нет - они не равны)
- сортировать только 1 массив. Вместо сортировки другого массива перебирайте элементы 1-го массива и ищите наличие каждого из них во 2 - м массиве (сравните только тогда, когда элементы во 2-м массиве меньше-не перебирайте весь массив).
Примечание: Если вы можете иметь те же цифры в permutaions (напр. [1,2,2,10]) затем вам нужно будет удалить элементы из 2-го массива, когда он соответствует члену из 1-го.
псевдо-код:
if length(arr1) <> length(arr2) return false;
sort(arr2);
for i=1 to length(arr1) {
elem=arr1[i];
j=1;
while (j<=length(arr2) and elem<arr2[j]) j=j+1;
if elem <> arr2[j] return false;
}
return true;
идея в том, что вместо сортировки другого массива мы можем просто попытаться сопоставить все его элементы в отсортированном массиве.
вы, вероятно, можете значительно уменьшить коллизии, используя продукт, а также сумму терминов.
1*10*3*18=540 и 10*18*3*1=540
таким образом, хэш суммы продукта будет [32,540]
вам все равно нужно что-то делать с столкновениями, когда они происходят, хотя
мне нравится использовать хэш-код по умолчанию string (Java, C# не уверен в других языках), он генерирует довольно уникальные хэш-коды. поэтому, если вы сначала сортируете массив, а затем генерируете уникальную строку, используя некоторый разделитель.
таким образом, вы можете сделать следующее (Java):
int[] arr = selectRandomNumbers();
Arrays.sort(arr);
int hash = (arr[0] + "," + arr[1] + "," + arr[2] + "," + arr[3]).hashCode();
если производительность является проблемой, вы можете изменить предлагаемую неэффективную конкатенацию строк, чтобы использовать StringBuilder или String.формат
String.format("{0},{1},{2},{3}", arr[0],arr[1],arr[2],arr[3]);
строковый хэш-код, конечно, не гарантируйте, что две разные строки имеют разные хэши, но, учитывая это предлагаемое форматирование, столкновения должны быть чрезвычайно редкими