news 2026/3/19 20:17:14

抽奖机随机号码生成: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/3/16 6:10:01

python django flask鹿幸公司员工食堂在线点餐餐饮餐桌预约管理系统的设计与实现_utcnqqs0--论文

文章目录系统截图项目技术简介可行性分析主要运用技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统截图 python django flask鹿幸公司员工食堂在线点餐餐饮餐桌预约管理系统的设计与实现_utcnqqs0–论文 …

作者头像 李华
网站建设 2026/3/15 17:52:40

性价比高的老房换新实用门窗品牌精选指南排名

性价比高的老房换新实用门窗品牌精选指南排名在老房换新的过程中&#xff0c;门窗的更换是至关重要的一环。选择一款性价比高的门窗&#xff0c;不仅能提升居住的舒适度&#xff0c;还能为家居增添美观。以下为大家带来一份实用的门窗品牌精选指南。工厂直营模式&#xff1a;性…

作者头像 李华
网站建设 2026/3/15 17:52:48

好用做老房换新实用门窗品牌精选指南的机构

做老房换新实用门窗的品牌精选指南引言老房换新门窗是提升居住品质的重要工程&#xff0c;然而面对众多的门窗品牌&#xff0c;消费者往往不知如何选择。在众多选择中&#xff0c;工厂直营模式的品牌有着独特的优势。专业评估能力像采用工厂直营模式的这类品牌&#xff0c;具备…

作者头像 李华
网站建设 2026/3/15 17:51:38

灵活用工平台,我的实践复盘

灵活用工平台技术实践复盘&#xff1a;从行业挑战到解决方案的演进行业痛点分析当前&#xff0c;灵活用工平台领域正面临一系列深刻的技术挑战&#xff0c;这些挑战直接关系到平台的稳定性、合规性及用户体验。首要挑战在于海量并发处理与数据精准性。随着灵活用工模式渗透率的…

作者头像 李华
网站建设 2026/3/15 14:34:30

在duckdb 递归CTE中实现深度优先搜索DFS

原帖地址 https://github.com/duckdb/duckdb/discussions/15386 通常的递归CTE都是广度优先搜索&#xff08;BFS&#xff09; WITH RECURSIVE edges(a, b) as( VALUES(1, 2),(1, 3),(2, 4),(4, 5),(4, 6) ), bfs(node, path) AS (SELECT 1 AS node, [] :: STRUCT("from&…

作者头像 李华
网站建设 2026/3/16 5:54:46

基于记忆增强网络的语言模型推理优化

基于记忆增强网络的语言模型推理优化 关键词:记忆增强网络、语言模型、推理优化、注意力机制、深度学习 摘要:本文聚焦于基于记忆增强网络的语言模型推理优化。首先介绍了相关背景,包括研究目的、预期读者、文档结构和术语定义。接着阐述了核心概念,如记忆增强网络和语言模…

作者头像 李华