一.伪随机数
刚接触随机数的时候,觉得计算机好神奇啊,可以得到我想要的任意数字,但是接触多了就发现计算机产生的随机数相似度如此之高,为什么运行多次产生的都是这几个数呢,也不是特别随机嘛。后来通过查阅资料才发现,计算机产生随机数字的方法其实产生的是伪随机数字。如果不采取一些措施,计算机总是应用同样的过程来产生一系列随机数字。这意味着每次运行程序都产生一样的结果。但是计算机为什么不产生真正的随机数呢,是做不到吗?答案当然是否定的了。
二.为什么要使用伪随机数呢?
大家可能觉得既然计算机可以产生真正的随机数,那么为什么不用呢。我们举一个小例子来解释一下:例如我们经常玩的贪吃蛇游戏中果实的位置是随机产生的,如果在某一时刻,果实出现在了某处,但是程序出错了,你想跟踪调试,但是你发现已经找不到原来那个出错的地方了,因为果实的产生是完全随机的。这样你的这个游戏就存在了潜在的错误,你自己知道,但是却不能发现错误源在哪里。所以为了避免这种错误,调试时还是使用伪随机数比较好,直到觉得程序确实没有错误了,可以考虑使用完全随机数。
三.随机数
完全随机函数通过初始化内部的伪随机数字生成器来使得每一次运行程序产生不同的结果,这正是编写游戏程序时所需要的。如果您要写一个用到随机数字的程序,那么在调试阶段最好不要调用完全随机函数。当程序运行良好时,可以在主程序的开始调用完全随机函数,从而使得程序运行发生变化。
四.有关随机数字的函数
int rand(void);
和其他大多数函数不同的是,rand每次调用时返回的结果不同。rand的结果一定是非负的,且不大于常量RAND_MAX,常量RAND_MAX是在stdlib.h中定义的。因此,每次调用rand时,它都返回一个0RAND_MAX之间的整数,包括边界。
RAND_MAX的值取决于计算机系统。当编写用到随机数字的程序时,您不能主观臆断RAND_MAX的值。可以通过代码查看你的计算机系统中的RAND_MAX的值。
rand函数需要一个初始值,用于启动整个过程的值,被称为随机数字发生器的种子(seed)。Rand函数的实现是这样的,每次运行程序时都把种子设为一个常数值,这就是为什么该函数总是生成序列一样的数值。来看一下下面的代码:
例一:
#include<stdlib.h>
#include<stdio.h>

#define NTrials 10

int main()
{
  int i;
  printf("On this Computer,RAND_MAX is %d.\n",RAND_MAX);
  printf("The first %d calls to rand return:\n",NTrials);
  for(i=0;i<NTrials;i++)
  {
         printf("%10d\n",rand());
  }
  return 0;
}
运行结果为:
On this Computer,RAND_MAX is 32767.
The first 10 calls to rand return:
                        41
                 18467
                    6334
                 26500
                 19169
                 15724
                 11478
                 29358
                 26962
                 24464
 
   
连续运行了好多遍,每次运行结果都运行结果还跟上面的一样,这就说明了rand函数是一个伪随机函数。
为了改变这种序列,需要设置seed为一个不同的值,这可以通过调用函数srandseed)来实现。函数srand可以重置seed的值,从而每次得到的随机数字的序列不一样。通常的做法是使用系统的时钟值作为初始值。因为时间是不断变化的,所以随机序列也是变化的。可以通过调用函数time检索当前系统的时钟值,该函数在ctime接口中有定义,之后把该结果转化为一个整数。这种技术可以使您写出下面的语句,它对初始化伪随机数字发生器的结果有不可预知的影响:
srand((int)time(NULL)); 
用一个例子看一下srand是不是真的能改变seed
例二:
int main()
{
         int i;
         srand((int)time(NULL));  //改变seed的值
    printf("The first %d calls to rand return:\n",NTrials);
         for(i=0;i<NTrials;i++)
         {
                   printf("%10d\n",rand());
         }
         return 0;
}
运行结果:
The first 10 calls to rand return:
     25865
     28610
     24648
      4690
      4180
     15648
     22709
      7359
     22068
     31563
   
再运行一次看看结果如何。
int main()
{
  int i;
  srand((int)time(NULL));    //改变seed的值
        printf("The Second %d calls to rand return:\n",NTrials);
  for(i=0;i<NTrials;i++)
  {
    printf("%10d\n",rand());
  }
  return 0;
}
运行结果:
The Second 10 calls to rand return:
         26551
         24784
            7804
         11714
            7553
         21061
         20964
         15223
         16094
         18421
 
    看来srand确实有效果哦。
   
五.如何将rand所得的结果转化为确定区间的数呢?
我们可以使用自定义函数RandomInteger来解释这个转化过程,它可以将0RAND_MAX之间的随机数字转换为参数lowhigh之间的数。这种实现有4个步骤:
正规化:该过程中的第一步是将rand的整数结果转换为一个在半开区间[0,1)之间的浮点型数d。为达到此目的,我们所做的工作就是将rand的结果转换为double类型,然后除以该范围中的元素个数。因为RAND_MAX是机器可以提供的最大整数,在加1之前将它转为double类型是很重要的,这样可以确保除法得到的结果严格小于1.
缩放:第二步是值d乘以要求范围的大小,这样它就能包含要求的整数个数。因为要求的范围包括端点,lowhigh,所以得到的范围中的整数个数为high-low+1.
舍尾:第三步是通过舍掉小数点后面的尾数,使用类型强制转换将数字换回成整数。这样就得到一个下限为0的一个随机整数。
平移:最后一步是加上下限low,以符合所要求的下限。
RandomInteger函数的实现如下代码:
int RandomInteger(int low,int high)
{
int k;
double d;
d=(double) rand()/((double) RAND_MAX+1);
k=(int)(d*(high-low+1));
return (low+k);
}
 
   
我们用一个小小的例子来测试一下这个函数能否达到目的。
例三:
   
#include<stdlib.h>
#include<stdio.h>
#include<ctime>                            

#define NTrials 10

int RandomInteger(int low,int high)
{
  int k;
  double d;
  d=(double) rand()/((double) RAND_MAX+1);
  k=(int)(d*(high-low+1));
  return (low+k);
}

int main()
{
  int i;
  srand((int)time(NULL));
  printf("%d calls to RandomInteger return:\n",NTrials);
  for(i=0;i<NTrials;i++)
  {
    printf("%4d",RandomInteger(0,100));
  }
  printf("\n");
  return 0;
}
运行结果:
10 calls to RandomInteger return:
    91    85    98    90     8    37    40    92     8    19    
 
   
浮点数的实现过程与整数的实现过程类似,只是不需要第三步,函数实现如下:
double RandomReal(double low,double high)
{
        double d;
        d=(double) rand()/ ((double)RAND_MAX+1);
        return (low+d*(high-low));
}
 
   
通过一下例子看一下它的运行结果如何:
例四:
    
#include<stdlib.h>
#include<stdio.h>
#include<ctime>                            //srand所在的头文件

#define NTrials 10

double RandomReal(double low,double high)
{
        double d;
        d=(double) rand()/ ((double)RAND_MAX+1);
        return (low+d*(high-low));
}

int main()
{
  int i;
  srand((int)time(NULL));
  printf("%d calls to RandomReal return:\n",NTrials);
  for(i=0;i<NTrials;i++)
  {
    printf("%6.2f",RandomReal(2.5,12.8));
  }
  printf("\n");
  return 0;
}
运行结果:
10 calls to RandomReal return:
    12.68     4.89     9.09     8.53    12.74     5.24    10.30     7.33    12.44     7.93