对拍(C++)

对拍是什么

​ 众所周知,当我们正在考试敲代码的时候,每一道题,都会有某种正解能拿到满分;当我们想不出正解时,我们往往可以打暴力代码来骗分。

​ 但是,当我们有思路写正解,但又担心自己正解写的不对,而恰好,我们又有一个能够暴力骗分的代码。这个时候就可以用到对拍。 暴力骗分代码必须有正确性,最多只是超时。

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

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


对拍的实现

1.准备基本代码

​ 首先,我们要有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 res=0;
int i;
for(i=1;i<=a;i++)
res++;
for(i=1;i<=b;i++)
res++;
printf("%d\n",res);
return 0;
}

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

2.制作数据生成器

​ 我们制作的数据要求格式和上面两份代码的输入格式一样。根据上面,我们可以知道输入的数据为2个数,中间有空格分隔。那么,我们的数据生成器就要输出2个数,中间也要用空格分隔。

#include<cstdio>
#include<cstdlib>
#include<ctime>
int main()
{
srand(time(0));
//这是一个生成随机数随机种子,需要用到 ctime 库
printf("%d %d\n",rand(),rand());
//这样就生成了2个随机数
return 0;
}

​ 运行一下,果然生成了2个随机数。

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

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

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

​ 对于任意数,\(0\leq rand()\%(a+1) \leq a\) 。

​ 于是 \(0+k\leq rand()\%(a+1) +k\leq a+k\)。

​ 举几个简单的例子。

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

  2. a=rand()%2+1 时,a 的范围:\(1 \leq a \leq 2\) 。

  3. 要想让 \(1 \leq a \leq 30000\) ,则 a=rand()%30000+1

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

    比如让 \(1 \leq a \leq 1,000,000\)

    int main()
    {
    srand(time(0));
    while(1)
    {
    int a=rand()%1000+1;
    int b=rand()%990+1;
    // a的最大值 × b的最大值=990000
    int c=rand()%10000+1;
    //a*b+c 刚好凑个1000000
    int d=a*b+c;
    cout<<d<<endl;
    }
    }

    看一下输出结果

    怎么样,是不是很神奇呢(滑稽)

3.对拍代码

​ 在这里,我们需要用到一些文件的读写符号。(需用到 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")
#include<cstdio>
#include<cstdlib>
#include<ctime>
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;
}

4.运行对拍程序

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

​ 并且打开每一份代码,让每一份代码都生成一个同名的 .exe 程序。如下:

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

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

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

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

插入图片7

​ 这是存在差异的情况。

5.程序的美化

​ 众所周知,每一道编写程序题都有时间限制。那么我们可以用一个计时函数,来计算我们写的正解用的时间,判断它是否超时,并把所用时间在对拍程序上体现出来。

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

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
int ok=0;
int n=50;
for(int i=1; i<=n; ++i)
{
system("make.exe > make.txt");
system("std.exe < make.txt > std.txt");
double begin=clock();
system("baoli.exe < make.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 Enough 用时 %.0lfms\n",i,t);
}
else
{
printf("测试点#%d Accepted 用时%.0lfms\n",i,t);
ok++; //AC数量+1
}
}
cout<<endl;
double res=100.0*ok/n;
printf("共 %d 组测试数据,AC数据 %d 组。 得分%.1lf。",n,ok,res);
}

​ 上面造了50个测试点,我们还可以看看程序 AC 多少个点来评个总分。(当然这只是起一些美化作用,让对拍看起来更舒服)

​ 这样子,是不是感觉很有意思呢(滑稽*2)


总结

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

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

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

​ 总之,对拍是个比较实用的工具,考试必备神器,一定要掌握它。

希望大家在2020NOIP中发挥超常,RP++!

EdisonBa

2020.8.15

最新文章

  1. [转载]Matlab之静态文本多行输出
  2. C#下取得Exif中照片拍摄日期
  3. [Asp.net mvc] 在Asp.net mvc 中使用MiniProfiler
  4. Java中HashMap排序
  5. UVa 1595 (水题) Symmetry
  6. netbeans 将项目打包生成单个可执行的 jar
  7. EJB学习笔记
  8. CSS-负边距原理
  9. HTML5的localStorage和sessionStorage
  10. 再好好聊聊 HTTP 里的 Cookie | 实用 HTTP
  11. windows笔记本触摸板的快捷键教程
  12. windows WebStorm常用快捷键记录,常用的都在这儿找扒
  13. python定时脚本判断服务器内存
  14. redis主从复制几种结构
  15. 使用docker安装tomcat服务
  16. 架构师成长之路6.1 DNS理论
  17. 软件工程 BUAAMOOC项目Postmortem结果
  18. Ubuntu之网易云音乐无法启动
  19. 【git】 linux 环境安装git
  20. 第1章 Linux命令行简介

热门文章

  1. 超简单的jq图片上传
  2. Java应用服务器之tomcat session server msm搭建配置
  3. windows 下部署 .netcore 到 iis
  4. idea2020安装教程
  5. 96年/离职8个月/拒绝华为offer/目前自由职业-记这大半年来的挣扎与迷茫
  6. python线程,进程,队列和缓存
  7. springboot+quartz以持久化的方式实现定时任务
  8. arcgis for js 如何用contains过滤数据
  9. Python日历模块
  10. PHP fmod() 函数