这样是否每次都会始终生成UNIQUE RANDOM号码?

时间:2018-07-19 03:09:58

标签: c# .net

下面,我编写了运行良好的代码,并生成一个随机数,其工作逻辑是在返回随机数之前,它将随机数与文本文件中已生成的数字进行比较。我想确保每次都会产生唯一的随机数?

    public int GenerateRandomNo()
    {
        int _min = 0000;
        int _max = 9999;
        Random _rdm = new Random();
        return _rdm.Next(_min, _max);
    }
    public int rand_num()
    {

        string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
        int number = File.ReadLines(file_path).Count(); //count number of lines in file
        System.IO.StreamReader file = new System.IO.StreamReader(file_path);
        if (number == 0)
        {
            randnum = GenerateRandomNo();

        }
        else
        {
            randnum = GenerateRandomNo();
            for (int a = 1; a <= number; a++)
            {
                if ((file.ReadLine()) == randnum.ToString())
                    randnum = GenerateRandomNo();
            }
            file.Close();
        }
        createText = randnum.ToString() + Environment.NewLine;
        File.AppendAllText(file_path, createText);
        file.Close();
        return randnum;

    }

如果他们需要对代码进行任何改进,请告诉我。正如我要完全确保的那样,它将始终生成唯一的随机数。

5 个答案:

答案 0 :(得分:2)

这是执行此任务的非常有效的方法:

private Random rnd = new Random();

public int rand_num()
{
    string exe_path = System.Windows.Forms.Application.ExecutablePath;
    string exe_folder = System.IO.Path.GetDirectoryName(exe_path);
    string file_path = System.IO.Path.Combine(exe_folder, "Invoices\numbers.txt");

    var number =
        Enumerable
            .Range(0, 10000)
            .Except(File.ReadAllLines(file_path).Select(x => int.Parse(x)))
            .OrderBy(x => rnd.Next())
            .First();

    File.AppendAllLines(file_path, new [] { number.ToString() });

    return number;
}

它将生成所有数字Enumerable.Range(0, 10000)的列表。然后,它将删除在文本文件.Except(File.ReadAllLines(file_path).Select(x => int.Parse(x)))中已经找到的数字。然后,它会随机排序剩余的数字(例如洗牌一包).OrderBy(x => rnd.Next()),最后只选择第一个数字.First();

答案 1 :(得分:-1)

这是一种获取唯一随机数的方法。它使用另一个贡献者建议的HashSet。请注意,有一个System.Random实例级别的实例(您不应该一直在创建新实例),并且在种子中注明了时间,这将导致每个新的随机数序列时间。

    private readonly System.Random _random = new System.Random((int) (DateTime.UtcNow.Ticks & 0x7fff_ffff));
    private readonly HashSet<int> _previousNumbers = new HashSet<int>();
    private const int Min = 0;
    private const int Max = 9999;
    public int GetNextRandom()
    {
        int next;
        do
        {
            next = _random.Next(Min, Max);
        } while (_previousNumbers.Contains(next) && _previousNumbers.Count < Max-Min);

        _previousNumbers.Add(next);
        return next;
    }

还请注意,一旦没有剩余可分发的数字,它将停止分发随机数(由于它们需要唯一,因此在用完井之前只能进行最大-最小调用)。随着达到最大呼叫数量,它也会变慢(因为冲突的可能性不断增加)。

我还没有尝试解密您发布的代码,这有点混乱(而且,如果仅复制/粘贴,则不会编译)。但是,您的问题是关于生成唯一的随机数。

但是,有两点值得一提:

1。您正在尝试做什么

@mjwills(上)的评论是最相关的。为什么要这样做?

2。跳过先前生成的随机数可能会降低随机性

伪随机数生成器(例如System.Random)会创建一个数字序列,该数字序列在任何频率下均不显示自相关。这是随机的序列,而不是任何特定的数字。通过跳过其中一些(重复项),您可能会破坏这种随机性。

3。 System.Random通常是错误的选择

对于大多数应用程序,System.Random并不是一个好的随机数生成器。 System.Random是可重复的伪随机数生成器。如果给它相同的种子,它将一遍又一遍地为您提供完全相同的序列伪随机数(这就是为什么我从上面的时钟开始为它播种)。如果您确实想使用这种算法,并且不希望出现这种情况,则需要获得一种具有很多熵的播种机制(例如,一个好的随机数生成器,嘿,哎呀!)。

如果您想创建一个简单的游戏-比如说“ Magic 8-Ball”,则System.Random(上面带有机器刻度的种子)可能是正确的工具。如果您想要玩包含赌博的游戏,请不要这样做(并且,如果您想了解如何不使用System.Random的信息,请参阅蒙特利尔赌场上的Wikipedia文章(搜索“ keno”)) )。

如果您想要一个更好的可重复的伪随机数算法,请考虑使用Mersenne Twister实现。

如果您想做类似创建赌博机或做加密的事情,则需要像System.Security.Cryptography.RNGCryptoServiceProvider之类的东西。生成一系列128位随机数,就可以保证您的唯一性(或者,只需使用GUID)即可。

答案 2 :(得分:-2)

我原来的答案并没有真正回答真正的问题,所以我正在更新我的答案以查看是否可以添加一些有价值的东西。

这里是谜题答案的替代版本,可让您使用一个文件,该文件将数字与任何字符分隔,而不仅仅是换行符:

private Random rnd = new Random();

public int rand_num()
{
    string exe_path = System.Windows.Forms.Application.ExecutablePath;
    string exe_folder = System.IO.Path.GetDirectoryName(exe_path);
    string file_path = System.IO.Path.Combine(exe_folder, "Invoices\numbers.txt");

    string[] commaSeparatedValues = Regex.Split(System.IO.File.ReadAllText(file_path), "[^0-9]+");
    IEnumerable<int> usedValues = commaSeparatedValues.Select(x => int.Parse(x)).AsEnumerable();
    int[] availableValues = Enumerable.Range(0, 9999).Except(usedValues).ToArray();
    int number = availableValues[rnd.Next(0, availableValues.Count())];

    File.WriteAllText(file_path, commaSeparatedValues + "," + number);

    return number;
}

原始答案:

这不是不是创建创建随机数方法的好方法。问题出在这里:

每次调用GenerateRandomNo()时,都会创建一个新的Random对象,该对象使用当前时间作为种子。现在,假设您有一个要在其中生成随机数的循环(您可以这样做!),然后使用该方法。例如:

for(int i=0; i<10; i++){
    Console.WriteLine("Here is a number: " + GenerateRandomNo());
}

此过程是如此之快,以至于您可能实例化具有相同种子的Random对象!当您要求一个随机数时,这些单独的实例将为您提供相同的数字,因为它们具有相同的种子。

要解决此问题,您希望代码实例化Random一个实例,并根据需要多次使用它。例如:

public int GenerateRandomNo(Random r)
{
    int _min = 0000;
    int _max = 9999;
    return r.Next(_min, _max);
}
public int rand_num()
{
    Random rdm = new Random();
    string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
    int number = File.ReadLines(file_path).Count(); //count number of lines in file
    System.IO.StreamReader file = new System.IO.StreamReader(file_path);
    if (number == 0)
    {
        randnum = GenerateRandomNo(rdm);
    }
    else
    {
        randnum = GenerateRandomNo(rdm);
        for (int a = 1; a <= number; a++)
        {
            if ((file.ReadLine()) == randnum.ToString())
                randnum = GenerateRandomNo(rdm);
        }
        file.Close();
    }
    createText = randnum.ToString() + Environment.NewLine;
    File.AppendAllText(file_path, createText);
    file.Close();
    return randnum;
}

答案 3 :(得分:-2)

我建议您使用DateTime.UtcNow.Ticks.ToString() *。

我相信它会起作用,直到灾难性地失败为止。换句话说,代码将正常工作,直到根本无法工作(文件太大)为止。

我建议一种改进: 将初始种子存储在文件中。

*如果您将计算机配置为回到过去,则DateTime.UtcNow.Ticks.ToString()可能会返回相同的值,但这类似于对解决方案中使用的文件进行编辑。

答案 4 :(得分:-4)

不,它不起作用,请使用这种情况:

您的文件:

4
8
99
7

您的程序:

- generate the first random number = 4
- found the random number in the file
- generate antoher random number = 4 (yes, still 4)
- fail!

我试图在不运行代码的情况下修复您的代码。

private Random _rdm = new Random();

public int GenerateRandomNo()
{
    int _min = 0000;
    int _max = 9999;
    return _rdm.Next(_min, _max);
}

public int rand_num()
{

  string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
  int randnum = 0;
  string line = "";
  if (File.Exists(file_path)) {
    while(randnum==0)
    {
      randnum = GenerateRandomNo();
      using (System.IO.StreamReader file = new System.IO.StreamReader(file_path)) {
        while ((line = sr.ReadLine()) != null)
        {
           if (line == randnum.ToString()) {
             randnum = 0;
             break;
           }
        }
      }
      file.Close();
    }

    createText = randnum.ToString() + Environment.NewLine;
    File.AppendAllText(file_path, createText);
    file.Close();
    return randnum;
  } else {
    randnum = GenerateRandomNo();
    createText = randnum.ToString() + Environment.NewLine;
    File.WriteAllText(file_path, createText);
    file.Close();
  }
}

请注意支付: 您的代码库将多次访问文本文件,这是一个解决方案,但不是最佳解决方案。 如果将生成所有可能的随机数(在您的情况下为10000),则while循环永远不会结束。 如果您打算生成大量随机数,则必须更改此过程。