下面,我编写了运行良好的代码,并生成一个随机数,其工作逻辑是在返回随机数之前,它将随机数与文本文件中已生成的数字进行比较。我想确保每次都会产生唯一的随机数?
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;
}
如果他们需要对代码进行任何改进,请告诉我。正如我要完全确保的那样,它将始终生成唯一的随机数。
答案 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循环永远不会结束。 如果您打算生成大量随机数,则必须更改此过程。