伪随机数是以相同的概率从一组有限的数字中选取的。所选数字并不具有完全的随机性,因为它们是用一种确定的数学算法选择的,但是从实用的角度而言,其随机程度已足够了。Random 类的当前实现是基于 Donald E. Knuth 的减随机数生成器算法的,它提供我们产生随机的整数、双精度随机数和字节随机数。
以下是模拟投掷硬币两面的代码,需注意的是:Next产生的是从第一个参数开始,无限的趋向第二个参数,但不会产生第二个参数的值。
System.Random r = new Random();
for (int i = 0; i <= 10 - 1; i++)
{
System.Console.WriteLine(r.Next(1, 3));
}
运行的效果如图3.1.18:
图3.1.18
如果写成以下代码,
System.Console.WriteLine(r.Next(1, 2));
则产生出来的都是1了。
产生双精度的随机值其实是产生0到1之间的浮点数,以下代码给出了样例:
System.Random r = new Random();
for (int i = 0; i <= 10 - 1; i++)
{
System.Console.WriteLine(r.NextDouble());
}
运行的结果如图3.1.19:
图3.1.19
产生随机字节的方式有所不同。我们需要先定义一个字节数组,然后填充这个数组,代码演示如下:
System.Random r = new Random();
byte[] bytes = new Byte[10];
r.NextBytes(bytes);
for (int i = 0; i <= bytes.Length - 1; i++)
{
System.Console.WriteLine(bytes[i]);
}
运行效果如下图3.1.20:
图3.1.20
在实例化Random对象时,需要注意一个很重要的概念。在一般情况不要在循环中生成随机对象实例,以下代码会产生令你惊讶的效果。
for (int i = 0; i <= 10 - 1; i++)
{
System.Random r = new Random();
System.Console.WriteLine(r.Next(1, 10));
}
也许你认为是产生1-9之间的随机分部值,但事实是往往出现的是一连串相同的值,偶然会出现些不同的值列。如图3.1.21所示,所有数字均为6。
图3.1.21
这是为什么呢?随机数的生成是从种子值开始。如果反复使用同一个种子,就会生成相同的数字系列。产生不同序列的一种方法是使种子值与时间相关,从而对于 Random 的每个新实例,都会产生不同的系列。默认情况下,Random 类的无参数构造函数使用系统时钟生成其种子值。
因为处理器的速度很快,在一个循环中每次实例的时候系统时钟的值都一样,因此得到的数字系列也就一样。如果你一定要在循环中实例化随机对象,可用使用以下方式:
for (int i = 0; i <= 10 - 1; i++)
{
System.Random r = new Random(unchecked((int)(DateTime.Now.Ticks >> i)));
System.Console.WriteLine(r.Next(1, 10));
}
以下我们模拟一个随机发票的案例,首先我们定义一个存放54个string元素的数组,为该数组创建一副新的牌。
string[] pokers = new string[54];
for (int i = 0; i <= 13 - 1; i++)
{
string title = "";
switch (i + 1)
{
case 1:
title = "A";
break;
case 11:
title = "J";
break;
case 12:
title = "Q";
break;
case 13:
title = "K";
break;
default:
title = (i + 1).ToString();
break;
}
pokers[i] = string.Format("{0}{1}", (char)3, title);//红桃花色
pokers[i + 13] = string.Format("{0}{1}", (char)4, title);//方块花色
pokers[i + 26] = string.Format("{0}{1}", (char)5, title);//草花花色
pokers[i + 39] = string.Format("{0}{1}", (char)6, title);//黑桃花色
}
pokers[52] = string.Format("{0}", (char)1);//小鬼
pokers[53] = string.Format("{0}", (char)2);//大鬼
我们使用for循环排列输出数组中的元素。
for (int i = 0; i <= pokers.Length - 1; i++)
{
System.Console.Write("{0,-4}", pokers[i]);
//每13张牌输出一行
System.Console.Write("{0}", (i + 1) % 13 == 0 ? "\n" : "");
}
结果如图3.1.22:
图3.1.22
现在我们再定义三个string数组,分部标示玩家一、玩家二和玩家三。
string[] player1 = new string[18];
string[] player2 = new string[18];
string[] player3 = new string[18];
然后我们要把pokers数组中的值,随机的赋值到这三个数组中。我们的思路是,随机的定一个pokers中的位置,如果该位置中元素的值非空,则将元素中的值赋值给一个玩家,然后将这个位置上的元素赋值为null。
System.Random r = new Random();
for (int i = 0; i <= player1.Length - 1; i++)
{
int index = r.Next(0, pokers.Length);
while (pokers[index] == null)
{
index = r.Next(0, pokers.Length);
}
player1[i] = pokers[index];
pokers[index] = null;
index = r.Next(0, pokers.Length);
while (pokers[index] == null)
{
index = r.Next(0, pokers.Length);
}
player2[i] = pokers[index];
pokers[index] = null;
index = r.Next(0, pokers.Length);
while (pokers[index] == null)
{
index = r.Next(0, pokers.Length);
}
player3[i] = pokers[index];
pokers[index] = null;
}
以上代码根据我们的思路进行处理的结果,将牌随机的发给三个玩家了。然后我们输出看下,是否真的随机将牌发出了。
string[][] players = new string[][] { player1, player2, player3 };
for (int i = 0; i <= players.Length - 1;i++ )
{
for (int j = 0; j < +players[i].Length - 1;j++ )
{
System.Console.Write("{0,-4}", players[i][j]);
}
System.Console.WriteLine();
}
我们得到的结果如图3.1.23:
图3.1.23