繁体   English   中英

没有重复的随机数生成器

[英]Random number generator with no duplicates

基本上我正在创建一个程序来随机生成 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();

    }

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

提前致谢

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

取而代之的是,我会生成一个介于149之间的序列,将它们打乱并从序列中挑选 6 个数字,例如:

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

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

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

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

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

返回重复值必要的,以便生成器满足随机性的必要统计属性:抽取数字的概率不依赖于先前抽取的数字。

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

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

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

使用此扩展方法进行水库采样

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 版本: 随机化列表<T>

   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++;
     } 
  } 

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

/// <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;
}

为时已晚,但我使用了我创建的名为 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;
}
}

看看使用一个数组来保存你的 6 个数字。

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

使用数组和 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();
        }
    }
}

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

例如:

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 +),您可能想查看其他方法。

如果在不使用查找表的情况下甚至可能的话,制作一种仅首先生成唯一数字的随机算法会更有效。

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

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];
    }
}

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

完毕:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM