http://blog.csdn.net/JustLovePro/archive/2008/12/27/3624542.aspx
写的很好的说,解决了我实际中的一个问题
昨天一朋友问了下我关于他写的一段程序的错误.
其中有一个问题就是关于随机数的.他的代码如下(C++代码).
C/C++ code
for (int i =0;i< n;++i)
{
srand((unsigned)time( NULL ));
int r = rand()%100;
cout << r << ",";
}
这里很明显他是想输出一串小于100的随机的数列.可是运行结果输出的却是类似
97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,....
的序列.很明显这样完全看不出有任何的随机性.这是由于他对C的rand函数不理解导致
的错误用法.而这两天逛C#区我也同样看到了几个类似的错误用法(C和C#的rand从大体
的原理上差不多).想想自己初学的时候类似的错误犯得也不少.所以自己下去查了写资料
总结了在随机数使用上的一些错误的用法.希望能对初学者有所帮助.
先来说说随机数算法的实现.借用C数值算法里的一句话:利用计算机,这种人类所设计的
各种机器中最精确,最能做出确切判断的机器,来产生"随机数",这看上去有些自相矛盾.甚至在
概念上是讲不通的.任何程序必将产生完全可以预计的结果.因而不是真正的"随机数".
现在各种语言中的随机数产生函数所产生的"随机数",实际上被称之为"伪随机数".可以将
整个随机数函数看做这样一个表达式:
A = R(s)
其中R是随机函数,s是种子.A是一个数列.即对于任意一个种子s,经过R的计算后,总有一个确定
的数列A与之对应.而当在C#里调用var rnd = new Random (s)或在C里调用srand(s)实质上
所做工作之一就是设定这个种子.而rnd.Next();或rand()只不过是在A上取下一个元素而已.当然实
际的实现不可能事先计算一个数列A,所以rand()相当于由s计算出下一个数字s',然后将s'作为新
的种子赋值给s,最后将s'作为结果返回.
接下来就是两种常见的错误用法了:
C# code
for (int i=0;i<n;++i)
{
var rnd = new Random (s);//s是实先确定的一个数字
Console.Write ("{0},",rnd.Next());
}
这样使用随机数产生器只会产生一个固定的常数N.
因为每次都用同一个种子初始化了随机数产生器后调用了Next().
所取得的都是数列A上的第一个元素.而这个元素的值肯定是固定
的(当然N取什么值要看随机函数的实现而定).
而第二种情况就更常见了:
C# code
for (int i=0;i<n;++i)
{
var rnd = new Random ();//用系统时间作为种子
Console.Write ("{0},",rnd.Next());
}
这样调用应该是希望通过时间的不同来达到随机的效果;但是得到的结果就和我那位朋友一样.会是形似
97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,....
的一串数列.这是因为Windows系统时钟的更新频率大概在10ms左右.而这个for循环的执行显然要快
得多.于是在一段执行时间内Environment.TickCount (Random的默认种子)或是C的time函数返回的
都是同一个值.从而导致rnd.Next在一段时间内返回一个常数.
所以正确的用法应该将随机数产生器的初始化移出循环:
C# code
var rnd = new Random ();//用系统时间作为种子
for (int i=0;i<n;++i)
{
Console.Write ("{0},",rnd.Next());
}
就我个人习惯来说.在一个经常用到随机数的程序中,我会在Main中就初始化一个全局的随机数产生器,在
之后要用到随机数的地方就直接调用Next,而不用每次都构造一个Random.
1: --------------
2: *
3: * Copyright (c) 2008 Microsoft::Tsorgy.Utils, Reserved.
4: *
5: * Filename: @(#)Random.cs
6: * Create by: TsOrgY
7: * Email: tsorgy@gmail.com
8: * Date: 2008/12/27 15:01:40
9: *
10: * Classname: Random
11: * Description: 一种能够产生满足某些随机性统计要求的数字序列的设备.
12: *
13: */
using System;
using System.Runtime.InteropServices;
namespace Tsorgy.Utils {
/// <summary>
/// 表示伪随机数生成器,一种能够产生满足某些随机性统计要求的数字序列的设备.
/// </summary>
20: [Serializable]
true)]
class Random {
int inext;
int inextp;
int MBIG = 0x7fffffff;
int MSEED = 0x9a4ec86;
int MZ = 0;
int[] SeedArray;
/// <summary>
/// 使用与时间相关的默认种子值,初始化 Random 类的新实例.
/// </summary>
public Random()
this(Environment.TickCount) {
34: }
/// <summary>
/// 使用指定的种子值初始化 System.Random 类的新实例.
/// </summary>
int Seed) {
int[0x38];
int num2 = 0x9a4ec86 - Math.Abs(Seed);
this.SeedArray[0x37] = num2;
int num3 = 1;
int i = 1; i < 0x37; i++) {
int index = (0x15 * i) % 0x37;
this.SeedArray[index] = num3;
48: num3 = num2 - num3;
if (num3 < 0) {
50: num3 += 0x7fffffff;
51: }
this.SeedArray[index];
53: }
int j = 1; j < 5; j++) {
int k = 1; k < 0x38; k++) {
this.SeedArray[1 + ((k + 30) % 0x37)];
this.SeedArray[k] < 0) {
this.SeedArray[k] += 0x7fffffff;
59: }
60: }
61: }
this.inext = 0;
this.inextp = 0x15;
64: Seed = 1;
65: }
double GetSampleForLargeRange() {
this.InternalSample();
this.InternalSample() % 2) == 0) ? 1 : 0) != 0) {
69: num = -num;
70: }
double num2 = num;
72: num2 += 2147483646.0;
return (num2 / 4294967293);
74: }
int InternalSample() {
this.inext;
this.inextp;
if (++inext >= 0x38) {
79: inext = 1;
80: }
if (++inextp >= 0x38) {
82: inextp = 1;
83: }
this.SeedArray[inextp];
if (num < 0) {
86: num += 0x7fffffff;
87: }
this.SeedArray[inext] = num;
this.inext = inext;
this.inextp = inextp;
return num;
92: }
/// <summary>
/// 返回非负随机数.
/// </summary>
/// <returns>大于或等于零且小于 System.Int32.MaxValue 的 32 位带符号整数。</returns>
int Next() {
this.InternalSample();
99: }
/// <summary>
/// 返回一个小于所指定最大值的非负随机数.
/// </summary>
/// <returns>大于或等于零且小于 maxValue 的 32 位带符号整数,即:返回的值范围包括零但不包括 maxValue。</returns>
int maxValue) {
if (maxValue < 0) {
, maxValue));
109: }
this.Sample() * maxValue);
111: }
/// <summary>
/// 返回一个指定范围内的随机数.
/// </summary>
/// <returns>一个大于或等于 minValue 且小于 maxValue 的 32 位带符号整数,即:返回的值范围包括 minValue 但不包括 maxValue。如果minValue 等于 maxValue,则返回 minValue。</returns>
int maxValue) {
if (minValue > maxValue) {
, minValue, maxValue));
122: }
long num = maxValue - minValue;
if (num <= 0x7fffffffL) {
this.Sample() * num)) + minValue);
126: }
this.GetSampleForLargeRange() * num))) + minValue);
128: }
/// <summary>
/// 用随机数填充指定字节数组的元素.
/// </summary>
byte[] buffer) {
null) {
);
137: }
int i = 0; i < buffer.Length; i++) {
this.InternalSample() % 0x100);
140: }
141: }
/// <summary>
/// 返回一个介于 0.0 和 1.0 之间的随机数.
/// </summary>
/// <returns>大于或等于 0.0 而小于 1.0 的双精度浮点数字。</returns>
double NextDouble() {
this.Sample();
148: }
/// <summary>
/// 返回一个介于 0.0 和 1.0 之间的随机数.
/// </summary>
/// <returns>大于或等于 0.0 而小于 1.0 的双精度浮点数字。</returns>
double Sample() {
this.InternalSample() * 4.6566128752457969E-10);
155: }
156: }
157: }