C#中字符串的快速散列函数

本文关键字:散列函数 字符串 | 更新日期: 2023-09-27 18:26:36

我想散列一个长度不超过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;
}

C#中字符串的快速散列函数

static UInt64 CalculateHash(string read)
{
    UInt64 hashedValue = 3074457345618258791ul;
    for(int i=0; i<read.Length; i++)
    {
        hashedValue += read[i];
        hashedValue *= 3074457345618258799ul;
    }
    return hashedValue;
}

这是Knuth散列。你也可以使用詹金斯。

首先,考虑使用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。

为了加快实现速度,(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];