C#生成不重复随机数的多种实现方案
C#中的 System.Random 类默认产生伪随机数,若直接调用极易出现重复值。针对需要生成不重复随机数列的场景(如题目乱序),本文分析并提供了三种主流实现方式:利用数组元素交换提高效率的方法、使用集合去重的简单循环,以及递归检测方案。
Random 类基础使用
System.Random 通过特定的算法对“随机种子”进行运算来模拟随机性。保证每次生成的随机数足够随机的关键在于随机种子的选取。通常系统默认使用当前时间作为种子。
第一步:初始化 Random 对象
可以通过无参构造或指定种子两种方式进行初始化:
1. 自动种子初始化(推荐)
不指定参数时,系统自动选取当前时间戳相关参数作为种子:
Random ro = new Random();
2. 手动指定种子
可以传入一个整型参数。为了获得更高质量的随机性,常将 DateTime.Now.Ticks 转换为整数:
long tick = DateTime.Now.Ticks;
// 确保在不同进程并发启动时种子不同
Random ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
int iSeed = 10;
Random rFixed = new Random(10); // 固定种子,便于调试
第二步:生成随机数值
初始化完成后,使用对应的方法获取随机数:
- 生成非负整数:
ro.Next()返回从 0 到Int32.MaxValue的值。 - 生成上限内整数:
ro.Next(100)返回小于 100 的非负整数(不包含 100)。 - 生成区间整数:
ro.Next(50, 100)返回 50 到 99 之间的随机整数(左闭右开)。 - 生成浮点数:
ro.NextDouble()返回 0.0 到 1.0 之间的双精度浮点数。
生成不重复随机数的三种方法
直接使用 Random 类生成大量数字时,重复概率很高。在生成不重复随机数序列(如打乱题库顺序)时,可采用以下逻辑:
方法一:数组交换法(效率最高,推荐使用)
该算法核心思想类似于 Fisher-Yates 洗牌算法。先建立一个包含所有可能数字的数组,然后随机抽取其中一个位置的数字放入结果集,并将该位置与当前范围的末尾元素交换,从而缩小下一次抽取的范围。
适用场景:数据总量较小,追求极高的运行效率。
int[] index = new int[15];
// 1. 填充索引数组: [0, 1, 2, ... 14]
for (int i = 0; i < 15; i++)
index[i] = i;
Random r = new Random();
int[] result = new int[10]; // 目标:获取10个不重复的数
int site = 15; // 当前可抽取的最大范围上限
for (int j = 0; j < 10; j++)
{
// 2. 在剩余范围内随机选一个位置
int id = r.Next(1, site - 1);
// 3. 取出该位置的数存入结果
result[j] = index[id];
// 4. “踢出”机制:用最后一个数覆盖当前位置
index[id] = index[site - 1];
// 5. 缩小范围
site--;
}
方法二:Hashtable/HashSet 去重法
不断生成随机数并使用哈希表检查是否已存在。如果存在则重新生成,直到达到目标数量。
注意:Hashtable 是旧式 .NET 集合,现代 C# 开发建议使用 HashSet<int>。此方法在数据总量很大且只需提取少量随机数时性能较好;若需提取接近总量的数据,会因频繁冲突导致死循环或极慢速度。
using System.Collections;
Hashtable hashtable = new Hashtable();
Random rm = new Random();
int RmNum = 10; // 目标数量
while (hashtable.Count < RmNum)
{
int nValue = rm.Next(100); // 范围 0-99
// 关键:检查是否已存在
if (!hashtable.Contains(nValue) && nValue != 0)
{
hashtable.Add(nValue, nValue);
Console.WriteLine(nValue.ToString());
}
}
方法三:递归检测法
每生成一个新数字,就与已生成的数组进行比较。如果发现重复,则重新生成并再次检测(递归)。这种方法逻辑直观,但在数组较长或范围较窄时,重复率增加会导致递归深度过大,甚至引发栈溢出(Stack Overflow)。
public int GetDistinctNumber(int[] arrNum, int tmp, int minValue, int maxValue, Random ra)
{
int n = 0;
bool isDuplicate = false;
// 检查是否已存在于数组中
while (n <= arrNum.Length - 1)
{
if (arrNum[n] == tmp)
{
isDuplicate = true;
break;
}
n++;
}
if (isDuplicate)
{
// 重复则重新生成
tmp = ra.Next(minValue, maxValue);
return GetDistinctNumber(arrNum, tmp, minValue, maxValue, ra);
}
else
{
return tmp;
}
}
调用方式示例:
Random ra = new Random(unchecked((int)DateTime.Now.Ticks));
int[] arrNum = new int[10];
int minValue = 1;
int maxValue = 10;
for (int i = 0; i < 10; i++)
{
arrNum[i] = GetDistinctNumber(arrNum, ra.Next(minValue, maxValue), minValue, maxValue, ra);
}
总结
C# 生成不重复随机数的最佳实践取决于具体需求:对于确定范围的离散值排列(如抽签、排序),方法一(数组交换法) 是性能和内存占用平衡最好的选择;对于从小范围大数据集中抽样,可选用 方法二(集合去重);而 方法三(递归) 由于潜在的性能陷阱,仅建议在简单测试或极小范围内使用。
以上就是用C#生成不重复的随机数的详细内容,更多关于C#编程的资料请关注本站其它相关文章!免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









