Rabin-Karpは本当にローリングハッシュにmod Q操作を適用することに注意する必要がありますか?


8

私はRabin Karpアルゴリズムについて読んでいて、値Qによって制限されたローリングハッシュ値を維持することの大事なことは何だろうとずっと思い続けました。

一般的なコンピューターでの整数表現は2の補数であるため、実際には、ローリングハッシュに対するすべての操作を2 ^ 31で制限するのとまったく同じであると考えました。つまり、気にしないでください。さらに、バインドまたはハッシュする値が小さいほど、衝突が増えるため、Qが大きいほどパフォーマンスが向上します。

簡単な(Java)実装をコーディングしてみました。

public static int rabinKarp(String text, String pattern) {
    if (text.length() < pattern.length()) {
        return -1;
    } else {
        int patternHash = 0;
        int textHash = 0;
        int pow = 1;

        // preprocessing the pattern and the first characters of the text string
        for (int i = pattern.length()-1; i >= 0; --i) {
            patternHash += pattern.charAt(i) * pow;
            textHash += text.charAt(i) * pow;
            pow *= 10;
        }
        pow /= 10;

        // actual search
        if (patternHash == textHash && areEqual(text, 0, pattern)) {
            return 0;
        } else {
            for (int i = 1; i < text.length()-pattern.length()+1; ++i) {
                textHash -= text.charAt(i-1)*pow;
                textHash *= 10;
                textHash += text.charAt(i+pattern.length()-1);
                if (textHash == patternHash && areEqual(text, i, pattern)) {
                    return i;
                }
            }
            return -1;
        }
    }
}

いくつかの予備テストから、私の仮説は経験的に正確であるように見えますが、それがどこにも書かれていないので、疑問に思っています。

何か不足していますか?


2
大事なことは、おそらくすべての計算をモジュロにしたいということです。 Q、おそらくMAXINTに近い大きな素数。それはおそらくより良いハッシュ関数をもたらすはずです。ただし、参照アルゴリズムが何なのかわからないので、知るのは難しいです。Rabin–Karpには多くのバリアントがあります。また、Javaコードを読まないようにしています。確かに、代わりに疑似コードでアルゴリズムを要約できます。
Yuval Filmus

回答:


10

はい、実際には、計算をオーバーフローさせるだけで問題ありません。あなたは効果的にモジュロを働いています232。また、(高価な)モジュロ計算を必要としないという利点もあります。ただし、理論的なパフォーマンス保証の一部が欠けています。ベースの選択には非常に注意する必要があります(この場合:10)モジュラスに関して。

特に、あなたの選択 10とても貧しいです。ご了承ください1032=232532、 そう 1032 モッド 232=0。つまり、最後の32 文字列の文字はハッシュで考慮に入れられるため、アルゴリズムのパフォーマンスが非常に低い入力を構築できます。

干し草の山を メートル 1の、すなわち 1111111 そして針はからなるひも 1の、1 0、 その後 32 1の。文字列が32 1のすべての位置は偽のヒットとなり、アルゴリズムはループオーバーする必要があります 1ゼロに遭遇する前の、つまり、 Ωメートル 実行時間。

私は入力であなたのアルゴリズムをテストしました =3000メートル=2=9106。それは取った18 終了した入力で実行する秒数 32 1ですが、 200メートルs で終わる文字列の場合 31 1の。

問題はそれです 10係数に対して比較的素数ではありません。たとえば、9 ベースがプログラムのパフォーマンスを大幅に向上させるため、 200メートルs の場合 32 1の。もちろん、基数が自動的に相対的に素数になるため、素数係数を使用すると、この問題が部分的に解決されます。ただし、これが素数係数を優先する唯一の理由ではありません。

さて、モジュラスが そしてベース b比較的素晴らしく、望ましくないことがまだ発生する可能性があります。たとえば、k そのため bk=1 モッド 。それは望ましくないk ハッシュ関数はすべてを区別できないため、 番目 すべてのキャラクター +k番目キャラクター。数学的な用語では、次の順序が必要ですb モッド できるだけ大きくします。

の順 b モッド 常に最大でもオイラー・ファイ関数 φ。素数のためにpφp=p1 プライム以外の場合 小さくなります。だから取る 素数になることは、 bk「役に立つ」こと。理想的には、b 原始根を法として 、それを作る bk=1 モッド  の値を保持しません 0<k<φ

常にパフォーマンスが低いインスタンスを構築でき、敵からの「攻撃」から保護するために、ベースとモジュラスをランダムな値にする必要があることに注意してください。


すばらしい答えです。追加したいのは、Q=2kThue-Morse文字列があります:任意のp、多項式ハッシュでは区別できない短い部分文字列があります。たとえば、Q=264、倍数で終わる部分文字列 4096=212 に関係なく、ハッシュはすべてゼロになります pここでは人気のある説明の1つです。
Gassa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.