1. ハッシュ衝突とは何ですか?
ハッシュテーブルにデータを挿入すると、異なるキー値によって生成されたh(キー)は等しくなり、この時に競合が発生します。
2. ハッシュの競合を解決するにはどうすればよいですか?
一般的に使用される方法には、オープン アドレス指定方法、ジッパー方法、再ハッシュ方法、およびパブリック オーバーフロー領域の確立などがあります。
1. オープンアドレッシング方式
いわゆるオープン アドレッシング方法では、競合が発生した場合に次の空のハッシュ アドレスを見つけます。ハッシュ テーブルが十分に大きい限り、空のハッシュ アドレスは常に見つけることができ、レコードは式 fi( key)
= (f(key)+di) MOD m (di=1,2,3,……,m-1)
詳細な説明: 競合が発生した場合、何らかの検出テクノロジーを使用してハッシュ テーブル内に検出シーケンスを形成します (シーケンスを生成するためのさまざまなルールに従って、線形検出方法、擬似ランダム検出方法、二次検出方法、ダブル ハッシュが使用できます)方法など)。指定されたキーワードが見つかるか、公開アドレスが見つかるまで (つまり、アドレス セルが空になるまで)、このシーケンスに沿ってセルごとに検索します。
たとえば (例を借用します)、キーワード セットは {12,67,56,16,25,37,22,29,15,47,48,34} で、テーブルの長さは 12 です。ハッシュ関数 f(key) = key mod l2 を使用します。
最初の S 数値 {12, 67, 56, 16, 25} を計算するとき、それらはすべて競合のないハッシュ アドレスであり、直接保存されます。
key = 37 を計算すると、f(37) = 1 であることがわかり、25 の位置と矛盾します。
したがって、上記の式 f(37) = (f(37)+1) mod 12 = 2 を適用します。したがって、37 はインデックス 2 の場所に保存されます。
2. ジッパー方式
ジッパー メソッドは、ハッシュの競合を解決する効果的な方法です (たとえば、PHP と Java は、このメソッドを使用してハッシュの競合を解決します)。一部のハッシュ アドレスは、複数のキーワード値で共有できるため、各ハッシュをターゲットにすることができます。目的のアドレスのリスト。
原則: 各ハッシュ テーブル ノードにはネクスト ポインタがあります。複数のハッシュ テーブル ノードは、ネクスト ポインタを使用して一方向リンク リストを形成できます。同じインデックスに割り当てられた複数のノードは、この一方向リンク リストを使用して接続できます。
たとえば(例をお借りします):キーと値のペア k2, v2 とキーと値のペア k1, v1 の計算されたインデックス値は両方とも 2 です。 このとき競合が発生しますが、k2 と v1 が一致するノードは特定された k1 は次のポインタを介して接続できるため、ハッシュ競合の問題が解決されます。
3. リハッシュ法
リハッシュ法は比較的理解しやすいです。リハッシュ法はダブルハッシュ法とも呼ばれます。複数の異なるハッシュ関数があります。競合が発生した場合は、2 番目の関数を使用できます。競合が発生した場合は、引き続き 3 番目の関数を使用します。関数、ハッシュ方式は集計が行われにくいですが、計算時間が大幅に増加します。
4. 公共のオーバーフローエリアを確立する
原則: ハッシュ テーブルを基本テーブルとオーバーフロー テーブルの 2 つの部分に分割し、基本テーブルと競合する要素はすべてオーバーフロー テーブルに埋められます。
3. ハッシュ衝突を利用した攻撃
PHP や Java などの言語では、ハッシュの競合を解決するためにジッパー メソッドが使用されると上で述べました。
攻撃原理は、悪意のあるデータを作成してハッシュ テーブルをリンク リストに縮退させ、データが挿入されるたびにリンク リストを走査し、大量のサーバー リソースを消費することで攻撃の目的を達成します。
例: PHP 配列はハッシュ テーブルを使用して実装されます。例を使用して、悪意を持って構築されたデータと通常のデータの違いを見てみましょう。
<?php
$size = pow(2, 16);
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) {
$array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 个恶意的元素需要 ', $endTime - $startTime, ' 秒', "\n";
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = $size - 1; $key <= $maxKey; ++$key) {
$array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 个普通元素需要 ', $endTime - $startTime, ' 秒', "\n";
上記の例では、悪意を持ってデータが構築され、データが書き込まれるたびにハッシュの競合が発生し、データが格納されるたびにリンク リスト全体が走査されます。私のマシンでの実行結果は次のとおりです。
(これは、私のマシン構成が上位であり、php7 でハッシュ テーブルの構造とアルゴリズムが最適化されているためです。php5.6 を使用すると、さらに時間がかかります)
插入 65536 个恶意的元素需要 7.9304070472717 秒
插入 65536 个普通元素需要 0.0048370361328125 秒
Niao Ge のブログアドレスを参照してください: PHP 配列のハッシュ競合例 - 風雪の片隅