C#生成不重复随机数的多种实现方案

Ada
Ada
发布时间:2026-10-14 | 5人浏览过
原创

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#编程的资料请关注本站其它相关文章!
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表