首页 > 笔记 > 在竞赛中对拍程序的书写

在竞赛中对拍程序的书写

对拍,就是用一个随机数生成程序不停地生成数据,把数据放到一个暴力/标程和比较快的不知道是不是正解的程序中,比较输出。来测试近似正解的正确性。

比如我有这样的一个a+b

#include <cstdio>
using namespace std;
int a,b;
main()
{
	scanf("%d%d",&a,&b);
	for (int i=1;i<=100;i++)
		if (a==i)
		{
			a--;
			break;
		}
	printf("%d",a+b);
}

虽然很有个性,但交上wa了

于是我从网上找了个正解

#include <cstdio>
using namespace std;
int a,b;
main()
{
	scanf("%d%d",&a,&b);
	printf("%d",a+b);
}

好神啊

我把他们放在一个文件夹中,并编译了

images

如图

然后我新建了一个数据生成器

#include <cstdio>
#include <ctime>
#include <cstdlib>
using namespace std;
main()
{
	srand(time(0));
	int x=rand()%201,y=rand()%201;
	printf("%d %d",x,y);
}

随机数

先埋种子,用的现在的时间做种子。

srand(time(0));

要产生一个[a,b]之间的随机整数x

x = rand()%(b-a+1)+a

然后就是写对拍程序了

那么牵扯到三个步骤:

1生成一组输入数据
2把这组数据分别给两个程序运行,并生成两组输出数据
3比较两组输出数据

首先新建一个批处理文件,命名为 duipai.bat,什么你不知道怎么新建?右键新建一个文本文档直接把后缀名改成bat就好啦,因为批处理文件本质上就是一堆命令文本嘛。

然后右键—编辑,开始打代码:

首先第一步:生成一组输入数据。

我们已经写好了一个数据生成器,编译成data.exe并放在当前目录下了,那么我们只要把这个程序的输入重定向到一个文件就行了,如果你直接在源码里操作,还得各种文件流重定向烦得要死。在批处理命令里很简单,就一句话:

data > input.txt

是不是很简单明了?

那怎么把文件输入到一个程序里去呢?没错:

ac < input.txt
test < input.txt

test.exe是你写的近似正解,ac.exe是标程

那怎么把两个程序的输出再重定向到文件里去呢?也很简单:

ac < input.txt > testout.txt
test < input.txt > acout.txt

是不是相当方便?

接下来就是比较testout.txt和acout.txt了,也不用你手写判断程序,windows自带一个比较命令:fc(file compare)

fc testout.txt acout.txt

这样只能对拍一次,如何循环?

:1
data > input.txt
ac < input.txt > testout.txt
test < input.txt > acout.txt
fc testout.txt acout.txt
if not errorlevel 1 goto 1
pause

:1是定位标记点,和C++里的goto很像。
中间是主体程序。
if not errorlevel 1 goto 1 ,errorlevel 是上一个命令的返回值,fc在文件不同时返回1,相同时返回0,这一行的意思就是,如果fc返回的不是1,就跳到:1,循环。
pause,暂停,一旦fc返回1,就会执行到这一行,停住程序,给你看数据。

现在的功能就很强大了。

你以为就没了?不,这还不够。

再看我们的数据生成程序

#include <cstdio>
#include <ctime>
#include <cstdlib>
using namespace std;
main()
{
	srand(time(0));
	int x=rand()%201,y=rand()%201;
	printf("%d %d",x,y);
}

这样的话有个缺点,time(0) 是一秒才更新一次的,也就是说我们的随机数据一秒才换一次,太慢了!

有没有什么变的更快的随机数种子?有!windows自带了一个随机数发生器:%random%,它的值就是一个随机整数,可以在命令行里调用。

那接下来就好办了,我们把这个数传给data.exe用来当随机数种子就行了。

什么?你不知道怎么传?

呃,你知不知道main函数里这两个参数干嘛用的:int argc, char *argv[]?

恐怕好多人还不知道,我这里解释下,这两个就是传入参数,argc 是参数个数,*argv[] 是参数表,从1开始。

知道了就好办了。

把duipai.bat改成

:1
data %random% > input.txt
ac < input.txt > testout.txt
test < input.txt > acout.txt
fc testout.txt acout.txt
if not errorlevel 1 goto 1
pause

把data改成

#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <sstream>
#define random(a,b) ((a)+rand()%((b)-(a)+1))
using namespace std;

stringstream ss;

int main(int avgc,char *argv[])
{
	int seed=time(0);
	if (avgc)
	{
		ss.clear();
		ss<<argv[1];
		ss>>seed;
	}
	srand(seed);

	int x=random(0,200),y=random(0,200);
	printf("%d %d",x,y);
}

 

images

运行duipai.bat

images

竟然错了。。。。

你可以看到目录里多了3个文件

images

打开input.txt就可以调试了

经过一天

我改完了我的test

#include <cstdio>
using namespace std;
int a,b;
main()
{
	scanf("%d%d",&a,&b);
//	for (int i=1;i<=100;i++)
//		if (a==i)
//		{
//			a--;
//			break;
//		}
	printf("%d",a+b);
}

再对拍

images

你会发现根本停不下来(可以加 @echo off 不让它刷屏)

于是直接ctrl+c大法

images

直接输入Y就停止了

交到OJ上

images

这就是对拍的样例了

the end