web-dev-qa-db-ja.com

C#の文字列の高速ハッシュ関数

最大30文字の長さの文字列をハッシュします。時間が問題である場合、これを行うための最良のアイデアは何でしょうか。関数は1億回以上呼び出されます。現在、私は次のコードを使用しています、

static UInt64 CalculateHash(string read, bool lowTolerance)
{
    UInt64 hashedValue = 0;
    int i = 0;
    while (i < read.Length)
    {
        hashedValue += read.ElementAt(i) * (UInt64)Math.Pow(31, i);
        if (lowTolerance) i += 2;
        else i++;
    }
    return hashedValue;
}
25
P basak
static UInt64 CalculateHash(string read)
{
    UInt64 hashedValue = 3074457345618258791ul;
    for(int i=0; i<read.Length; i++)
    {
        hashedValue += read[i];
        hashedValue *= 3074457345618258799ul;
    }
    return hashedValue;
}

これはKnuthハッシュです。 Jenkins を使用することもできます。

42
David Schwartz

まず、GetHashCode()の使用を検討してください。

既存の実装の簡単な改善:

static UInt64 CalculateHash(string read, bool lowTolerance)
{
    UInt64 hashedValue = 0;
    int i = 0;
    ulong multiplier = 1;
    while (i < read.Length)
    {
        hashedValue += read[i] * multiplier;
        multiplier *= 37;
        if (lowTolerance) i += 2;
        else i++;
    }
    return hashedValue;
}

高価な浮動小数点計算、およびElementAtのオーバーヘッドを回避します。

ところで(UInt64)Math.Pow(31, i)は、長い文字列ではうまく機能しません。浮動小数点の丸めにより、15を超える文字の乗数は0になります。

6
CodesInChaos

私はPaul Hsiehの実装を試しましたが、衝突はほとんどありません(とにかく私のシナリオでは)

1
skub

実装を高速化するには、(UInt64)Math.Pow(31, i)呼び出しをルックアップに置き換える必要があります。31の最初の30の累乗のテーブルを事前計算し、実行時に使用します。長さの制限は30なので、必要な要素は31だけです。

private static unsigned long[] Pow31 = new unsigned long[31];

static HashCalc() {
    Pow31[0] = 1;
    for (int i = 1 ; i != Pow31.Length ; i++) {
        Pow31[i] = 31*Pow31[i-1];
    }
}

// In your hash function...
hashedValue += read.ElementAt(i) * Pow31[i];
1
dasblinkenlight