随机数据生成与对拍

zbwer

2019-08-31 21:55:11

Personal

# 随机数据生成与对拍 --- ## 引言 在漫长的$OI$生涯中,你肯定遇到过这些情况 + 在OI赛制下,你写了一份你自认为是正解的代码,但是你不确定自己是否考虑到了所有的细节,你不敢轻易提交 + 对于有些无法获得数据的题目,你不知道自己错在了哪里 + 对于能够获得数据的题目,你拿到的数据都是上万级别的数据,难以$Debug$. 这时候,我们就可以试试**随机数据生成与对拍**` --- + 什么是随机数据生成? 顾名思义,~~就是针对我们的需求生成随机的数据~~。比如生成随机的整数序列,生成随机的树,生成随机的图。 + 什么是对拍? 对拍说白了就是对答案,举个栗子:在文化课中,对于同一道填空题,我们在写出自己的答案后,可能会找其他同学对一下答案,看是不是一样的。放在$OI$中,就是对于同一道题目,两个不同的人写了两份不同的程序,再针对同一组数据,看看两份程序的输出结果是否一样。 + 随机数据生成和对拍有什么用呢? 比方说在无法获得实时评测反馈的比赛中($NOIp$),对于一道题目,你思考并实现了一个“高分解法”,但不能确保这个程序是否完全正确,这种情况下,就可以考虑编写一份随机数据生成程序,一份用正确但是会$TLE$的暴力算法写的程序,然后将两个程序对拍,看是否输出结果一致。 ~~要是暴力都不会打,那就输出随机数据骗分吧~~ + 那么如何实现随机数据生成呢? > 参考《算法竞赛进阶指南》P439 先给出随机数据生成的模板,适用于此文中所有的生成数据方法 ```cpp #include <iostream> #include <cstdlib> #include <ctime> using namespace std; int random(int n)//生成一个[0,n-1]范围内的数 { return (long long)rand()*rand()%n; } int main() { freopen("data.in","w",stdout); //将生成的数据输出到名为data.in的文件中 srand((unsigned)time(0)); //具体内容... return 0; } ``` + 生成数据的方法: + 随机生成整数序列 ```cpp //随机生成n(n<=1e5)个绝对值在1e8之间的整数 int n=random(100000)+1,x; for(int i=1;i<=n;i++) printf("%d ",random(x*2+1)-x); putcha('\n');//可有可无 ``` + 随机生成排列 ```cpp //随机生成1~n(n<=1e5)的排列 //STL的方法 int n=random(100000)+1,a[100050]; for(int i=1;i<=n;i++) a[i]=i; random_shuffle(a+1,a+n+1);//库文件algorithm for(int i=1;i<=n;i++) printf("%d ",a[i]); //瞎搞方法 int n=random(100000)+1,bool vis[100050]; for(int i=1;i<=n;i++) { int x=random(n)+1; while(vis[x]==1) x=random(n)+1; vis[x]=1;printf("%d ",x); } ``` + 随机生成若干个区间(常用于数据结构题目的区间操作) ```cpp //随机生成m个[1,n]的子区间 for(int i=1;i<=m;i++) { int l=random(n)+1; int r=random(n)+1; if(l>r) swap(l,r); printf("%d %d\n",l,r); } ``` + 随机生成树 ```cpp //随机生成一棵n个点带边权(<=100000)的树 for(int i=2;i<=n;i++) { int fa=random(i-1)+1; int val=random(100000)+1; printf("%d %d %d\n",fa,i,val); } ``` + 随机生成连通图 ```cpp //随机生成一张n个点,m条边的无向图. pair<int,int> e[];//[]内填写数组大小 map<pair<int,int>,bool> h;//库文件map //先生成一棵树,保证联通 int n=random(具体大小),m=random(具体大小); printf("%d %d\n",n,m); for(int i=1;i<n;i++) { int fa=random(i)+1; e[i]=make_pair(fa,i+1); h[e[i]]=h[make_pair(i+1,fa)]=1; } //在生成剩余的m-n+1条边 for(int i=n;i<=n;i++) { int x,y; do{ x=random(n)+1,y=random(n)+1; }while(x==y || h[make_pair(x,y)]); e[i]=make_pair(x,y); h[e[i]]=h[make_pair(y,x)]=1; } //随机打乱,输出 random_shuffle(e+1,e+m+1);//库文件algorithm for(int i=1;i<=m;i++) printf("%d %d\n",e[i].first,e[i].second); ``` + 随机生成链 ```cpp //生成一条有n个节点的链 int n=random(1000)+1;printf("%d\n",&n); int root=random(n)+1;bool vis[100000]; vis[root]=1;int last=root; for(int i=1;i<n;i++) { int x=random(n)+1; while(vis[x]==1) x=random(n)+1; printf("%d %d\n",last,x); last=x;vis[x]=1; } ``` + 随机生成一张菊花图 ```cpp //生成一条有n个节点的菊花图 int n=random(1000)+1;printf("%d\n",n); int root=random(n)+1; for(int i=1;i<=n;i++) { if(i==root) continue; printf("%d %d\n",root,i); } ``` --- + 那么如何实现对拍呢? 假设此时我们已经编写好了三个程序: 1. 自己编写的“高分解法”程序,名为“sol.cpp”,改程序输出答案到data.out中。 2. 自己编写的“暴力解法”程序,名为“bf.cpp”,该程序输出正确答案到data.ans中。 3. 自己编写的随机数据生成程序,名为“random.cpp”,该程序输出随机数据到data.in中 **依次编译这三个程序,得到三个exe文件**,然后运行如下程序即可。 ```cpp #include <cstdlib> #include <iostream> #include <cstdio> #include <ctime> using namespace std; int main() { for(int T=1;T<=100;T++)//T即为测试数据的组数 { system("random.exe");//运行数据生成的程序 // 获取正解程序的运行时间,Windows下单位为ms double st=clock(); system("sol.exe");//运行正解程序 double ed=clock(); system("bf.exe");//运行暴力程序 if(system("fc data.out data.ans")) //对比正解与暴力程序的输出结果,如果不同 { puts("Wrong Answer"); return 0; } else printf("Accepted, 测试点 #%d, 用时 %.0lfms\n",T,ed-st); //如果输出结果一样,那么输出程序运行时间 } return 0; } ``` --- ### 举个栗子: + 情景:此时我手中有一份95分的代码,和一份$std$,但是手中并没有WA了的那个点的数据 95分代码: ![](https://cdn.luogu.com.cn/upload/image_hosting/485069jk.png?x-oss-process=image/resize,m_lfit,h_170,w_225) 无法获得数据:![](https://cdn.luogu.com.cn/upload/image_hosting/73rg2jld.png) + 这时候就可以利用std,随机数据生成与对拍来找出我这个95分程序的bug + 首先我们先把四个程序写好放在**同一个文件夹** ![](https://cdn.luogu.com.cn/upload/image_hosting/tf2tjrck.png) 其中`bf`是std,`sol`是我的95分程序,`rand`是针对这道题目写出来的随机数据生成器,`对拍`即上文提到的那个模板。 + 接着我们编译运行前三个程序,得到3个exe类型的程序,及两个答案文件,一个输入文件 ![](https://cdn.luogu.com.cn/upload/image_hosting/sj9lgpze.png) + 然后我们编译运行那个名为对拍的程序就可以了,会出现如下结果 ![](https://cdn.luogu.com.cn/upload/image_hosting/3xr7fzcr.png) + 我们可以看到,如果我们的`sol`程序的输出结果和`bf`的输出结果一样,那么它就会输出`Accepted`和我们这个`col`程序对于这一组数据的运行时间 + 而要是有不同的地方,这个对拍程序就会指出在哪里出现差异,这时我们只要打开`data.in`这个文件开始思考自己程序的bug就行了 --- ```cpp 完结撒花 typed by zbwer 2019-08-31 部分代码引用李煜东的《算法竞赛进阶指南》 ```