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

扫雷程序地雷随机分布异常:测试9失败与性能优化求助

Java扫雷程序:随机分布不均与超时问题修复方案

一、解决地雷分布不均匀问题(测试9失败)

你当前的随机地雷生成逻辑大概率是重复调用Math.random()生成坐标再去重的方式——这种方法会因坐标碰撞导致部分单元格被选中的概率偏低,甚至完全无法被选中,直接触发G-statistic测试失败。

最优的均匀分布生成方式是Fisher-Yates洗牌算法:将所有棋盘单元格的索引存入数组,打乱顺序后取前N个作为地雷位置,确保每个单元格被选中的概率完全相等。

实现代码

// 假设棋盘为rows行cols列,需要放置mineCount个地雷
int[] cellIndices = new int[rows * cols];
// 初始化所有单元格索引(0到rows*cols-1)
for (int i = 0; i < cellIndices.length; i++) {
    cellIndices[i] = i;
}

// Fisher-Yates洗牌:原地打乱数组,保证均匀分布
for (int i = cellIndices.length - 1; i > 0; i--) {
    // 生成0到i的随机索引(含i)
    int randomIdx = (int) (Math.random() * (i + 1));
    // 交换当前位置与随机位置的元素
    int temp = cellIndices[i];
    cellIndices[i] = cellIndices[randomIdx];
    cellIndices[randomIdx] = temp;
}

// 标记前mineCount个索引对应的单元格为地雷
for (int i = 0; i < mineCount; i++) {
    int idx = cellIndices[i];
    int row = idx / cols;
    int col = idx % cols;
    board[row][col] = '*'; // 用*标记地雷
}

这种方式完全避免了重复随机的碰撞问题,每个单元格被选为地雷的概率严格等于mineCount/(rows*cols),可以轻松通过分布均匀性测试。

二、解决超时问题(测试10未完成)

超时的核心原因是周边地雷计数逻辑效率低下——如果是事后遍历每个单元格,再逐个检查8个方向统计地雷数,时间复杂度会达到O(mn*8),大棋盘下必然超时。

优化思路是埋地雷时同步更新周边计数:在标记地雷的同时,直接给周围8个方向的非地雷单元格的计数加1,一次遍历完成所有计数工作,时间复杂度降至O(mn + mineCount*8)。

实现代码

首先初始化棋盘所有非地雷单元格为'0':

// 初始化棋盘
char[][] board = new char[rows][cols];
for (int i = 0; i < rows; i++) {
    Arrays.fill(board[i], '0');
}

然后在埋地雷的循环中同步更新周边计数(配合上面的洗牌代码):

// 定义8个方向的偏移量(无需扩展网格,直接做边界检查)
int[][] directions = {{-1, -1}, {-1, 0}, {-1, 1},
                      {0, -1},          {0, 1},
                      {1, -1},  {1, 0}, {1, 1}};

for (int i = 0; i < mineCount; i++) {
    int idx = cellIndices[i];
    int row = idx / cols;
    int col = idx % cols;
    board[row][col] = '*'; // 标记地雷

    // 遍历8个方向,更新周边单元格的地雷计数
    for (int[] dir : directions) {
        int newRow = row + dir[0];
        int newCol = col + dir[1];
        // 边界检查:确保坐标在棋盘范围内,且不是地雷
        if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && board[newRow][newCol] != '*') {
            // 字符转整数加1,再转回字符
            board[newRow][newCol] = (char) (board[newRow][newCol] - '0' + 1 + '0');
        }
    }
}

这种方式省去了事后二次遍历统计的开销,大幅提升大棋盘下的运行效率,解决超时问题。

额外优化建议

如果棋盘规模极大,使用数组存储单元格索引比ArrayList更高效(避免自动扩容和装箱拆箱开销),上面的代码已经采用数组实现。另外,确保Math.random()的使用正确:(int)(Math.random() * range)可以生成0到range-1的均匀随机整数,洗牌时的randomIdx计算逻辑是正确的。

内容的提问来源于stack exchange,提问作者Brandon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 12:09:50