算法竞赛宝典(第一部):语言及算法入门
上QQ阅读APP看书,第一时间看更新

产生随机数据

有些测试数据必须手工制作,以防止某些特例的出现,但是除此之外的多数测试数据,如果能写一个程序自动产生该有多好!这显然需要随机数函数的帮忙了。

【例题描述】 趣味摇奖机

魔法学院举行游园活动,为了烘托节日气氛,决定在活动中设一个如图3.7所示的趣味摇奖机,游戏规则如下:计算机在0~9十个数字中随机取出一个数,由学生去猜,猜中的获特等奖,相差一个数的,获一等奖,相差两个数的,获二等奖,相差三个数的获三等奖,其余的没有奖。现在,请你来编写这个程序实现这个功能。

图3.7

参考程序如下所示:

1 //最容易实现的随机数
2 #include <iostream>
3 #include <stdlib.h>      //C语言中常用的头文件,包含了常用的系统函数
4 #include <time.h> //必须使用time类的头文件
5 using namespace std;
6 
7 int main()
8 {
9   int i,j;
10   int t=time(0)%10; //以时间作为随机函数
11 
12   cout<<"  **趣味摇奖机*** \n\n";
13   cout<<"请任选一个数字(0-9): ";
14 
15   cin>>j;
16 
17   if(j<0 || j>9)
18     return 0;
19 
20   if(j==t)
21    cout<<"\n哇,特等奖!你真厉害!";
22   else if(abs(j-t)<=1)
23    cout<<"\n一等奖!很不错呀!";
24   else if(abs(j-t)<=2)
25    cout<<"\n二等奖!也可以啦...";
26   else if(abs(j-t)<=3)
27    cout<<"\n三等奖!还要努力哦...";
28   else
29    cout<<"\n真可惜!什么都没有...";
30   system("pause");
31   return 0;
32 }

哈哈,我得了N个特等奖,获得了N个高阶的魔法卷轴。这是因为我发现这个程序通过获取当前时间来生成随机数的方法有一定的规律性,我只要估算好时间,就可以算出下一个数的大致范围。

上面的程序中产生的随机数,一般称为伪随机数,真随机数的使用方法如下例所以(C++已默认包含time类的函数):

1 //真随机数
2 #include <iostream>
3 using namespace std;
4 #define n 100
5 int a[101],i;
6 
7 int main()
8 {
9  srand((unsigned)time(NULL));   //获取随机数种子,rand()产生0~32767之间的数
10  for(i=1;i<=n;i++)
11   a[i]=rand() % 100;
12  for(i=1;i<=n;i++)
13   cout<<a[i]<<' ';
14  getchar();
15  return 0;
16 }

生成0~1之间的随机整数:

1 //产生0~1之间的随机数
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  srand((unsigned)time(NULL));
8  for(int i=1;i<=5000;i++)
9   cout<<rand()% (2)<<' ';
10  system("pause");
11  return 0;
12 }

生成Low和hight范围内的随机整数:

1 //产生指定范围内的随机数
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  int low,hight;
8  srand((unsigned)time(NULL));
9  cin>>low>>hight;        //输入low,hight值,大小不得颠倒
10  for(int i=1;i<=5000;i++)
11   cout<<rand()%(hight-low+1)+low<<' ';
12  system("pause");
13  return 0;
14 }

生成随机字符串:

1 //产生指定长度的随机字符串
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  int i,n,m;                  //输出n行m个字符的随机字符串
8  string str;
9  cin>>n>>m;
10  srand((unsigned)time(NULL));
11  for(i=1;i<=n;i++)
12  {
13   str=""; //清空字符串
14   for(int j=1;j<=m;j++)
15   {
16    int temp=rand()%2; //随机决定输出大写或小写字母
17  if (temp==0)
18     str+=(char)(rand()%(26)+1+64); //'A'=65,'Z'=90
19  else
20   str+=(char)(rand()%(26)+1+96); //'a'=97,'z'=122
21   }
22   cout<<str<<endl;
23  }
24  system("pause");
25  return 0;
26 }

随机数函数产生的数介于0~32767之间,因此,如果需要产生的随机数大于32767,则还需要进一步的处理。想想看,怎么做?

在比赛中,有时写好了一道题却不知道正确与否,对此我们可以写两个不同算法的程序来“对拍”检验。例如针对某道题写好的两个程序分别为program1.cpp和program2.cpp,其中一个程序一般是保证绝对正确但运行效率很低的代码(例如使用了穷举算法),如下例所示:

1 //program1.cpp,此处仅为举例,并不是低效算法
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  freopen("in.txt","r",stdin);         //从in.txt中读取数据
8  freopen("out1.txt","w",stdout);//注意此处为out1.txt
9  int x;
10  cin>>x;
11  cout<<2*x<<endl;
12 }

第二个程序是优化后准备最终上交的程序,但不确定程序是否正确。

1 //program2.cpp,第二个程序,此处仅为举例
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  freopen("in.txt","r",stdin);        //读文件名相同,即in.txt
8  freopen("out2.txt","w",stdout);//注意此处为out2.txt
9  int x;
10  cin>>x;
11  cout<<x+x<<endl;//此处用了另一种方法计算结果
12 }

再根据题目要求写出随机生成测试数据的程序CreatRand.cpp:

1 //产生随机数据
2 #include <iostream>
3 using namespace std;
4 
5 int main()
6 {
7  freopen("in.txt","w",stdout);      //产生的随机数据写入in.txt
8  srand((unsigned)time(NULL));
9  cout<<rand()%1000<<endl;//随机产生一个数
10 }

将此三个源程序均编译成可执行文件,并放在同一文件夹下。在该文件夹下再用记事本等纯文本编辑软件编写如下的“对拍”批处理文件例如test.bat。

1 @echo off
2 :start
3 CreatRand
4 
5 program1
6 program2
7 fc out1.txt out2.txt>result.txt
8 :end
9 goto start

如图3.8所示,双击运行批处理文件即可自动评测。

图3.8

由于使用随机数生成程序能自动产生无穷无尽的测试数据,所以批处理文件的第7行是把fc的结果写入了result.txt文件中。运行时,一旦发现结果不一样,就把错误结果记录到result.txt文件中。