Rabin–Karpでハッシュを計算するために使用される基底が常に素数であるのはなぜですか?


8

Rabin–Karp文字列照合アルゴリズムには、迅速に計算できるハッシュ関数が必要です。一般的な選択は

h(x0xn)=i=0nbixi,
どこ bあるプライム(すべての計算は、モジュールです2w、 どこ wマシンワードの幅です)。なぜそれが重要なのかb プライムになるには?

あなたの質問を誤解した場合はお知らせください。
Yuval Filmus 14年

@YuvalFilmusいいえ、あなたはそれを正しい方法で取りました....
sdream 2014年

回答:


9

まず簡単にまとめます。パターンを探していますP[1m] 文字列で S[1n]。Rabin-Karpアルゴリズムは、ハッシュ関数を定義することでこれを行いますh。計算しますh(P) (つまり、パターンのハッシュ)、それを h(S[1m])h(S[2m+1])等々。一致するハッシュが見つかった場合、それは一致する可能性のある部分文字列です。

アルゴリズムの効率は、計算する能力に依存します h(S[r+1s+1]) から効率的に h(S[rs])。これは「ローリングハッシュ」と呼ばれます。注ことを任意の効率的なローリングハッシュ関数が行います、そしてそれはまだラビン-カープです。あなたが尋ねている質問は、あなたが使用するハッシュ関数の1つの特定の選択です:

h(S[rs])=i=rsS[i]psimodq

どこ p 文字セットのサイズとほぼ同じ桁の素数であり、 qハッシュ関数の範囲のカーディナリティを定義するもう1つの素数です。通常、マシンワードを文字セットサイズで割ったものと同じ桁数です。私がそれを正しく読んでいるなら、あなたは理由を尋ねていますq プライムでなければなりません。

実際、これはより一般的な質問です。ハッシュに関する古い(そして現在の)多くの文献では、アドバイスは、ハッシュ関数は素数を法として取られるべきであるということです(例えば、ハッシュテーブルは素数サイズを持つべきです)。

ハッシュ関数が可能な限り役立つためには、その範囲が比較的均一である必要があります(ドメインがそうでない場合でも)。自然言語のテキスト(たとえば)は頻度分布が均一ではありませんが、ハッシュ値はそうでなければなりません。

もし q が素数である場合、他の多くの数値はそれに対して比較的素数であり、特に合計(特に pもプライムです!)これにより、ハッシュ関数が比較的弱い場合でも、ハッシュ値の頻度分布がより均一になります。

これを行うことを理解することが重要です。ハッシュ関数が弱い、です。ハッシュ関数の方が強力な場合、素数で割った余りを取る必要はありません。たとえば、余りを2の累乗で割ると、より安価なビットマスク操作になります。ただし、Rabin-Karpアルゴリズムのすべての入力文字に対して実行できるほど安価な強力なローリングハッシュ関数を設計することは困難です。

この「素数の残り」の手法は、以前は多くのハッシュアプ​​リケーションで一般的でしたが、このアドバイスは最新のハードウェアではお勧めできません。最終的な整数除算命令は常に高価でしたが、整数乗算などのハッシュ関数を計算するために使用した演算もそうだったので、それはかつて意味のあることでした。最近のCPUでは、整数の乗算よりも整数の除算を行う方がはるかにコストがかかります。

最新のキャリーセーブ加算器乗算器は完全にパイプライン化されているため、このような命令を複数同時に実行できます。最新の除算器はSPHまたはGoldschmidtアルゴリズムを使用します。これらのアルゴリズムはマルチサイクルであり、パイプライン処理は不可能です。Goldschmidt除算器も乗算ユニットを拘束するため、パフォーマンスヒットがさらに大きくなります。

この除算命令がボトルネックになっているプログラムがありましたが、標準ライブラリの中に隠されているのが面倒です。

最近のCPUでは、完全にパイプライン化可能な演算(乗算やテーブルルックアップなど)から構築されたより高度なハッシュ関数を使用し、2の累乗であるハッシュテーブルを使用する価値があるため、モジュロ演算はビットマスクです。その除算演算を回避するために何でもしてください。

Rabin-Karpだけではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.