C语言如何从存储文件内容的数组中等概率抽取不重复行
C语言实现从plates.txt读取行后无重复等概率随机选取
原有代码问题修正
你当前的代码存在核心逻辑错误:char line[100]是栈上的共享缓冲区,所有string[i] = line赋值都让指针指向同一块内存,最终数组所有元素都会指向最后一行的内容,无法实现多行存储,需要为每一行单独分配内存存储内容。
实现思路
要满足所有行被选中概率相等、每行仅被选中一次的要求,我们使用Fisher-Yates洗牌算法实现,该算法时间复杂度为O(n),可以保证元素打乱后每个位置的取值概率完全相等,按打乱后的顺序取行即可满足需求,具体步骤:
- 正确读取文件所有行,每行单独分配内存存入指针数组
- 初始化随机数种子
- 使用Fisher-Yates算法打乱指针数组顺序
- 按打乱后的顺序依次取行输出,天然无重复且概率均等
完整可运行代码
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> int main(void) { int line_count = 0; char* string[100]; char line[100]; FILE *file; // 打开文件 file = fopen("plates.txt", "r"); if (file == NULL) { perror("打开文件失败"); return 1; } // 读取所有行,单独分配内存存储 while(fgets(line, sizeof line, file) != NULL && line_count < 100) { // 去掉fgets读取到的换行符(可选,根据需求调整) line[strcspn(line, "\n")] = '\0'; // 为当前行分配内存并拷贝内容 string[line_count] = malloc(strlen(line) + 1); strcpy(string[line_count], line); line_count++; } fclose(file); if (line_count == 0) { printf("文件为空\n"); return 0; } // 初始化随机数种子,仅需执行一次 srand((unsigned int)time(NULL)); // Fisher-Yates 洗牌算法打乱数组 for (int i = line_count - 1; i > 0; i--) { // 生成0到i之间的随机索引 int j = rand() % (i + 1); // 交换元素 char* temp = string[i]; string[i] = string[j]; string[j] = temp; } // 示例:依次输出所有随机不重复的行,需要取多少就取前多少个 printf("随机选取的行:\n"); for (int i = 0; i < line_count; i++) { printf("%s\n", string[i]); // 用完释放内存,避免泄漏 free(string[i]); } return 0; }
可选实现:不打乱原数组的方案
如果你需要保留原数组的顺序,可以额外维护一个访问标记数组,每次生成随机索引直到找到未访问过的行:
// 初始化访问标记数组 int visited[100] = {0}; int pick_count = 3; // 假设要选3行 for (int i = 0; i < pick_count; i++) { int rand_idx; do { rand_idx = rand() % line_count; } while (visited[rand_idx]); visited[rand_idx] = 1; printf("第%d次选中:%s\n", i+1, string[rand_idx]); } // 最后记得释放string数组的内存
内容的提问来源于stack exchange,提问作者user17276226
相关产品推荐
相关产品推荐

