简介
对拍是一种对学竞赛的同学非常有用的debug技能。在做题时,你肯定会遇到这样的情况:
???我不是过了样例吗???为什么WA了2个点???代码好像没什么问题啊???
辣鸡评测姬
这个时候你就需要对拍来调试程序了。
对拍,说白了就是拿一个输入数据分别让你写的程序和标程(暴力程序)跑一遍,比较输出的数据。
对拍一般有以下3个步骤:
- 生成一组输入数据
- 把这组数据分别让两个程序运行一遍,生成输出数据
- 比较两组输出数据
那么实现对拍,我们需要:
- 你的程序
- 标程
- 数据生成器
- 对拍脚本(批处理脚本)
如果你懒得看下面的内容可以直接在GitHub下载程序。
本文仅适用于Windows系统(虽然考试大多在Linux上)。
批处理脚本
假设你已经写好了一个数据生成器并编译成了rand.exe
,那么我们可以把它的输出重定向到文件std.in
:1
rand.exe > std.in
当然你也可以直接在源码里重定向输入输出流,不过这样比较麻烦,所以一般不采用这种做法。
输入重定向也类似,假设my.exe
是你的程序,std.exe
是标程:1
2my.exe < std.in
std.exe < std.in
然后再把输出重定向到文件里:1
2my.exe < std.in > my.out
std.exe < std.in > std.out
接下来就是比较了,Windows自带一个fc
命令,用于比较两个文件:1
fc my.out std.out /n
当两个文件有差异时,会显示不同处附近的几行文本。/n
参数会显示行号。
这样我们就可以写出一个对拍脚本了:1
2
3
4
5
6
7
8
9@echo off
:loop
rand.exe > std.in
my.exe < std.in > my.out
std.exe < std.in > std.out
fc my.out std.out /n
if not errorlevel 1 goto loop
pause
goto loop
@echo off
的作用是关掉输入显示;:loop
的作用与C中的goto
类似,标号loop
;errorlevel
是上一个命令的返回值,fc
在文件相同时会返回0
,不同时返回1
,因此if not errorlevel 1 goto loop
的意思是:如果fc
返回的不是1
,就跳转到loop
(相当于循环);
一旦返回值为1
,脚本就会以pause
命令暂停,让你可以看数据;
最后goto loop
,继续循环。
把这个脚本命名为check.bat
,可以直接运行。
新鲜的炒栗
比如P1001 A+B Problem这道难题,我们先写出标程:1
2
3
4
5
6
7
8
int main() {
int a, b;
std::cin >> a >> b;
std::cout << a + b << std::endl;
return 0;
}
这是不确定对不对的程序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main() {
int a, b, sum = 0, carry = 0;
std::cin >> a >> b;
do {
sum = a ^ b;
carry = (a & b) << 1;
a = sum;
b = carry;
}
while (carry);
std::cout << sum << std::endl;
return 0;
}
数据生成器怎么写呢?
头文件stdlib.h
中有一个rand()
函数,可以生成0
到RAND_MAX
之间的一个随机整数,RAND_MAX
定义在stdlib.h
中,其值为2147483647
。
由于题目要求有负数,因此可以再生成一个随机数来设置符号:1
2
3
4
5
6
7
8
9
10
int main() {
const int p = 1000000000;
int a = rand() % p, b = rand() % p;
a = (rand() % 2 == 0 ? -a : a), b = (rand() % 2 == 0 ? -b : b);
std::cout << a << " " << b << std::endl;
return 0;
}
但是,上面的程序重新运行后生成的随机数相同,因为随机数种子相同,因此我们需要用srand()
函数设置种子。
一般都使用time(NULL)
的返回值作为种子(对于C++ 11,建议使用time(nullptr)
),time()
函数包含在time.h
头文件中:1
2
3
4
5
6
7
8
9
10
11
12
int main() {
const int p = 1000000000;
srand(time(nullptr));
int a = rand() % p, b = rand() % p;
a = (rand() % 2 == 0 ? -a : a), b = (rand() % 2 == 0 ? -b : b);
std::cout << a << " " << b << std::endl;
return 0;
}