3


0

線形探査から二次探査への移行(ハッシュ衝突)

ハッシュテーブルの現在の実装では、線形プローブを使用していますが、次に、2次プローブに移動します(後でチェーンになり、場合によってはダブルハッシュにもなります)。 いくつかの記事、チュートリアル、ウィキペディアなどを読みました。 しかし、私はまだ何をすべきかを正確に知りません。

基本的に、リニアプローブには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 */);

Quadratic Probingに関しては、「インデックス」ステップサイズの計算方法を変更する必要があると思いますが、それがどうすればよいか理解できません。 さまざまなコードを見てきましたが、それらはすべて多少異なります。

また、ハッシュ関数がそれに対応するように変更されている(すべてではない)Quadratic Probingの実装を見てきました。 その変更は本当に必要ですか、それともハッシュ関数を変更せずに二次探査を使用できますか?

編集: Eli Benderskyが指摘したすべてを読んだ後、私は一般的なアイデアを得たと思います。 http://eternallyconfuzzled.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;

…​and not:

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

どちらかといえば、私はやるだろう:

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

…​cause I’ve seen other code examples diving by two. Although I don’t 理由を理解する…​

  • 1)*なぜそれがステップを引いているのですか? + * 2)*なぜ2倍になるのですか?

2 Answer


11


テーブルサイズが2の累乗である場合、2次プローブを実装する特にシンプルでエレガントな方法があります。

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にあります。 二次関数です)。

これにより、ハッシュテーブル内のすべての位置にヒットすることが保証されます(したがって、空のバケットがあればそれを見つけることが保証されます)_provided_テーブルサイズは2の累乗です。

'' '' '

証拠のスケッチは次のとおりです。

  1. テーブルサイズがnの場合、n個の異なる値が得られることを示したい (i *(i + 1))/ 2(mod n)の値はi = 0 …​ n-1.

  2. これは矛盾によって証明できます。 nより少ないと仮定します 異なる値:その場合、(i *(i + 1))/ 2(mod n)が同じになるように、範囲[0、n-1]のiに少なくとも2つの異なる整数値が必要です。 これらのpとqを呼び出します(p <q)。

  3. i.e. (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(mod 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、2n-2]の範囲内でなければなりません。

  10. 2nを法として作業しているため、扱いにくいケースにも対処する必要があります ここで、両方の因子はゼロではありませんが、乗算して0(mod 2n)を与えます:

    • 2つの因子(q-p)と(p + q + 1)は(2p + 1)であり、これは奇数です。したがって、因子の一方は偶数でなければならず、他方は奇数でなければなりません。

    • (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)はfalseでなければなりません。

(テーブルサイズが2の累乗ではない場合、これはステップ10でバラバラになります。)


4


二次探査のためにハッシュ関数を変更する必要はありません。 二次探査の最も単純な形式は、実際には、線形1、2、3の代わりに、結果の正方形を計算された位置に追加するだけです。

良いリソースhttp://www.brpreiss.com/books/opus5/html/page241.html[here]があります。 そこから次のようになります。 これは、単純な多項式 `c(i)= i ^ 2`が使用される場合の2次プローブの最も単純な形式です。

画像:https://i.imgur.com/vMiEBFt.gif [alt text]

より一般的な場合、式は次のとおりです。

画像:https://i.imgur.com/03DbmTj.gif [alt text]

そして、定数を選択できます。

ただし、2次探査は特定の場合にのみ有用であることを覚えておいてください。 Wikipedia entryのように:

_ 2次プローブは、参照の局所性を保持するため、優れたメモリキャッシュを提供します。ただし、線形プローブは局所性が高いため、キャッシュのパフォーマンスが向上します。 二次探査は、免疫的ではありませんが、線形探査で発生する可能性のあるクラスタリングの問題を回避します。 _

'' '' '

*編集:*コンピューターサイエンスの多くのものと同様に、2次探査の正確な定数と多項式はヒューリスティックです。 はい、最も単純な形式は「i ^ 2」ですが、他の多項式を選択することもできます。 ウィキペディアの例では、 `h(k、i)=(h(k)+ i + i ^ 2)(mod m)`を使用しています。

したがって、「なぜ」質問に答えることは困難です。 ここでの唯一の「理由」は、なぜ二次探査が必要なのかということです。 他の形式のプローブやクラスター化されたテーブルの取得に問題がありますか? それとも、宿題ですか、それとも自己学習ですか?

ハッシュテーブルでの最も一般的な衝突解決手法は、チェーンプローブまたはリニアプローブであることに注意してください。 二次探査は、特別な場合に使用できるヒューリスティックなオプションです。非常にうまくやっていることがわからない限り、使用することはお勧めしません。