C# Rabin-Karp字符串搜索算法中使用的滚动哈希函数是否有任何有效的实现?
我希望使用一个滚动散列函数,这样我就可以对一个非常大的字符串的n-gram进行散列 例如: “stackoverflow”,分为5克: “stack”、“tacko”、“ackov”、“ckove”, “kover”、“overf”、“verfl”、“erflo”、“rflow” 这对于滚动散列函数非常理想,因为在我计算第一个n-gram散列之后,下面的计算相对便宜,因为我只需删除第一个散列的第一个字母,然后添加第二个散列的新的最后一个字母 我知道,一般情况下,此哈希函数的生成方式如下: H=c1ak− 1+c2ak− 2+c3ak− 3 + ... + cka0,其中a为常数,c1,…,ck为输入字符 如果您在上遵循此链接,它说明“a”通常是某个大素数 我希望我的散列存储在32位整数中,那么素数“a”应该有多大,这样我就不会溢出整数 是否存在我可以使用的哈希函数的现有实现C# Rabin-Karp字符串搜索算法中使用的滚动哈希函数是否有任何有效的实现?,c#,java,algorithm,hash,rabin-karp,C#,Java,Algorithm,Hash,Rabin Karp,我希望使用一个滚动散列函数,这样我就可以对一个非常大的字符串的n-gram进行散列 例如: “stackoverflow”,分为5克: “stack”、“tacko”、“ackov”、“ckove”, “kover”、“overf”、“verfl”、“erflo”、“rflow” 这对于滚动散列函数非常理想,因为在我计算第一个n-gram散列之后,下面的计算相对便宜,因为我只需删除第一个散列的第一个字母,然后添加第二个散列的新的最后一个字母 我知道,一般情况下,此哈希函数的生成方式如下: H=c
以下是我创建的一个实现:
public class hash2
{
public int prime = 101;
public int hash(String text)
{
int hash = 0;
for(int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
hash += c * (int) (Math.pow(prime, text.length() - 1 - i));
}
return hash;
}
public int rollHash(int previousHash, String previousText, String currentText)
{
char firstChar = previousText.charAt(0);
char lastChar = currentText.charAt(currentText.length() - 1);
int firstCharHash = firstChar * (int) (Math.pow(prime, previousText.length() - 1));
int hash = (previousHash - firstCharHash) * prime + lastChar;
return hash;
}
public static void main(String[] args)
{
hash2 hashify = new hash2();
int firstHash = hashify.hash("mydog");
System.out.println(firstHash);
System.out.println(hashify.hash("ydogr"));
System.out.println(hashify.rollHash(firstHash, "mydog", "ydogr"));
}
}
公共类hash2
{
公共整数素数=101;
公共整数散列(字符串文本)
{
int hash=0;
对于(int i=0;i
我用101作为我的主要目标。我的哈希值是否会溢出有关系吗?我认为这是可取的,但我不确定
这似乎是正确的方法吗?据我所知,这是一种功能最小化:
2^31 - sum (maxchar) * A^kx
其中
maxchar=62
(对于A-Za-z0-9
)。我刚刚用Excel(OO Calc,准确地说):)计算了它,它找到的最大值a是质数的76
,或73
。我记得一个稍微不同的实现,它似乎来自sedgewick的一本算法书(它还包含示例代码-尝试查找)。下面是调整为32位整数的摘要:
您可以使用模运算来防止每次运算后整数溢出
初始设置:
- c=文本(“堆栈溢出”)
- M=n克的长度
- d=字母表的大小(256)
- q=一个大素数,这样(d+1)*q不会溢出(8355967可能是一个不错的选择)
- dM=dM-1模q
h = 0
for i from 1 to M:
h = (h*d + c[i]) mod q
对于以下每n克:
for i from 1 to lenght(c)-M:
// first subtract the oldest character
h = (h + d*q - c[i]*dM) mod q
// then add the next character
h = (h*d + c[i+M]) mod q
在减去最早的字符之前必须添加d*q的原因是,由于前面的模运算导致的值较小,因此可能会遇到负值
错误也包括在内,但我认为你应该明白这一点。试着找一本sedgewick的算法书,以获得详细信息、更少的错误和更好的描述。:) 不确定您的目标是什么,但如果您试图提高性能,那么使用math.pow将比计算滚动哈希值节省更多的成本
我建议您从保持简单高效开始,您很可能会发现它足够快。为什么此应用程序的素数与“正常”字符串哈希代码生成有任何不同?该算法非常简单,很容易从伪代码实现。你试过自己编码吗?你所说的包含错误是什么意思?如果我这样做,我会遇到“负值”吗?如何防止它?@Myth17:我的意思是你应该谨慎使用我的(伪)代码,因为它可能包含错误/i没有对它进行广泛测试。Rabin-Karp字符串serach算法中使用的滚动哈希应该允许计算下一个哈希值:s[i+1..i+m]=s[i..i+m-1]-s[i]+s[i+m]。您提供的算法不能用于此目的。计算功率的最快方法?这取决于具体情况。简单乘法通常更快。