C++对拍小技巧

前言

在各大OI赛制的赛事中,对拍是一个比较实用的工具。它能够非常方便地对于两个程序的输出文件进行比较,可以帮助我们实现一些自动化的比较输出结果的问题。

众所周知,几乎每一道编程题目,都会有某种正解能拿到满分;当我们想不出正解时,我 们往往可以打暴力代码来获取部分分数。

但是,当我们觉得有思路写正解,但又担心自己正解写的不对,而恰好,我们又有一个能够暴力骗分的代码。这个时候就可以用到对拍。 暴力骗分代码必须保证正确性,只是超出时间限制,不能出现答案错误的情况。

翻译成人话就是:用正确但超时的算法去测试不超时但不一定正确的算法

这样,我们可以造多组数据,让暴力骗分的程序跑一遍,再让我们自己写的正解跑一遍, 二者进行多次对比。

如果多组数据都显示二者的输出结果一样,那么这个正解大概率没问题。

相反地,如果两组数据不同,我们就找到了一组错误数据,方便调试,找到正解哪里出了问题。

这便是对拍。

对拍的实现

一、准备基本代码

首先,我们需要有 2 份代码,一份是这一题 “你写的正解” 代码,另一份是同一题 “你打的暴力” 代码。

为了方便,我们使用 A+B problem 来演示对拍。

正解代码: std.cpp

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	cout << a + b;

	return 0;
}

暴力代码: baoli.cpp

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	int ans = 0;
	for (int i = 1; i <= a; i++) {
		ans++;
	}
	for (int i = 1; i <= b; i++) {
		ans++;
	}
	printf("%d\n", ans);
	
	return 0;
}

两份代码有了后,将它们全部编译。我们把它们放在同一个文件夹里。这样算是做好了对拍的准备工作。

二、数据生成器 

2.1 生成数据

我们制作的数据要求格式和上面两份代码的输入格式一样,说白了就是用数据生成器生成用于测试的输入数据。

根据上面,我们可以知道输入的数据为 2 个数,中间有空格分隔。

那么,我们的数据生成器就要输出 2 个数,中间也要用空格分隔。

创建一个新的源代码: data.cpp

#include <bits/stdc++.h>
using namespace std;

int main()
{
    srand(time(0));
    
    int a = rand() % 0217;
    int b = rand() % 1112;
    printf("%d %d", a, b);
    
    return 0;
 }

编译运行一下,确实生成了 2 个随机数:

2.2 数据范围控制

要让随机数限定在一个范围,可以采用 “模除加加法” 的方式。

对于任意数,0 ≤ rand() % (a + 1) ≤ a,

于是就有了 0 + k ≤ rand() % (a + 1) + k ≤ a + k。

举几个简单的例子:

1. 当 a = rand() % 2 时,a的范围: 0 ≤ a ≤ 1 。

2. 当 a = rand() % 2 + 1 时,a的范围:0 + 1 ≤ a ≤ 1 + 1 。

3. 要想让 2 ≤ a ≤ 3000 ,只需要使用 a = rand() % (3000 - 2 + 1) + 2

三、对拍代码

3.1 标准输入输出代码

标准输入输出指的是:两份基本代码和数据生成代码里不含文件输入输出操作,如 freopen 等。

在这里,我们需要用到一些文件的读写符号。

system("A.exe > A.txt") 指的是运行 A.exe,把结果输出(>)到 A.txt 中。

system("B.exe < A.txt > C.txt") 指的是运行 B.exe,从 A.txt 中读入(<)数据,把结果输出(>)到 C.txt 中。

system("fc A.txt B.txt") 指的是比较 A.txt B.txt ,如果两个文件里的数据相同返回 0,不同返回 1。

那么,我们就可以执行这一操作来实现对拍。

1. 先让数据生成器输出数据: system("data.exe > in.txt")

2. 然后用这个数据跑一遍暴力代码,输出结果:

system("baoli.exe < in.txt > baoli.txt")

3. 再用这个数据跑一遍你写的正解代码,输出结果:

system("std.exe < in.txt > std.txt")

4. 把两个结果相比较,判断是不是一样的: system("fc std.txt baoli.txt")

5. 创建一个源代码,命名为 duipai.cpp

#include<iostream>
using namespace std;
int main()
{
    for(int i=0;i<10086;++i) //一直循环,直到找到不一样的数据
    {
        system("data.exe > in.txt");
        system("baoli.exe < in.txt > baoli.txt");
        system("std.exe < in.txt > std.txt");
        if (system("fc std.txt baoli.txt")) //当 fc 返回 1 时,说明这时数据不一样
        {
            cout<<"哈哈哈做错了吧,跟暴力的答案不一样!";
            break; //不一样就跳出循环
        }
    }
    return 0;
}

如果上面的 fc 命令找不到,报错了,说明电脑终端用的是 powershell 那就把 fc 命令换成 comp 即可。

如果你用的是 linux,就要把上面的 fc 命令改写成 diff 命令,逻辑上还是一样的。

目前我们有了 4 份代码,为了实现对拍,我们要把这些代码放在同一个文件夹的同一层里。

然后打开 duipai.exe ,我们可以看到程序正在对两个输出文件进行比较。

找不到差异,说明这两份代码输出的两个文件是一样的。

总结

经过上面的一番讲解,大家一定对 “对拍” 已经有了一些了解。相信大家跟着上面的步骤,也能用对拍来解决一些实际的问题。

在考场上,对于一些 比较容易写出暴力代码而写正解又担心自己写不对的情况,我们可以用自己的暴力代码和写的正解比较一下。(毕竟暴力代码肯定不会WA掉,输出的答案只是慢了些,但答案肯定不会错)这么比较,就可以检查出自己写的正解有没有大问题。

而且,对拍还能方便地计算出任意随机数据所跑的时间,我们可以知道这个程序大约用的时间,我们可以自己再去调试优化。这避免了我们考试时写完代码,但是不知道自己的程序跑大数据非常慢,考试结束交程序评测的时候全是TLE,然后爆了 0 分。(悲)

但是,对拍仅仅能确保自己写的正解能跑过一些比较小的数据。如果数据范围太大: 一是暴力的程序跑不出来,二是数据生成的程序需要承受更多的压力

所以,如果想要确保能过大数据,需要自己手动去看一下代码里面是否隐藏着问题,比如中间过程要强转为 long long 等等。

总之,对拍是个比较实用的工具,它非常方便地对两个文件进行了比较操作,大家一定要好好掌握!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值