当前位置: 首页 > 文档资料 > C++大学教程 >

3.8 随机数产生器

优质
小牛编辑
130浏览
2023-12-01

下面要介绍一个在模拟事件和游戏的程序中常用的组件。本节和下节开发一个结构良好、包括多个函数的游戏程序。程序中要使用前面介绍的大多数控制结构。

在赌场上,人人都关心的一个问题就是机会元素(element of chance),也就是赢钱的运气。这个机会元素可以用标准库中的rand函数引入计算机应用程序中。

考虑下列语句:

i=rand();

rand 函数产生O到RAND_MAX之间的整数(这是<stdlib.h>头文件中定义的符号常量)。RAND_MAX的值至少应为32767,也就是两个字节(即16位)所能表示的最大整数值。如果rand函数真的可以随机产生整数,则每次调用rand函数时,o到RAND_MAX之间的每个数出现的机会(chance)或概率(probability)是相等的。

rand函数产生的数值范围可能与特定应用中所要求的数值范围不同。例如,模拟掷硬币的程序只要0(正面)和1(反面),模拟投骰子的程序只要1到6之间的随机整数,视频游戏中预测飞船(有四种类型)下一个类型的程序只要1到4之间的随机整数。

要演示rand函数,我们开发一个程序,模拟投骰子20次并打印每次的值。rand函数的函数原型见<stdlib.h>。我们使用求模运算符(%)和rand函数,如下所示:

rand( ) % 6

产生0到5的整数,称为比例缩放(scaling)。数字6称为比例因子(scalingfactor)。然后我们将产生的数值范围加1。得到所要结果。图3.7确认了产生的结果在1到6之间。

要显示这些数值的出现机会近似均等.图3.8模拟投骰子6000次。1到6的每个数值大约都出现1000次。

从程序输出可见,通过比例缩放和移动,可以利用rand函数真实地模拟投骰子。注意程序用不到switch结构中提供的default case,但我们还是加上default case,这是个良好的编程习惯。第4章数组将介绍如何把整个switch结构转换成简单的单行语句。

// Fig. 3.7: figO307.cpp
// Shifted, scaled integers produced by 1 + rand( ) % 6
#include 
#include 
#include 

int main(){
  for( int i = 1; i <= 20; i ++){
    cout << setw( 10 ) << ( 1 + rand() % 6 );
    if( i % 5 == 0 )
      cout << endl;
    }
    return O;
  }
}

输出结果:

5    5    3    5    5
2    4    2    5    5
5    3    2    2    1
5    1    4    6    4

图3.7  比例缩放和移动“rand()%6”产生的整数

// Fig. 3.8:fig03 08.cpp
// Roll a six-sided die 6000 times
#include 
#include 
#include 

int main(){
  int frequencyl = 0, frequency2 = 0,
  frequency3 = 0, frequency4 = 0,
  frequency5 = 0, frequency6 = 0,
  face;

  forf( int roll = 1; roll <= 6000; roll++ ) {
    ace = 1 + rand( ) % 6;

    switch ( face ) {
      case 1:
        ++frequencyl;
        break;
      case 2:
        ++frequency2;
        break;
      case 3:
        ++frequency3;
        break;
      case 4:
        ++frequency4;
        break;
      case 5:
        ++frequeneyS;
        break;
      case 6:
        ++frequency6;
        break;
      default:
        cout << "should never get here!";
    }
  }

  cout << "Face" << setw( 13 ) << "Frequency"
  << "\n  1" << setw( I3 ) << frequencyl
  << "\n  2" << setw( 13 ) << frequency2
  << "\n  3" << setw( 13 ) << frequency3
  << "\n  4" << setw( 13 ) << frequency4
  << "\n  5" << setw( 13 ) << frequency5
  << "\n  6" << setw( 13 ) << frequency6 << endl;

  return 0;
}

输出结果:

Face    Frequency
1      987
2      984
3      1029
4      974
5      1004
6      1022

图3.8模拟投骰子6000次

测试与调试提示3.1

即使能确切保证程序没有缺陷,也应在switch结 构中用一个default case来找到错误。

再次执行图3.7的程序得到:

5    5    3    5    5
2    4    2    5    5
5    3    2    2    1
5    1    4    6    4

注意打印的数值顺序与上一次完全相同,怎么能算是随机数呢,其实,这种可重复性是rand函数的一个重要特性。调试程序时,这种可重复性提供了证明修改后程序能正确工作的关键。

rand函数实际上产生的是伪随机数(pseudo-randomnumber)。重复调用rand函数产生一系列看上去是随机的数值,但每次执行程序时,这组数值本身是可重复的。一旦程序进行彻底调试之后,就可以调整为在每次执行程序时产生不同的随机数系列。这个过程称为随机化(randomizing),是用标准库函数srand完成的。srand函数取一个unsigned类型的整数参数并内嵌rand函数(即种子),
就可以在每次执行程序时产生不同的随机数系列。

srand函数的使用如图3.9所示。在这个程序中,我们使用数据类型unsigned(unsigned int的缩写)。int值至少占内存的两个字节,可以存正值或负值。unsignediht变量值也至少占内存的两个字节。2字节的unsigned int只能取O到65535的非负值,4字节的unsignediht只能取O到4294967295的非负值,srand函数取unsigned int值作参数。srand函数的函数原型在头文件<stdlibh>新C++标准的cstdlib)中。

下面将程序运行几次并观察结果。注意,只要提供不同的种子,即可在每次执行程序时产生不同的随机数系列。

1 // Fig. 3.9: fig0309.cpp
// Randomizing die-rolling program
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
int main(){
  unsigned seed;
  cout << "Enter seed: ";
  cin >> seed;
  srand( seed );
  for ( int i = 1; i <= 10; i++ ) {
    cout << setw( 10 ) << 1 + rand( ) % 6;
    if ( i % 5 == 0 )
      cout << endl;
  }
  return 0;
}

输出结果:

Enter seed:  67
1    6    5    1    4
5    6    3    1    2
Enter seed:432
4    2    6    4    3
2    5    1    4    4
Enter seed:  67
1    6    5    1    4
5    6    3    1    2

图3.9投骰子随机化程序

如果不想每次输入种子值而随机化,则要用如下语句:

srand( time ( 0 ) );

使计算机通过时钟值自动取得种子值。time函数(上述语句中该函数的参数为0)返回当前“日历时间”的秒数,将这个值转换为无符号整数值,作为随机数产生器的种子。time函数的函数原型在<time>(新C++标准的ctime)中。

性能提示 3.2

srand函数只要在程序中调用一次即可得到所需的随机化结果,多次调用是多余的,会降低程序性能。

由rand函数直接产生的值总是取值为:

O≤rand()≤  RAND_MAX

前面介绍了如何用一个语句模拟投骰子,该语句如下所示:

face=1+rand() % 6;

总是对变量face指定1≤face≤6的整数(随机)。注意这个范围的宽度(即构成该范围的连续整数的个数)为6并从1开始。从上述语句可以看出,范围宽度是由求模运算符比例缩放rand的数值(6)确定的,开始值等于rand%6中加进的数值(即1)。可以将这个结果一般化,如下所示:

n = a + rand()  %b;

其中a是位移值(等于所要的连续整数范围的开始值),b是比例因子(即由连续整数构成的该范围的宽度)。练习中将介绍如何从一组非连续整数中随机选择整数。

常见编程错误3.16

用srnd函数代替rand函数产生随机数是个语法错误,因为srnd函数不返回值。