Java哈希函数碰撞计算异常求助:结果超出哈希表大小
嘿,我看你在计算哈希碰撞时遇到了结果异常的问题——数值居然超过了条目总数17021,这确实不合理。作为刚学Java5个月的新手,能写出这样的代码已经很棒了,下面我帮你找出几个关键问题并给出修复建议:
1. 哈希函数实现错误(核心问题)
你要实现的哈希函数公式应该是:
H(x₀x₁...xₖ₋₁) = (x₀ + a*(x₁ + a*(x₂ + ... + a*(xₖ₋₂ + a*xₖ₋₁)...))) mod 17021
其中x₀是字符串首字符,xₖ₋₁是末字符。但你的berechneHashwertIntern方法完全漏掉了首字符x₀的计算!
举个例子,对于字符串"abc"(x₀='a', x₁='b', x₂='c'),你的代码当前计算的是a*(b + a*c),但正确结果应该是'a' + a*'b' + a²*'c'(当a=33时)。这种逻辑错误会导致哈希值完全偏离预期,直接造成碰撞数异常。
修复后的berechneHashwertIntern可以这样写(从首字符开始递推,更直观):
private int berechneHashwertIntern(char[] chars, int a) { int hash = chars[0]; // 先取首字符x₀ for (int i = 1; i < chars.length; i++) { // 每一步取模避免整数溢出,加17021确保结果非负 hash = (hash * a + chars[i] + 17021) % 17021; } return hash; }
2. 混用不同的哈希参数a
在berechneKollisionen方法中,你处理第一个字符串用了a=33,后面的字符串却用了a=40:
// 第一个字符串用a=33 hashwert = this.berechneHashwert(line, 33); // ... // 后续字符串用a=40 hashwert = this.berechneHashwert(line, 40);
这完全不符合测试要求!你需要对所有字符串使用同一个a值(33、37、39、41中的一个),建议把a作为参数传入berechneKollisionen方法,方便批量测试不同的a值。
3. 碰撞检测逻辑复杂且易出错
你用int[] hashwerte初始化每个元素为索引值,再用1000000标记已出现的哈希值,这种方式不仅逻辑绕,还容易出现混淆。更简单可靠的方式是用boolean数组:
boolean[] hasSeen = new boolean[17021]; int kollisionen = 0; // 处理每个字符串时: int h = berechneHashwert(line, a); if (hasSeen[h]) { kollisionen++; } else { hasSeen[h] = true; }
这样逻辑清晰,不会出现不必要的错误。
4. 文件读取循环易遗漏内容
你当前先读取第一个字符串处理,再进入循环读取后续内容,这种方式容易遗漏或重复处理。建议统一用标准的while循环读取:
String line; while ((line = inBuffer.readLine()) != null) { // 处理当前line }
这样能确保所有行都被正确处理。
修复后的完整代码示例
这里给出调整后的完整代码,你可以直接参考:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class ZeichenkettenHashfunktion { public int berechneKollisionen(int a) throws IOException { BufferedReader inBuffer = new BufferedReader(new FileReader("F:\\eclipse\\eclipse-workspace\\Info 2 Aufgabe 19\\words.txt")); boolean[] hasSeen = new boolean[17021]; int kollisionen = 0; String line; while ((line = inBuffer.readLine()) != null) { int hashwert = berechneHashwert(line, a); // 确保哈希值非负,避免索引越界 hashwert = (hashwert + 17021) % 17021; if (hasSeen[hashwert]) { kollisionen++; } else { hasSeen[hashwert] = true; } } inBuffer.close(); return kollisionen; } public int berechneHashwert(String eingabe, int a) { char[] chars = eingabe.toCharArray(); return berechneHashwertIntern(chars, a); } private int berechneHashwertIntern(char[] chars, int a) { int hash = chars[0]; for (int i = 1; i < chars.length; i++) { hash = (hash * a + chars[i] + 17021) % 17021; } return hash; } // 测试用main方法 public static void main(String[] args) throws IOException { ZeichenkettenHashfunktion hashFunc = new ZeichenkettenHashfunktion(); int[] aValues = {33, 37, 39, 41}; for (int a : aValues) { int collisions = hashFunc.berechneKollisionen(a); System.out.printf("使用a=%d时,碰撞数为:%d%n", a, collisions); } } }
额外注意事项
- 确保你的
words.txt文件确实包含17021个有效单词,没有空行或重复行; - Java的int类型计算可能溢出,所以每一步都要取模并加上17021,保证哈希值为非负数,避免数组索引越界。
内容的提问来源于stack exchange,提问作者AshsGlurak

