You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:37:50