最近、ハッシュテーブルの衝突に対処するためのさまざまな方法について学びました。また、リンクリストを使用した個別のチェーニングは常に時間効率が高く、スペース効率のために、後で使用しない線形プローブに事前定義されたメモリを割り当てます。個別のチェーニングには、メモリを動的に使用するため、リンクリストを使用した個別のチェーニングです。線形プローブよりも効率的ではありませんか?そうであれば、なぜ線形プローブを使用するのですか?
連鎖ハッシュの方が線形探索よりも高速であることに気付いて驚いています-実際には、線形探索は通常、連鎖よりもはるかに高速です。これは主に 参照の局所性 によるものです。線形プローブで実行されるアクセスは、連鎖ハッシュで実行されるアクセスよりもメモリ内で近くなる傾向があるためです。
線形プローブには他にもメリットがあります。たとえば、線形プローブハッシュテーブルへの挿入では、新しい割り当ては必要ありません(テーブルを再ハッシュしない限り)。そのため、メモリが不足しているネットワークルーターなどのアプリケーションでは、テーブルが設定されたら、それを知っておくと便利です。 malloc
が失敗するリスクなしに、要素を配置できます。
線形プローブの1つの弱点は、ハッシュ関数の選択が不適切な場合、 primary clustering が原因でテーブルのパフォーマンスが大幅に低下する可能性があることです。連鎖ハッシュは依然として悪いハッシュ関数の影響を受ける可能性がありますが、ランタイムに悪影響を与えない、近くのハッシュコードを持つ要素の影響を受けにくくなっています。理論的には、ハッシュ関数が 5-independent の場合、または キーに十分なエントロピーがある場合)O(1)のルックアップのみが線形プローブによって得られます =。 Robin Hood hashing 手法または hopscotch hashing を使用する場合と同様に、どちらもVanilla線形プローブよりもワーストケースが大幅に優れているため、これに対処する方法はたくさんあります。
線形プローブのもう1つの弱点は、負荷係数が1に近づくとパフォーマンスが大幅に低下することです。これは、定期的に再ハッシュするか、上記のRobin Hoodハッシュ技術を使用して対処できます。
お役に立てれば!
ハッシュテーブルがいっぱいに近い場合、線形プローブは実際にはメモリ効率が高くなります。
歴史的には、メモリはごくわずかしかなかったため、すべてのバイトが重要でした(メモリが非常に限られている場合もあります)。
なぜより少ないメモリを使用するのですか?
テーブルがどのように見えるかを検討します( Wikipedia のように個別のチェーンのバリエーション-他にもバリエーションがありますが、通常はより多くのメモリを使用します)。
_Linear Separate chaining #1 Separate chaining #2
probing List head in table Pointer in table
|------| |------|---| |---| |------|---|
|Object| |Object|Ptr| |Ptr| -> |Object|Ptr|
|------| |------|---| |---| |------|---|
|Object| |Object|Ptr| |Ptr| -> |Object|Ptr|
|------| |------|---| |---| |------|---|
| NULL | | NULL |Ptr| |Ptr|
|------| |------|---| |---|
. . .
. . .
. . .
_
(Ptr
は「ポインター」を表します-何かを指していないポインターはNULL
と見なすことができます)
個別のチェーニング#1は、テーブルのすべての要素がポインターのサイズによって大きくなるため、(常に)線形プローブよりも多くのメモリを使用します。
個別のチェーニング#2は、テーブルに多くない場合に利点があるかもしれませんが、満杯になると、すべての要素に対しておよそ2つのポインタが浮かぶようになります。
templatetypedef は、通常、線形プローブの方が通常は高速です(めったに間違っていることはありません)が正しいですが、通常、個別のチェーンの方が高速であることが教えられており、主要なAPI( Java実装など)で見られます など)、おそらくこれが原因で、線形プローブがはるかに遅くなるケースを回避するために(いくつかの適切に選択された値を使用すると、O(n)
独立したチェーンでの線形プローブによるパフォーマンスはO(1)
のままでした)、またはおそらく他の理由によるものです。