没有重复的随机数生成器

Posted

技术标签:

【中文标题】没有重复的随机数生成器【英文标题】:Random number generator with no duplicates 【发布时间】:2021-06-01 14:07:04 【问题描述】:

基本上我正在创建一个程序来随机生成 6 个唯一的彩票号码,因此在同一行中没有重复,这是我到目前为止的代码......

        //Generate 6 random numbers using the randomiser object

        int randomNumber1 = random.Next(1, 49);
        int randomNumber2 = random.Next(1, 49);
        int randomNumber3 = random.Next(1, 49);
        int randomNumber4 = random.Next(1, 49);
        int randomNumber5 = random.Next(1, 49);
        int randomNumber6 = random.Next(1, 49);

        textBox1.Text = randomNumber1.ToString();
        textBox2.Text = randomNumber2.ToString();
        textBox3.Text = randomNumber3.ToString();
        textBox4.Text = randomNumber4.ToString();
        textBox5.Text = randomNumber5.ToString();
        textBox6.Text = randomNumber6.ToString();

    

我得到的是随机数,但有时同一行上有相同的数字,如何使每个数字唯一????

提前致谢

【问题讨论】:

一种方法是打乱数字 (Fisher-Yates),然后取前 6 个。另一种方法是通过哈希集拒绝已经遇到的数字。 可能重复***.com/questions/22737687/… 不要忘记不允许重复会使数字不那么随机。 另一种方法是使用reservoir sampling,正如我在回答中所展示的那样。对于这样一个小问题,这可能是矫枉过正,但如果你想从 100000 个数字中挑选 6 个没有重复的数字,那么最好走水库采样路线,而不是创建一个包含这么多项目的列表并排序它。 在这里查看我的答案:***.com/a/47420924/700693 【参考方案1】:

您需要将它们存储在一个集合中,并且每次选择一个新号码时都需要确保它不存在,否则您需要生成一个新号码,直到找到一个唯一号码。

代替这个,我会在149之间生成一个序列,将它们打乱并从序列中选出6个数字,例如:

var rnd = new Random();
var randomNumbers = Enumerable.Range(1,49).OrderBy(x => rnd.Next()).Take(6).ToList();

【讨论】:

这会导致对小数字的一些偏见,但它相当小,在大多数情况下可以忽略。【参考方案2】:

你不能。您只指定每个数字是 1 到 49 之间的随机数,而不是它不应该匹配任何重复项。

由于您的数字集相对较少,因此最好的选择可能是抽取随机数,将它们放入 HashSet 中,然后如果需要更多,请拉更多。像这样的:

HashSet<int> numbers = new HashSet<int>();
while (numbers.Count < 6) 
    numbers.Add(random.Next(1, 49));

在这里,您正在利用 HashSet 消除重复项。这不适用于 List 或其他集合。

【讨论】:

好点。陷入了考虑创建六个数字的陷阱,然后确定是否需要解决重复问题。谢谢。 请注意——在这种特殊情况下,您不妨使用SortedSet&lt;T&gt;,它会以开箱即用的排序顺序为您提供数字。另一方面,任何适当的ISet&lt;T&gt; 实现都应该足够了,因此没有必要使用HashSet&lt;T&gt;(但它仍然可以)。【参考方案3】:

返回重复值为了让生成器满足必要的随机性统计特性的必要条件:抽取数字的概率不依赖于先前抽取的数字。

您可以随机播放 1 到 49 范围内的整数并返回前 6 个元素。有关此类洗牌器的更多详细信息,请参阅http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle。

但是,我认为这样做会产生轻微的统计偏差。

最好的方法可能是使用random.Next(1, 49); 并拒绝任何重复。这将没有统计偏差,而且您只需要 49 种可能性中的 6 种,碰撞次数不会明显减慢算法。

【讨论】:

And the canonical shuffle here on ***.【参考方案4】:

对reservoir sampling使用这种扩展方法:

public static IList<T> TakeRandom<T>(
    this IEnumerable<T> source, int count, Random random)

    var list = new List<T>(count);
    int n = 1;
    foreach (var item in source)
    
        if (list.Count < count)
        
            list.Add(item);
        
        else
        
            int j = random.Next(n);
            if (j < count)
            
                list[j] = item;
            
        
        n++;
    
    return list;

您可以像这样对您的收藏品进行采样:

var random = new Random();
var numbers = Enumerable.Range(1, 49).TakeRandom(6, random);
numbers.Shuffle(random);

请注意,返回的numbers 将从 1, 2, ..., 49 中的一组 6 个数字的所有(49 个选择 6)可能性中统一采样,但它们既不会保持顺序也不会保持被统一洗牌。如果您还想随机排序,您可以在之后轻松地执行标准Fisher-Yates shuffle。

public static void Shuffle<T>(this IList<T> list, Random random)

    for (int i = 0; i < list.Count; i++)
    
        int j = random.Next(i, list.Count);
        T temp = list[j];
        list[j] = list[i];
        list[i] = temp;
    

请注意,可以在此答案中找到更优化的 Fisher-Yates shuffle 版本:Randomize a List<T>

【讨论】:

【参考方案5】:
   List<int> aux = new List<int>();
   while(aux.Count < 6)
   
      int rnd = random.Next(1,49);
      if(!aux.Contains(rnd))aux.add(rnd);
    

如果您将所有 Texbox 放在同一个面板中,您可以这样做

  int j = 0;
  foreach(Control x in MyPanel.Controls)
  
     if(x is TexBox)
     
        x.Text = aux[j].toString();
        j++;
      
   

【讨论】:

【参考方案6】:

这是我的解决方案:生成数字数组

/// <summary>
/// auto generate a array with number element and max value is max
/// </summary>
/// <param name="number">number element of array</param>
/// <param name="max">max value of array</param>
/// <returns>array of number</returns>
public static int[] createRandomArray(int number, int max)

    List<int> ValueNumber = new List<int>();
    for (int i = 0; i < max; i++)
        ValueNumber.Add(i);
    int[] arr = new int[number];
    int count = 0;
    while (count < number)
    
        Random rd = new Random();
        int index = rd.Next(0,ValueNumber.Count -1);
        int auto = ValueNumber[index];
        arr[count] = auto;
        ValueNumber.RemoveAt(index);
        count += 1;
    
    return arr;

【讨论】:

【参考方案7】:

为时已晚,但我使用了我创建的名为 M_Randomizer 的方法。它可能看起来工作量太大,但它的技术不同于传统的基于生成随机数并检查先前生成的列表的唯一性。此代码在生成新的随机数时,从不查找先前生成的。如果我们谈论触及所有组合,我已经测试了这种方法直到 9 阶乘,可能对某些人来说几乎没有偏差,但它触及了所有组合。

using System;
class Randomizer

public int[] M_Randomizer(int x)

    bool b = false;
    if (x < -1)
    
        b = true;
        x = -1 * x;
    
    if(x == -1)
        x = 0;
    if (x < 2)
        return new int[x];

    int[] site;
    int k = new Random(Guid.NewGuid().GetHashCode()).Next() % 2;
    if (x == 2)
    
        site = new int[2];
        site[0] = k;
        site[1] = 1 - site[0];
        return site;
    
    else if (x == 3)
    
        site = new int[3];
        site[0] = new Random(Guid.NewGuid().GetHashCode()).Next(0, 3);
        site[1] = (site[0] + k + 1) % 3;
        site[2] = 3 - (site[0] + site[1]);
        return site;
    
    site = new int[x];
    int a = 0, m = 0, n = 0, tmp = 0;
    int[] p = M_Randomizer(3);
    int[] q;

    if (x % 3 == 0)
        q = M_Randomizer(x / 3);
    else
        q = M_Randomizer((x / 3) + 1);
    if (k == 0)
    
        for (m = 0; m < q.Length; m++)
        
            for (n = 0; n < p.Length && a < x; n++)
            
                tmp = (q[m] * 3) + p[n];
                if (tmp < x)
                
                    site[a] = tmp;
                    a++;
                
            
        
    
    else
    
        while (n < p.Length)
        
            while (a < x)
            
                tmp = (q[m] * 3) + p[n];
                if (tmp < x)
                
                    site[a] = tmp;
                    a++;
                
                m = m + k;
                if (m >= q.Length)
                    break;
            
            m = m % q.Length;
            n++;
        
    

    a = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2) + 1;
    k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
    if (k > 5)
        for (int i = a; i < k; i++)
            while (a < site.Length)
            
                if (k % (a + 1) == 0)
                
                    tmp = site[a - 1];
                    site[a - 1] = site[a];
                    site[a] = tmp;
                
                a = a + 2;
            

    k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
    if (k > 5)
    
        n = x / 2;
        k = 0;
        if (x % 2 != 0)
            k = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2);

        p = new int[n + k];
        m = (x - n) - k;
        for (a = 0; m < x; a++, m++)
            p[a] = site[m];

        m = n + k;
        for (a = (x - m) - 1; a >= 0; a--, m++)
            site[m] = site[a];

        for (a = 0; a < p.Length; a++)
            site[a] = p[a];
    

    int[] site2;
    int[] site3 = new int[x];
    if (b)
        return site;
    else
        site2 = M_Randomizer(-1 * x);

    for (a = 0; a < site.Length; a++)
        site3[site2[a]] = site[a];

    return site3;


public int[] M_Randomizer(int x, int start)

    int[] dm = M_Randomizer(x);

    for(int a = 0; a < x; a++)
        dm[a] = dm[a] + start;

    return dm;


【讨论】:

【参考方案8】:

看看使用array 来保存您的 6 个号码。

每次生成一个时,循环遍历数组以确保它不存在。如果是,则再次生成另一个 & 循环,直到出现不匹配。

【讨论】:

好主意,但您应该根据代码问题向人们展示一个代码示例,这将真正帮助人们...... 有人对此投了反对票(不是你,你的评论是一年前的昨天,巧合),所以我只想说我们不是代码编写服务。真的,人们应该发布代码,告诉我们它应该做什么以及它实际上在做什么,我们可以帮助协调后两者。如果我们给他们代码,我们不会帮助他们。即便如此,给出一个(非常基本的)算法也有点推动它。但是,如果他能弄清楚如何编写代码,那么至少会学到一些东西。【参考方案9】:

使用数组和 OOP(面向对象编程)非常简单。在开始之前,您必须将 Linq(使用 System.Linq) 库添加到您的项目中。

Random random = new Random();
int[] array = new int[6];
int number;

for (int i = 0; i < 6; i++)

    number = random.Next(1, 50);
    if (!array.Contains(number)) //If it's not contains, add number to array;
    array[i] = number;
    else //If it contains, restart random process
    i--;


for (int i = 1; i < 7; i++)

    foreach (Control c in this.Controls) //Add random numbers to all Textboxes
    
        if (c is TextBox && c.Name.EndsWith(i.ToString()))
        
            c.Text = array[i - 1].ToString();
        
    

【讨论】:

【参考方案10】:

一种函数式方法可能是生成无限的随机数序列,过滤掉非唯一数并获取您需要的唯一数。

例如:

private IEnumerable<int> RandomDigitStream(int seed)

    Random random = new Random(seed);
    while (true)
    
        yield return random.Next(DIGIT_MIN, DIGIT_MAX);
    


private List<int> GenerateUniqueRandomNumbers(int seed, int count)

     // Assert that DIGIT_MAX - DIGIT_MIN > count to ensure
     // algorithm can finish

     return RandomDigitStream(seed)
        .Distinct()
        .Take(count)
        .ToList();

这个算法的效率主要取决于.NET团队如何实现Distinct。它的内存使用量会随着您需要的位数和随机函数产生的位数范围而增长。它还具有不可预测的运行时间,因为它取决于随机函数的概率分布。事实上,如果随机算法产生的位数范围小于您需要的位数,则该算法可能会陷入无限循环。

然而,实际来看,少量数字应该没问题,但如果您正在查看大量数字 (100 +),您可能需要查看其他方法。

如果在不使用查找表的情况下甚至可能的话,设计一个只产生唯一数字的随机算法会更有效。

【讨论】:

【参考方案11】:

这是一个使用递归生成数字行的小程序,也使用递归来随机化并获取唯一数字。

using System;
using System.Linq;
using System.Collections.Generic;

public class Program

    public static Random random;
    public static List<int> lottoNumbers = Enumerable.Range(1, 49).ToList();
    public static void Main()
    
        random = new Random((int)DateTime.Now.Ticks);

        var LinesToGenerate = 10;

        GenerateNumbers(LinesToGenerate);
    

    public static void GenerateNumbers(int LineCount)
    
        int[] SelectedNumbers = new int[6];
        for (var i = 0; i < 6; i++)
        
            var number = GetRandomNumber(lottoNumbers.ToArray());

            while (SelectedNumbers.Contains(number))
                number = GetRandomNumber(lottoNumbers.ToArray());

            SelectedNumbers[i] = number;
        

        var numbersOrdered = SelectedNumbers.OrderBy(n => n).Select(n => n.ToString().PadLeft(2, '0'));
        Console.WriteLine(string.Join(" ", numbersOrdered));

        if (LineCount > 1)
            GenerateNumbers(--LineCount);
    

    //Recursively and randomly removes numbers from the array until only one is left, and returns it
    public static int GetRandomNumber(int[] arr)
    
        if (arr.Length > 1)
        
            //Remove random number from array
            var r = random.Next(0, arr.Length);
            var list = arr.ToList();
            list.RemoveAt(r);
            return GetRandomNumber(list.ToArray());
        

        return arr[0];
    

【讨论】:

【参考方案12】:

是的。使用数组。 循环你想要多少次: 生成一个随机数, 遍历数组并将所有与生成的数字进行比较。 如果有匹配,则再次循环,直到没有匹配。 然后存储它。

完成:)

【讨论】:

以上是关于没有重复的随机数生成器的主要内容,如果未能解决你的问题,请参考以下文章

golang连续生成随机数重复

生成n组随机数而没有循环[重复]

没有数组的java随机数生成器

创建无重复的随机数序列

相邻不重复随机数的生成及优化

需要一个JavaScript代码,生成1--100之间的随机数一共一百个,每行显示10个数字,而且这