对拍是什么#

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

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

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

​这样,我们可以造多组数据,让暴力骗分的程序跑一遍,再让我们自己写的正解跑一遍,二者进行多次对比。如果多组数据都显示二者的输出结果一样,那么这个正解大概率没问题。相反地,如果两组数据不同,我们就找到了一组错误数据,方便调试,找到正解哪里出了问题。

​这便是对拍。其作用也在上文提出。


对拍的实现#

准备基本代码#

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

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

​正解代码: std.cpp

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

​暴力代码:baoli.cpp

#include <cstdio>
using namespace std;
int main()
{
int a, b;
scanf("%d%d", &a, &b);
int ans = 0;
int i;
for (i = 1; i <= a; i++)
ans++;
for (i = 1; i <= b; i++)
ans++;
printf("%d\n", ans);
return 0;
}

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

制作数据生成器#

前置知识:利用 timeb 生成毫秒级别随机数

​我们制作的数据要求格式和上面两份代码的输入格式一样。

​根据上面,我们可以知道输入的数据为 2 个数,中间有空格分隔。那么,我们的数据生成器就要输出 2 个数,中间也要用空格分隔。

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

#include<bits/stdc++.h>
int main()
{
struct _timeb T;
_ftime(&T);
srand(T.millitm);
//生成随机数种子,利用 timeb 生成毫秒级别随机数 printf("%d %d\n", rand(), rand());
//这样就生成了2个随机数
}

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

​注:如果不加那个随机种子,重复执行程序,生成的随机数每次都是一样的数。

Extra:数据范围#

​如果我们对于数据范围有要求,那怎么办呢?

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

​对于任意数,0≤rand()%(a+1)≤a0≤����()%(�+1)≤� 。

​于是 0+k≤rand()%(a+1)+k≤a+k0+�≤����()%(�+1)+�≤�+� 。

​举几个简单的例子:

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

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

  3. 要想让 1≤a≤300001≤�≤30000 ,则 a = rand() % 30000 + 1 。

但是,这里有个小问题。Windows 系统下 rand() 生成的随机数的范围在 0~32767 之间。如果我们想要得到比 32767 更大的随机数怎么办呢?除了换 Unix 系统外,我还有一个小办法,很实用。

比如让 1≤a≤1,000,0001≤�≤1,000,000

#include<bits/stdc++.h>
#define ll long long ll Random(ll mod)
{
ll ans = 2147483647;
return ans = ans * rand() % mod + 1;
} int main()
{
struct _timeb T;
_ftime(&T);
srand(T.millitm); ll n;
while (1)
{
n = Random(1000000);
printf("%lld\n", n);
}
return 0;
}

看一下输出结果

对拍代码#

标准输入输出代码#

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

​在这里,我们需要用到一些文件的读写符号。(需用到 <cstdlib> 库)

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")

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

#include<bits/stdc++.h>
using namespace std;
int main()
{
while (1) //一直循环,直到找到不一样的数据
{
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 时,说明这时数据不一样
break; //不一样就跳出循环
}
return 0;
}

文件输入输出#

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

​因为基本代码中有文件输入输出,所以我们在对拍代码中不必使用 ' < ' 、' > ' 等符号对文件进行操作。只需运行一下两个程序,程序会自己输出文件。

​这种文件输入输出的模式适合 OIer 使用。优点在于对拍的时候不用删除 freopen 。

  1. 数据生成代码 data.cpp 示例:
#include <bits/stdc++.h>
int main()
{
struct _timeb T;
_ftime(&T);
srand(T.millitm); freopen("in.txt", "w", stdout); //生成 使两份基本代码 将要读入的数据
int a = rand(), b = rand();
printf("%d %d\n", a, b);
}
  1. 暴力代码 baoli.cpp 示例:
#include <bits/stdc++.h>
int main()
{
freopen("in.txt", "r", stdin); //读入数据生成器造出来的数据
freopen("baoli.txt", "w", stdout); //输出答案
int a, b, ans = 0;
scanf("%d %d", &a, &b);
for (int i = 1; i <= a; ++i)
ans++;
for (int i = 1; i <= b; ++i)
ans++;
printf("%d\n", ans);
}
  1. 正解代码 std.cpp 示例:
#include <bits/stdc++.h>
int main()
{
freopen("in.txt", "r", stdin);
freopen("std.txt", "w", stdout);
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", a + b);
}
  1. 对拍代码 duipai.cpp 示例:
#include<bits/stdc++.h>
using namespace std;
int main()
{
while (1) //一直循环,直到找到不一样的数据
{
system("data.exe");
system("baoli.exe");
system("std.exe");
if (system("fc std.txt baoli.txt")) //当 fc 返回 1 时,说明这时数据不一样
break; //不一样就跳出循环
}
return 0;
}

运行对拍程序#

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

​再次确保打开每一份代码,编译,让每一份代码都生成一个同名的 .exe 程序。如下:

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

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

​那么我们可以一直拍着,如果长时间都是找不到差异,那么你写的正解就可能是对的了。

​如果找到差异,它会分别返回两个文件的数据,这样我们就有了一组错误数据,方便我们 debug 。

这是存在差异的情况。

美化对拍程序#

​众所周知,每一道编写程序题都有时间限制。那么我们可以用一个计时函数"clock()",来计算我们写的正解用的时间,判断它是否超时(当然,本地测出的时间和评测机测的时间一般不同),并把所用时间在对拍程序上体现出来。

​我们还可以给把一个通过的数据当作一个测试点,还可以给他赋予编号,这些都能在对拍程序直观地体现出来,像下面这样:

#include <iostream>
#include <cstdio>
#include <windows.h>
#include <cstdlib>
#include <ctime>
using namespace std;
int main()
{
int ok = 0;
int n = 50;
for (int i = 1; i <= n; ++i)
{
system("data.exe > in.txt");
system("std.exe < in.txt > std.txt");
double begin = clock();
system("baoli.exe < in.txt > baoli.txt");
double end = clock(); double t = (end - begin);
if (system("fc std.txt baoli.txt"))
{
printf("测试点#%d Wrong Answer\n", i);
}
else if (t > 1000) //1秒
{
printf("测试点#%d Time Limited Exceeded 用时 %.0lfms\n", i, t);
}
else
{
printf("测试点#%d Accepted 用时%.0lfms\n", i, t);
ok++; //AC数量+1
}
}
printf("\n");
double res = 100.0 * ok / n;
printf("共 %d 组测试数据,AC数据 %d 组。 得分%.1lf。", n, ok, res);
}

​上面造了 50 个测试点,我们还可以计算程序 AC 多少个点来评个总分。这样可以让我们大致地了解一下编出的程序的正确性。


总结#

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

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

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

​但是,对拍仅仅能确保自己写的正解能跑过一些比较小的数据。如果数据范围太大,一是暴力的程序跑不出来,二是数据生成的程序需要承受更多的压力。所以,如果想要确保能过大数据,需要自己手动去看一下代码里面是否隐藏着问题,比如中间过程要强转为 long long 等等。

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

我的世界锻造台怎么合成-9335游戏网

C++ 对拍详解 和解读的更多相关文章

  1. oracle监听(lsnrctl)详解解读

    (总结)Oracle监听服务lsnrctl参数及查询状态详解   lsnrctl命令常用参数详解: lsnrctlstart启动指定的监听器 stop关闭指定的监听器 status显示监听器的状态.s ...

  2. 通讯协议序列化解读(一) Protobuf详解教程

    前言:说到JSON可能大家很熟悉,是目前应用最广泛的一种序列化格式,它使用起来简单方便,而且拥有超高的可读性.但是在越来越多的应用场景里,JSON冗长的缺点导致它并不是一种最优的选择. 一.常用序列化 ...

  3. Java集合详解6:这次,从头到尾带你解读Java中的红黑树

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  4. 设计模式相关面试问题-Builder基础详解与代码解读

    java的builder模式详解: 概念:建造者模式是较为复杂的创建型模式,它将客户端与多含多个组成部分(或部件)的复杂对象的创建过程分离. 使用场景:当构造一个对象需要很多参数的时候,并且参数的个数 ...

  5. Maven详解

    一.前言     以前做过的项目中,没有真正的使用过Maven,只知道其名声很大,其作用是用来管理jar 包的.最近一段时间在项目过程中使用Maven,用Maven构建的web项目,其项目结构只停留在 ...

  6. AJAX请求详解 同步异步 GET和POST

    AJAX请求详解 同步异步 GET和POST 上一篇博文(http://www.cnblogs.com/mengdd/p/4191941.html)介绍了AJAX的概念和基本使用,附有一个小例子,下面 ...

  7. 字符编码详解及由来(UNICODE,UTF-8,GBK)[转帖]

    相信許多人對字符編碼都不是很了解,透過下文可以清晰的理解各种字符编码方式详解及由来. 一直对字符的各种编码方式懵懵懂懂,什么ANSI.UNICODE.UTF-8.GB2312.GBK.DBCS.UCS ...

  8. [Maven]Maven详解

    转自:http://www.cnblogs.com/hongwz/p/5456578.html 一.前言     以前做过的项目中,没有真正的使用过Maven,只知道其名声很大,其作用是用来管理jar ...

  9. 【ORM】--FluentNHibernate之AutoMapping详解

           上篇文章详细讨论了FluentNHibernate的基本映射的使用方法,它的映射基本用法是跟NHibernate完全一样的,首先要创建数据库链接配置文件,然后编写Table的Mappin ...

  10. 理论经典:TCP协议的3次握手与4次挥手过程详解

    1.前言 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提供一种面向连接的.可靠的字节流服务. 面向连接意味着两个使用TCP的应用(通常是一个客户和一 ...

随机推荐

  1. JUC同步锁原理源码解析三----CountDownLatch、CyclicBarrier

    JUC同步锁原理源码解析三----CountDownLatch.CyclicBarrier CountDownLatch.CyclicBarrier的来源 1.CountDownLatch的来源 A ...

  2. 函数接口(Functional Interfaces)

    定义 首先,我们先看看函数接口在<Java语言规范>中是怎么定义的: 函数接口是一种只有一个抽象方法(除Object中的方法之外)的接口,因此代表一种单一函数契约.函数接口的抽象方法可以是 ...

  3. 有哪些ASIC加速技术可以实现低功耗运行?

    目录 文章主题: 10. 有哪些ASIC加速技术可以实现低功耗运行? 背景介绍:随着移动设备.物联网.云计算等应用场景的不断增长,功耗成为了一个日益重要的技术问题.为了在移动设备上实现更长时间的运行, ...

  4. Linux系统运维之subversionEdge部署

    一.介绍 Subversion Edge是Collabnet公司发布的SVN和Apache等组件结合的SVN管理工具.由于安装过subversion+apache,发现添加账户都需要登录服务器改配置, ...

  5. 【笔试实战】LeetCode题单刷题-编程基础 0 到 1【二】

    1822. 数组元素积的符号 题目链接 1822. 数组元素积的符号 题目描述 已知函数 signFunc(x) 将会根据 x 的正负返回特定值: 如果 x 是正数,返回 1 . 如果 x 是负数,返 ...

  6. iOS 百度导航没有语音播报

    1.百度地图没有语音播报 可以尝试如下方式: 1.tts确认相关key正确,可以放入官方demo测试 2.setting中 Product Name 尝试设置成英文,在info.plist设置Bund ...

  7. 使用react-test-renderer/shallow写测试

    我的项目是采用react + ts来写的,项目中要写单元测试,于是采用了Jest库,  主要用的package有 react-test-renderer react-test-renderer/sha ...

  8. 将Dubbo注册到Nacos,与DubboAdmin的部署

    王有志,一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群:共同富裕的Java人 本文是<从 0 开始学 Dubbo>系列文章中应用篇的番外篇. 在这篇文章中我会和大家一起部署 ...

  9. 2021-3-9 保存csv格式文件

    public void SaveCSV(DataTable dt, string fullPath) { FileInfo fi = new FileInfo(fullPath); if (!fi.D ...

  10. asp.net core之日志

    日志记录在应用程序开发中起着至关重要的作用,它可以帮助开发人员诊断和调试问题,同时也是监控和性能优化的重要工具.ASP.NET Core 提供了强大且灵活的日志记录功能,本文将详细介绍ASP.NET ...