如何在4×4二维数组生成无重复随机数?现有C代码仍输出重复求排查
代码错误分析及修正方案
核心错误点
1. 重复检查范围严重缺失
在检查已填入元素是否重复时,代码对**当前行之前的行(k < i)**仅检查到l <= j的列,完全漏掉了这些行中列号大于j的元素。比如填充第1行第0列(i=1,j=0)时,第0行的第1、2、3列元素根本没被检查,导致生成的随机数如果和这些元素重复,代码无法识别,最终出现重复值。
正确的检查逻辑应该是:
- 对k < i的行:遍历该行所有4列(l从0到3)
- 对k == i的行:只遍历到当前列的前一列(l从0到j-1),避免检查刚赋值的当前元素
2. 重复处理逻辑无效
当发现重复时,代码仅执行j--后break,但外层j循环会自动执行j++,直接抵消j--的效果,回到原位置。更关键的是,这种处理没有重新生成随机数并再次校验,即便重新进入循环生成新数,也会因为检查范围的缺失依然可能出现重复。
另外,代码先赋值再检查的逻辑不合理,应该先确认随机数不重复,再赋值给数组元素。
3. 冗余的当前元素判断
代码中if(k != i && l != j)的判断完全多余——只要调整检查范围,让当前行只遍历到j-1列,就不会检查到刚赋值的当前元素,反而增加了逻辑复杂度。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int A[4][4]; int i, j, k, l; int num; int is_duplicate; srand(time(NULL)); for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { do { is_duplicate = 0; num = rand() % 16 + 1; // 检查所有已填满的行 for(k = 0; k < i; k++) { for(l = 0; l < 4; l++) { if(A[k][l] == num) { is_duplicate = 1; break; } } if(is_duplicate) break; } // 检查当前行已填入的前几列 if(!is_duplicate) { for(l = 0; l < j; l++) { if(A[i][l] == num) { is_duplicate = 1; break; } } } } while(is_duplicate); // 重复生成直到得到不重复的数 A[i][j] = num; } } // 输出数组 for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { printf("%3d", A[i][j]); } printf("\n"); } return 0; }
更高效的实现思路
上面的方法是生成随机数后校验重复,当数组接近填满时,重复概率会升高,效率较低。更优的方式是先创建1-16的有序数组,用Fisher-Yates洗牌算法打乱顺序,再填入二维数组:
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int arr[16]; int A[4][4]; int i, j, temp, random_idx; // 初始化有序数组 for(i = 0; i < 16; i++) { arr[i] = i + 1; } srand(time(NULL)); // Fisher-Yates洗牌:从后往前交换随机位置的元素 for(i = 15; i > 0; i--) { random_idx = rand() % (i + 1); temp = arr[i]; arr[i] = arr[random_idx]; arr[random_idx] = temp; } // 转换为4×4二维数组 for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { A[i][j] = arr[i * 4 + j]; } } // 输出结果 for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { printf("%3d", A[i][j]); } printf("\n"); } return 0; }
这种方法确保所有数字不重复,且时间复杂度更低,适合元素数量较多的场景。
内容的提问来源于stack exchange,提问作者mouin
相关产品推荐
相关产品推荐

