Подтвердить что ты не робот

Идеальная хеш-функция

Я пытаюсь хэш значения

10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0

Мне нужна функция, которая будет отображать их в массив размером 13, не вызывающий никаких конфликтов.

Я провел несколько часов, размышляя об этом и отправляясь в Google, и не могу понять это. Я не приблизился к жизнеспособному решению.

Как мне найти хэш-функцию такого типа? Я играл с gperf, но я этого не понимаю, и я не мог получить результаты, которые я искал.

4b9b3361

Ответ 1

Найдено один

Я пробовал несколько вещей и нашел одно полу-вручную:

(n ^ 28) % 13

Полу-ручная часть была следующей ruby ​​ script, которую я использовал для проверки функций-кандидатов с рядом параметров:

t = [10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0]
(1..200).each do |i|
  t2 = t.map { |e| (e ^ i) % 13 }
  puts i if t2.uniq.length == t.length
end

Ответ 2

если вы знаете точные ключи, тогда тривиально создать идеальную хэш-функцию -

int hash (int n) {
  switch (n) {
    case 10:   return 0;
    case 100:  return 1;
    case 32:   return 2;
    // ...
    default:   return -1;
  }
}

Ответ 3

На некоторых платформах (например, вложенных) операция modulo стоит дорого, поэтому % 13 лучше избегать. Но AND работа младших разрядов дешева и эквивалентна по модулю мощности-2.

Я попробовал написать простую программу (в Python) для поиска идеального хеша из ваших 11 точек данных, используя простые формы, такие как ((x << a) ^ (x << b)) & 0xF (где & 0xF эквивалентно % 16, давая результат в диапазон 0,15, например). Мне удалось найти следующий хеш без конфликтов, который дает индекс в диапазоне 0..15 (выражается как макрос C):

#define HASH(x)    ((((x) << 2) ^ ((x) >> 2)) & 0xF)

Вот программа Python, которую я использовал:

data = [ 10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0 ]

def shift_right(value, shift_value):
    """Shift right that allows for negative values, which shift left
    (Python shift operator doesn't allow negative shift values)"""
    if shift_value == None:
        return 0
    if shift_value < 0:
        return value << (-shift_value)
    else:
        return value >> shift_value

def find_hash():
    def hashf(val, i, j = None, k = None):
        return (shift_right(val, i) ^ shift_right(val, j) ^ shift_right(val, k)) & 0xF

    for i in xrange(-7, 8):
        for j in xrange(i, 8):
            #for k in xrange(j, 8):
                #j = None
                k = None
                outputs = set()
                for val in data:
                    hash_val = hashf(val, i, j, k)
                    if hash_val >= 13:
                        pass
                        #break
                    if hash_val in outputs:
                        break
                    else:
                        outputs.add(hash_val)
                else:
                    print i, j, k, outputs

if __name__ == '__main__':
    find_hash()

Ответ 4

Просто некоторые квазианалитические штрихи:

В вашем наборе чисел одиннадцать во всех, три нечетные и восемь четные. Рассмотрение простейших форм хэширования -% 13 - даст вам следующие значения хеширования:  10 - 3, 100 - 9,  32 - 6,  45 - 6,  58 - 6, 126 - 9, 3 - 3,  29-3, 200 - 5, 400 - 10, 0 - 0

Что, конечно, непригодно из-за количества столкновений. Требуется нечто более сложное.

Зачем утверждать очевидное? Учитывая, что числа настолько малы, что любой сложный или, скорее, "менее простой" алгоритм, скорее всего, будет медленнее, чем оператор switch или (что я предпочитаю), просто просматривая беззнаковый короткий/длинный вектор размером одиннадцать позиций и используя индекс матча.

Зачем нужен векторный поиск?

  • Вы можете настроить его, поместив наиболее часто встречающиеся значения в начало вектора.
  • Я предполагаю, что целью является включение хэш-индекса в коммутатор с красивой последовательной нумерацией. В этом свете кажется бесполезным сначала использовать переключатель, чтобы найти индекс, а затем подключить его к другому коммутатору. Может быть, вам стоит не использовать хэширование вообще и перейти непосредственно к окончательному коммутатору?
  • Версия хеширования коммутатора не может быть точно настроена и из-за широко различающихся значений заставит компилятор генерировать двоичное дерево поиска, что приведет к большому количеству сравнений и условных/других переходов (особенно дорогостоящих), которые возьмите время (я предположил, что вы перешли на хеширование для своей скорости) и требуют места.
  • Если вы хотите ускорить векторный поиск дополнительно и используете x86-систему, вы можете реализовать векторный поиск на основе команд ассемблера repne scasw (short)/repne scasd (long), который будет намного быстрее. По истечении времени установки нескольких инструкций вы найдете первую запись в одной инструкции, а последнее - в одиннадцать, за которой следует несколько инструкций по очистке. Это означает, что 5-10 лучших инструкций и 15-20 наихудших. Это должно бить хэширование на основе коммутатора во всех, но, возможно, одном или двух случаях.

Ответ 5

У Боб Дженкинса есть программа для этого: http://burtleburtle.net/bob/hash/perfect.html

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

Ответ 6

Я быстро проверил и использовал хэш-функцию SHA256, а затем выполнил модульное деление на 13, когда я попробовал его в Mathematica. Для С++ эта функция должна быть в библиотеке openssl. См. Этот пост.

Если вы делали много хэширования и поиска, модульное разделение - довольно дорогостоящая операция, которую нужно делать повторно. Существует еще один способ сопоставления n-разрядной хеш-функции в индексы i-бит. См. Этот post от Майкла Миценмахера о том, как это сделать с помощью операции с битным сдвигом в C. Hope, которая помогает.

Ответ 7

Попробуйте следующее, которое сопоставляет ваши значения n с уникальными индексами от 0 до 12 (1369% (п + 1))% 13