在竞赛中对拍程序的书写
对拍,就是用一个随机数生成程序不停地生成数据,把数据放到一个暴力/标程和比较快的不知道是不是正解的程序中,比较输出。来测试近似正解的正确性。
比如我有这样的一个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); }
好神啊
我把他们放在一个文件夹中,并编译了
如图
然后我新建了一个数据生成器
#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); }
运行duipai.bat
竟然错了。。。。
你可以看到目录里多了3个文件
打开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); }
再对拍
你会发现根本停不下来(可以加 @echo off 不让它刷屏)
于是直接ctrl+c大法
直接输入Y就停止了
交到OJ上
这就是对拍的样例了
the end