对拍

紫书P121以及参考链接https://blog.csdn.net/wlx65003/article/details/51149196?utm_source=blogxgwz3

随机数发生器。

核心函数是cstdlib中的rand(),生成一个闭区间[0,RAND_MAX]内的均匀随机整数,其中RAND_MAX至少为32767(\(2^{15}-1\)),在不同环境下的值可能不同。这里的随机数是“伪随机数”,因为它也是由数学公式计算出来的,不过在算法领域,多数情况下可以把它当作真正的随机数。

如何产生[0,n]之间的整数呢?

方法一:使用rand()%n产生区间[0,n-1]内的一个随机整数。

整数出现的均匀分布不一定能够保证了,而且RAND_MAX很有可能只有32767这么小,只要n大于RAND_MAX,就不能得到期望的结果了。

方法二:执行rand()之后先除以RAND_MAX得到[0,1]之间的随机实数,扩大n倍之后四舍五入,得到[0,n]之间的均匀整数。

这样,在n很大时“精度”不好(好比把小图放大后会看到“锯齿”),但对于普通的应用,这样做已经可以满足要求了。如果坚持需要更高的精度,可以采取多次随机的方法。

提示5-18总结,cstdlib中的rand()可生成闭区间[0,RAND_MAX]内均匀分布的随机整数,其中RAND_MAX至少为32767.如果要生成更大的随机整数,在精度要求不太高的情况下可以用rand()的结果“放大”得到。

需要随机数的程序在最开始一般会执行一次srand(time(NULL)),目的是初始化“随机数种子”。time函数返回的是自UTC时间1970年1月1日0点以来经过的“秒数”,因此每秒才变化一次。

简单地说,种子是伪随机数计算的依据。种子相同,计算出来的“随机数”序列总是相同。如果不调用srand而直接使用rand(),相当于调用过一次srand(1),程序每次执行时,将得到同一套随机数。

不同的编译器计算随机数的方法可能不同。如果是不同编译器编译出来的程序,即使是用相同的参数调用srand(),也可能得到不同的随机序列。

无论怎样,只在程序开头调用一次srand(),而不要在同一个程序中多次调用。

对于“同一套随机数”,可能是好事也可能是坏事。

若要反复测试程序对不同随机数据的响应,需要每次得到的随机数不同。如果程序是由操作系统自动批量执行的,可能因为每次运行的间隔时间过短,导致在相邻若干次执行时time的返回值全部相同。一个解决办法是在测试程序的主函数中设置一个循环,做足够多次测试后再退出。

若发现某程序对于一组随机数据报错,就需要在调试时“重现”这组数据,这时,“每次相同的随机序列”就显得十分重要了。

以UVa12657为例,编写第一版随机数发生器如下:

// n,m两个数都在1-100000之间
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
using namespace std;
const int MAXN = 100010;
int num[MAXN];
void fill_random_int(int n, int cnt) { // 产生1-n之间的整数 
	for (int i = 0; i < cnt; i++) {
		int temp = (int)(((double)rand() / RAND_MAX) * (n - 1) + 0.5) + 1;
		num[i] = temp;
	}
	return;
} 

void fill_random_int2(int n, int cnt) {  // 产生0-n之间的整数 
	for (int i = 0; i < cnt; i++) {
		int temp = (int)(((double)rand() / RAND_MAX) * n + 0.5);
		num[i] = temp;
	}
	return;
} 


int main() {
	srand(time(NULL));
//	printf("%d\n", RAND_MAX);  打印出来是32767 
	int n = 10;
	int cnt = 2;
	fill_random_int(n, cnt);
	for (int i = 0; i < cnt; i++) {
		printf("%d ", num[i]);
	}
	printf("\n");
	int first = num[0]; int second = num[1];
	for (int i = 0; i < second; i++) {
		fill_random_int(4, 1);
		int op = num[0];
		if (op == 4) {
			printf("4\n");
		} else {
			fill_random_int(first, 2);	
			while (num[0] == num[1]) {
				fill_random_int(first, 2);	
			}
			printf("%d %d %d\n", op, num[0], num[1]);
		}
	}
	return 0;
} 

编写不带循环的一次性对拍程序如下简易对拍程序.bat

randUVa12657.exe > data.in

UVa12657-tl.exe < data.in > mydata.out
UVa12657-2.exe < data.in > stdout.out

fc mydata.out stdout.out

对拍一下就会发现跑不出来,卡在这里一直不结束,这下就可以拿着生成的测试数据来debug了。

1613450391320.png

带循环的版本:

@echo off
:loop
	randUVa12657.exe > data.in

	UVa12657-tl.exe < data.in > mydata.out
	UVa12657-2.exe < data.in > stdout.out

	fc mydata.out stdout.out

if not errorlevel 1 goto loop
pause
goto loop

首先@echo off 是关掉输入显示,可以有也可以没有看喜好。
:loop是定位标记点,和c语言里的goto很像。
中间是主体程序。
if not errorlevel 1 goto looperrorlevel 是上一个命令的返回值,fc在文件不同时返回1,相同时返回0,这一行的意思就是,如果fc返回的不是1,就跳到:loop,使劲循环。
pause,暂停,一旦fc返回1,就会执行到这一行,停住程序,给时间看数据。
goto loop,看完数据,按下任意键结束暂停,继续循环。

答案不一致的情况。

1613450972552.png

超时卡住的情况。

1613451059411.png

然而这样还不够,time(NULL)是一秒更新一次,随机数据一秒换一次,对于批处理执行来说会比较慢。有变得更快的随机数种子,就是windows自带的随机数发生器:%random%,它的值就是一个随机整数,可以在命令行里调用,作为随机数种子。

传的方法就是main函数的两个参数int argc, char *argv[]. argc是参数个数,*argv[]是参数表,下标从1开始。

因此要修改随机数生成器以及bat文件。

:loop
	randUVa12657.exe %random% > data.in

	UVa12657-tl.exe < data.in > mydata.out
	UVa12657-2.exe < data.in > stdout.out

	fc mydata.out stdout.out

if not errorlevel 1 goto loop
pause
goto loop
// n,m两个数都在1-100000之间
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream> 
using namespace std;
const int MAXN = 100010;
int num[MAXN];
void fill_random_int(int n, int cnt) { // 产生1-n之间的整数 
	for (int i = 0; i < cnt; i++) {
		int temp = (int)(((double)rand() / RAND_MAX) * (n - 1) + 0.5) + 1;
		num[i] = temp;
	}
	return;
} 

void fill_random_int2(int n, int cnt) {  // 产生0-n之间的整数 
	for (int i = 0; i < cnt; i++) {
		int temp = (int)(((double)rand() / RAND_MAX) * n + 0.5);
		num[i] = temp;
	}
	return;
} 
stringstream ss;

int main(int argc, char* argv[]) {
	int seed = time(NULL);
	if (argc > 1) { // 如果有参数 
		ss.clear();
		ss<<argv[1];
		ss>>seed; // 把参数转换成整数赋值给seed 
	}
	srand(seed);
//	printf("%d\n", RAND_MAX);  打印出来是32767 
	int n = 10;
	int cnt = 2;
	fill_random_int(n, cnt);
	for (int i = 0; i < cnt; i++) {
		printf("%d ", num[i]);
	}
	printf("\n");
	int first = num[0]; int second = num[1];
	for (int i = 0; i < second; i++) {
		fill_random_int(4, 1);
		int op = num[0];
		if (op == 4) {
			printf("4\n");
		} else {
			fill_random_int(first, 2);	
			while (num[0] == num[1]) {
				fill_random_int(first, 2);	
			}
			printf("%d %d %d\n", op, num[0], num[1]);
		}
	}
	return 0;
} 
posted @ 2021-02-16 13:04  Mo_hw  阅读(74)  评论(0编辑  收藏  举报