Переход от линейного измерения к квадратичному (хэш-сопоставления)

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

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

index = hash_function(key) % table_size;

Затем во время поиска, вставки или удаления я просматриваю таблицу, пока не найду свободное ведро, например:

do {
    if(/* CHECK IF IT'S THE ELEMENT WE WANT */) {
        // FOUND ELEMENT

        return;
    } else {
        index = (index + 1) % table_size;
    }
while(/* LOOP UNTIL IT'S NECESSARY */);

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

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

РЕДАКТИРОВАТЬ: Прочитав все, что указано Эли Бендерски ниже, я думаю, что у меня есть общее представление. Вот часть кода по адресу http://eternalconfuzzled.com/tuts/datastructures/jsw_tut_hashtable.aspx < / а>:

15   for ( step = 1; table->table[h] != EMPTY; step++ ) {
16     if ( compare ( key, table->table[h] ) == 0 )
17       return 1;
18 
19     /* Move forward by quadratically, wrap if necessary */
20     h = ( h + ( step * step - step ) / 2 ) % table->size;
21   }

Есть 2 вещи, которых я не понимаю ... Говорят, что квадратичное зондирование обычно выполняется с использованием c(i)=i^2. Однако в приведенном выше коде он делает что-то вроде c(i)=(i^2-i)/2

Я был готов реализовать это в своем коде, но просто сделал бы:

index = (index + (index^index)) % table_size;

...и не:

index = (index + (index^index - index)/2) % table_size;

Во всяком случае, я бы сделал:

index = (index + (index^index)/2) % table_size;

... потому что я видел другие примеры кода, которые ныряли вдвое. Хотя не понимаю почему ...

1) Почему он вычитает шаг?
2) Почему он занижает его на 2?


person rfgamaral    schedule 27.02.2010    source источник
comment
имейте в виду, что квадратичное зондирование эффективно только в том случае, если размер стола простой, а коэффициент нагрузки ‹0,5; см. eternalconfuzzled.com/tuts/datastructures/ для обзора различных стратегий разрешения столкновений.   -  person Christoph    schedule 27.02.2010
comment
@Cristoph: это утверждение не совсем верно. Если размер стола простой, то он гарантированно будет работать правильно, если коэффициент загрузки <0,5; но неверно, что это единственный случай, когда квадратичное зондирование работает. Например, он также может быть эффективен с размером таблицы, равным мощности 2, и произвольным коэффициентом загрузки (см. Мой ответ).   -  person Matthew Slattery    schedule 28.02.2010
comment
@Mathew: есть разница между «работать» и «работать эффективно»; если коэффициент загрузки слишком высок, (вторичная) кластеризация может снова стать проблемой   -  person Christoph    schedule 02.03.2010
comment
@Cristoph: конечно (произвольный коэффициент загрузки, вероятно, был плохим выбором слов с моей стороны; коэффициент загрузки 0,999 не будет хорошей идеей, несмотря на причудливое зондирование). Но 0,75 - это разумный коэффициент загрузки для хэш-таблицы, чтобы он работал достаточно хорошо даже с линейным зондированием (пока хеш-функция хороша), и действительно будет эффективно работать с квадратичным зондированием и размером таблицы со степенью двойки (зондирование посещений все сегменты, такие как линейное зондирование, но с уменьшением первичной кластеризации), поэтому квадратичное зондирование с помощью оператора эффективно только в том случае, если размер таблицы простой, а коэффициент загрузки <0,5 неверен.   -  person Matthew Slattery    schedule 03.03.2010


Ответы (2)


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

здесь есть хороший ресурс. Следующее взято оттуда. Это простейшая форма квадратичного зондирования, когда используется простой многочлен c(i) = i^2:

alt text

В более общем случае формула:

alt text

И вы можете выбрать свои константы.

Однако имейте в виду, что квадратичное зондирование полезно только в определенных случаях. Как говорится в статье в Википедии:

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


РЕДАКТИРОВАТЬ: Как и многие другие вещи в информатике, точные константы и полиномы квадратичного зондирования являются эвристическими. Да, самая простая форма - i^2, но вы можете выбрать любой другой многочлен. Википедия дает пример с h(k,i) = (h(k) + i + i^2)(mod m).

Поэтому сложно ответить на ваш вопрос «почему». Единственное «зачем» здесь - зачем вообще нужно квадратичное зондирование? Возникли проблемы с другими формами проверки и получения кластерной таблицы? Или это просто домашнее задание или самообучение?

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

person Eli Bendersky    schedule 27.02.2010
comment
Извините, но математические формулы мне не помогают. :( И ты не дал мне больше того, что я уже читал об этом. - person rfgamaral; 27.02.2010
comment
@Nazgulled: Я действительно не понимаю, с чем у вас проблемы - и, поскольку у вас нет других ответов, может быть, я не единственный. Я думаю, вам стоит попытаться сформулировать свой вопрос и перефразировать его, чтобы точно объяснить, что вам нужно. - person Eli Bendersky; 27.02.2010
comment
Я смотрю на математические формулы и не понимаю их, а также не знаю, что делать в коде. Мне нужно знать, что делать словами, а не математическими формулами. - person rfgamaral; 27.02.2010
comment
@Nazgulled: Я добавил к своему ответу. Я надеюсь, вы понимаете, что для успешного программирования вы должны иметь понимание формул или, по крайней мере, желание смотреть на них. - person Eli Bendersky; 28.02.2010
comment
Это для самообучающихся людей. У меня будет проект, в котором мне придется использовать хеш-таблицы (возможно), но для этого я буду использовать цепочку, я просто хочу изучить разные способы сделать это, чтобы я мог написать о них в окончательном отчете и спорить почему я выбрал одно против другого. Для линейного зондирования я перестал искать, когда посетил все сегменты хеш-таблицы, но для квадратичного зондирования, когда я должен прекратить искать? - person rfgamaral; 28.02.2010
comment
@Nazgulled: на странице, на которую я указал, есть обсуждение - пока размер таблицы простой, первые образцы M / 2 (M - размер) уникальны. Прочтите об этом там или на любом другом онлайн-ресурсе по алгоритмам. - person Eli Bendersky; 28.02.2010

Существует особенно простой и элегантный способ реализовать квадратичное зондирование, если размер вашей таблицы равен степени двойки:

step = 1;

do {
    if(/* CHECK IF IT'S THE ELEMENT WE WANT */) {
        // FOUND ELEMENT

        return;
    } else {
        index = (index + step) % table_size;
        step++;
    }
} while(/* LOOP UNTIL IT'S NECESSARY */);

Вместо того, чтобы смотреть на смещения 0, 1, 2, 3, 4 ... от исходного индекса, это будет смотреть на смещения 0, 1, 3, 6, 10 ... (i th датчик находится со смещением (i * (i + 1)) / 2, т. е. квадратичным).

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


Вот набросок доказательства:

  1. Учитывая размер таблицы n, мы хотим показать, что мы получим n различных значений (i * (i + 1)) / 2 (mod n) с i = 0 ... n-1.
  2. Мы можем доказать это от противного. Предположим, что существует менее n различных значений: если это так, должно быть по крайней мере два различных целочисленных значения для i в диапазоне [0, n-1], такое что (i * (i + 1)) / 2 (mod n ) то же самое. Назовите их p и q, где p ‹q.
  3. то есть (p * (p + 1)) / 2 = (q * (q + 1)) / 2 (mod n)
  4. => (p 2 + p) / 2 = (q 2 + q) / 2 (mod n)
  5. => p 2 + p = q 2 + q (по модулю 2n)
  6. => q 2 - p 2 + q - p = 0 (mod 2n)
  7. Факторизация => (q - p) (p + q + 1) = 0 (mod 2n)
  8. (q - p) = 0 - тривиальный случай p = q.
  9. (p + q + 1) = 0 (mod 2n) невозможно: наши значения p и q находятся в диапазоне [0, n-1], а q> p, поэтому (p + q + 1) должно быть в диапазон [2, 2н-2].
  10. As we are working modulo 2n, we must also deal with the tricky case where both factors are non-zero, but multiply to give 0 (mod 2n):
    • Observe that the difference between the two factors (q - p) and (p + q + 1) is (2p + 1), which is an odd number - so one of the factors must be even, and the other must be odd.
    • (q - p) (p + q + 1) = 0 (mod 2n) => (q - p) (p + q + 1) делится на 2n. Если n (и, следовательно, 2n) является степенью 2, это требует, чтобы четный множитель был кратен 2n (поскольку все простые множители 2n равны 2, тогда как ни один из простых множителей числа наш нечетный фактор).
    • Но (q - p) имеет максимальное значение n-1, а (p + q + 1) имеет максимальное значение 2n-2 (как показано на шаге 9), поэтому ни одно из них не может быть кратным 2n.
    • Так что и этот случай невозможен.
  11. Следовательно, предположение, что существует менее n различных значений (на шаге 2), должно быть ложным.

(Если размер таблицы не является степенью двойки, это развалится на шаге 10.)

person Matthew Slattery    schedule 28.02.2010