news 2026/4/18 14:28:24

抽奖机随机号码生成:3 种算法实现 + 测试全解析(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
抽奖机随机号码生成:3 种算法实现 + 测试全解析(附完整代码)

在《数据结构与算法 II》课程设计中,我以 “抽奖机随机号码序列生成” 为主题,实现了 3 种经典随机抽样算法,并完成了随机性验证。这篇文章会详细分享算法逻辑、代码实现、测试过程及课设收获,文末附完整可运行代码

一、需求与算法选型

本次课设目标是实现 “从 1~n 中抽取 m 个不重复随机整数”,针对不同场景,选择了 3 种算法:

算法适用场景核心优势
暴力随机法m 远小于 n(如 1~30 抽 5)实现最简单
洗牌抽样法m 接近 n(如 1~10 抽 3)无重复生成开销
费雪耶茨抽样法n 极大、m 较小(如 1~100 抽 5)空间复杂度优化至 O (m)

二、算法实现与代码解析

1. 暴力随机法

核心逻辑:循环生成随机数,遍历已生成号码查重,重复则重新生成。

cpp

运行

// 暴力随机法:1~maxNum抽winNum个不重复号码 vector<int> bruteForceRandom(int minNum, int maxNum, int winNum) { vector<int> winNums; if (minNum > maxNum || winNum <= 0 || winNum > (maxNum - minNum + 1)) { cerr << "暴力随机法参数错误!" << endl; return winNums; } srand((unsigned int)time(NULL)); while (winNums.size() < static_cast<size_t>(winNum)) { int num = rand() % (maxNum - minNum + 1) + minNum; bool isRepeat = false; // 暴力查重 for (int n : winNums) { if (n == num) { isRepeat = true; break; } } if (!isRepeat) winNums.push_back(num); } return winNums; }

2. 洗牌抽样法

核心逻辑:生成完整号码序列→费雪耶茨洗牌→取前 m 个元素。

cpp

运行

// 洗牌函数 void shuffleNumbers(vector<int>& nums) { srand(time(nullptr)); for (size_t i = nums.size() - 1; i > 0; i--) { int j = rand() % (static_cast<int>(i) + 1); swap(nums[i], nums[j]); } } // 洗牌抽样法 vector<int> shuffleSampling(int minNum, int maxNum, int winNum) { vector<int> nums, result; if (minNum > maxNum || winNum <= 0 || winNum > (maxNum - minNum + 1)) { cerr << "洗牌抽样法参数错误!" << endl; return result; } // 创建号码池 for (int i = minNum; i <= maxNum; i++) nums.push_back(i); shuffleNumbers(nums); // 抽取前winNum个 size_t winNumUnsigned = static_cast<size_t>(winNum); for (size_t i = 0; i < winNumUnsigned && i < nums.size(); i++) { result.push_back(nums[i]); } return result; }

3. 费雪耶茨抽样法

核心逻辑:初始化 1~m 序列→从 m+1 到 n 随机替换前 m 个位置元素。

cpp

运行

// 简单随机数生成 int getRand(int min, int max) { static random_device rd; return min + (rd() % (max - min + 1)); } // 费雪耶茨抽样法 vector<int> fisherYatesSampling(int maxNum, int winNum) { vector<int> res; if (winNum <= 0 || winNum > maxNum) { cerr << "费雪耶茨抽样法参数错误!" << endl; return res; } res.resize(static_cast<size_t>(winNum)); // 初始化1~winNum for (size_t i = 0; i < static_cast<size_t>(winNum); i++) { res[i] = static_cast<int>(i + 1); } // 从winNum+1到maxNum随机替换 for (int i = winNum + 1; i <= maxNum; i++) { int j = getRand(1, i); if (static_cast<size_t>(j) <= static_cast<size_t>(winNum)) { res[static_cast<size_t>(j) - 1] = i; } } return res; }

三、测试类:验证算法随机性

为了验证算法的公平性,设计了LotteryTest测试类,支持单轮结果打印批量随机性统计

cpp

运行

class LotteryTest { private: int testTimes; // 测试轮数 int minNum; // 号码最小值 int maxNum; // 号码最大值 int winNum; // 抽取数量 // 打印单轮结果 void printSingleResult(const vector<int>& res, const string& algoName) { cout << "【" << algoName << "】抽奖结果:"; if (res.empty()) { cout << "无有效结果"; } else { for (size_t i = 0; i < res.size(); i++) { cout << res[i]; if (i != res.size() - 1) cout << " | "; } } cout << endl; } // 统计随机性 void statRandomness(vector<int> (*algo)(int, int, int), const string& algoName) { unordered_map<int, int> countMap; int validTest = 0; cout << "\n===== " << algoName << " 随机性测试(" << testTimes << "轮)=====" << endl; for (int i = minNum; i <= maxNum; i++) countMap[i] = 0; for (int t = 0; t < testTimes; t++) { vector<int> res = algo(minNum, maxNum, winNum); if (res.empty()) continue; validTest++; for (int num : res) countMap[num]++; } double idealFreq = static_cast<double>(winNum) / (maxNum - minNum + 1); cout << "理想频率:" << fixed << setprecision(4) << idealFreq << "次/轮" << endl; cout << "实际频率(号码:次数/轮 | 偏差):" << endl; for (int i = minNum; i <= maxNum; i++) { double actualFreq = static_cast<double>(countMap[i]) / validTest; cout << setw(3) << i << ": " << fixed << setprecision(4) << actualFreq << " | 偏差:" << abs(actualFreq - idealFreq) << endl; } } // 适配费雪耶茨抽样法的统计 void statFisherYates() { unordered_map<int, int> countMap; int validTest = 0; cout << "\n===== 费雪耶茨抽样法 随机性测试(" << testTimes << "轮)=====" << endl; for (int i = 1; i <= maxNum; i++) countMap[i] = 0; for (int t = 0; t < testTimes; t++) { vector<int> res = fisherYatesSampling(maxNum, winNum); if (res.empty()) continue; validTest++; for (int num : res) countMap[num]++; } double idealFreq = static_cast<double>(winNum) / maxNum; cout << "理想频率:" << fixed << setprecision(4) << idealFreq << "次/轮" << endl; cout << "实际频率(号码:次数/轮 | 偏差):" << endl; for (int i = 1; i <= maxNum; i++) { double actualFreq = static_cast<double>(countMap[i]) / validTest; cout << setw(3) << i << ": " << fixed << setprecision(4) << actualFreq << " | 偏差:" << abs(actualFreq - idealFreq) << endl; } } public: LotteryTest(int tTimes, int minN, int maxN, int winN) : testTimes(tTimes), minNum(minN), maxNum(maxN), winNum(winN) {} // 单轮测试 void singleTest() { cout << "=====================================" << endl; cout << " 单轮抽奖测试结果 " << endl; cout << "=====================================" << endl; vector<int> res1 = bruteForceRandom(minNum, maxNum, winNum); printSingleResult(res1, "暴力随机法"); vector<int> res2 = shuffleSampling(minNum, maxNum, winNum); printSingleResult(res2, "洗牌抽样法"); vector<int> res3 = fisherYatesSampling(maxNum, winNum); printSingleResult(res3, "费雪耶茨抽样法"); } // 批量随机性测试 void batchRandomTest() { cout << "\n=====================================" << endl; cout << " 随机性测试结果 " << endl; cout << "=====================================" << endl; statRandomness(bruteForceRandom, "暴力随机法"); statRandomness(shuffleSampling, "洗牌抽样法"); statFisherYates(); } };

四、运行结果与分析

1. 单轮测试结果

plaintext

===================================== 单轮抽奖测试结果 ===================================== 【暴力随机法】抽奖结果:8 | 3 | 9 【洗牌抽样法】抽奖结果:7 | 3 | 9 【费雪耶茨抽样法】抽奖结果:6 | 2 | 8

2. 批量随机性测试(10000 轮)

以暴力随机法为例,各号码实际频率与理想频率(0.3)偏差均小于 0.002,说明算法随机性均匀:

plaintext

===== 暴力随机法 随机性测试(10000轮)===== 理想频率:0.3000次/轮 实际频率(号码:次数/轮 | 偏差): 1: 0.2987 | 偏差:0.0013 2: 0.3012 | 偏差:0.0012 ...
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 11:05:09

python django flask考研互助交流平台_c62p51fu--论文

文章目录 系统截图项目技术简介可行性分析主要运用技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 系统截图 python django flask考研互助交流平台_c62p51fu–论文 项目技术简介 Python版本&#xff1…

作者头像 李华
网站建设 2026/4/18 4:00:12

小红书玫瑰克隆工具卡密购买

小红书目前流量非常大&#xff0c;适合商家去上面种草&#xff0c;且可以大量的发布笔记来获得流量&#xff01; 目前比较流行的小红书玫瑰克隆工具就是专门针对小红书笔记进行优化发布的一款实用型工具&#xff01; 很多小伙伴下载了软件&#xff0c;不知道在哪里充值购买卡…

作者头像 李华
网站建设 2026/4/18 1:06:38

熬夜刷手机不愿睡觉,这是一种心理问题吗?

熬夜刷手机&#xff0c;不愿睡觉 &#xff0c;这是一种“报复性睡前拖延” 你也是熬夜刷手机&#xff0c;不愿睡觉吗&#xff1f;其实这是一种 “报复性睡前拖延”心理&#xff1a;用剥夺睡眠来弥补白天被剥夺的自由感。 “白天不属于自己&#xff0c;于是深夜疯狂刷手机。…

作者头像 李华
网站建设 2026/4/18 4:02:03

基于SpringBoot+Vue的乡镇农村建设用地管理系统的设计与实现

前言 &#x1f31e;博主介绍&#xff1a;✌CSDN特邀作者、全栈领域优质创作者、10年IT从业经验、码云/掘金/知乎/B站/华为云/阿里云等平台优质作者、专注于Java、小程序/APP、python、大数据等技术领域和毕业项目实战&#xff0c;以及程序定制化开发、文档编写、答疑辅导等。✌…

作者头像 李华
网站建设 2026/4/18 4:04:58

解析:One-API 与 New-API 核心原理

One-API 与 New-API 都是用于统一管理和分发大模型API的工具&#xff0c;但其设计理念、核心架构和功能侧重点存在显著差异。下面将详细解析它们的工作原理&#xff0c;并进行对比。一、One-API 的工作原理One-API 的工作原理可以概括为&#xff1a;作为一个统一的API网关&…

作者头像 李华
网站建设 2026/4/17 5:40:46

【MongoDB实战】第10章 新手避坑指南:90%的人都会踩的错误

文章目录 《MongoDB实战入门》第10章 新手避坑指南:90%的人都会踩的错误 10.1 连接与配置类错误 10.1.1 连接字符串配置错误 错误场景与实战示例 正确配置与实战代码 标准连接字符串格式 正确实操代码(Python驱动) 10.1.2 服务启动失败 场景1:端口占用 排查与解决实战 场景…

作者头像 李华