• 随机数据生成与对拍


    随机数据生成与对拍


    引言

    在漫长的(OI)生涯中,你肯定遇到过这些情况

    • 在OI赛制下,你写了一份你自认为是正解的代码,但是你不确定自己是否考虑到了所有的细节,你不敢轻易提交
    • 对于有些无法获得数据的题目,你不知道自己错在了哪里
    • 对于能够获得数据的题目,你拿到的数据都是上万级别的数据,难以(Debug).

    这时候,我们就可以试试随机数据生成与对拍`


    概念

    • 什么是随机数据生成?

    顾名思义,就是针对我们的需求生成随机的数据。比如生成随机的整数序列,生成随机的树,生成随机的图。

    • 什么是对拍?

    对拍说白了就是对答案,举个栗子:在文化课中,对于同一道填空题,我们在写出自己的答案后,可能会找其他同学对一下答案,看是不是一样的。放在(OI)中,就是对于同一道题目,两个不同的人写了两份不同的程序,再针对同一组数据,看看两份程序的输出结果是否一样。

    • 随机数据生成和对拍有什么用呢?

    比方说在无法获得实时评测反馈的比赛中((NOIp)),对于一道题目,你思考并实现了一个“高分解法”,但不能确保这个程序是否完全正确,这种情况下,就可以考虑编写一份随机数据生成程序,一份用正确但是会(TLE)的暴力算法写的程序,然后将两个程序对拍,看是否输出结果一致。

    要是暴力都不会打,那就输出随机数据骗分吧

    • 那么如何实现随机数据生成呢?

    参考《算法竞赛进阶指南》P439

    先给出随机数据生成的模板,适用于此文中所有的生成数据方法

    #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('
    ');//可有可无 
        ```
    

    随机生成排列

    ```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
    ",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
    ",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,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
    ",e[i].first,e[i].second); 
    ```
    

    随机生成链

    ```cpp
    //生成一条有n个节点的链 
    int n=random(1000)+1;printf("%d
    ",&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
    ",last,x);
    	last=x;vis[x]=1;
    }
    ```
    

    随机生成一张菊花图

    ```cpp
    //生成一条有n个节点的菊花图 
    int n=random(1000)+1;printf("%d
    ",n);
    int root=random(n)+1;
    for(int i=1;i<=n;i++)
    {
    	if(i==root) continue;
    	printf("%d %d
    ",root,i);
    }
    ```
    

    那么如何实现对拍呢?

    假设此时我们已经编写好了三个程序:

    1. 自己编写的“高分解法”程序,名为“sol.cpp”,改程序输出答案到data.out中。
    2. 自己编写的“暴力解法”程序,名为“bf.cpp”,该程序输出正确答案到data.ans中。
    3. 自己编写的随机数据生成程序,名为“random.cpp”,该程序输出随机数据到data.in中

    依次编译这三个程序,得到三个exe文件,然后运行如下程序即可。

    #include <cstdlib>
    #include <iostream>
    #include <cstdio>
    #include <ctime>
    
    using namespace std;
    int main()
    {
    	for(int T=1;T<=100;T++)//T即为测试数据的组数
    	{
    		system("C:\random.exe");//运行数据生成的程序
    		
    		// 获取正解程序的运行时间,Windows下单位为ms
    		double st=clock();
    		system("C:\sol.exe");//运行正解程序 
    		double ed=clock();
    		system("C:\bf.exe");//运行暴力程序
    		if(system("fc data.out data.ans"))
    		//对比正解与暴力程序的输出结果,如果不同 
    		{
    			puts("Wrong Answer");
    			return 0;
    		}
    		else printf("Accepted, 测试点 #%d, 用时 %.0lfms
    ",T,ed-st);
    		//如果输出结果一样,那么输出程序运行时间 
    	}
    	return 0;
    }
    

    举个栗子:

    • 情景:此时我手中有一份95分的代码,和一份(std),但是手中并没有WA了的那个点的数据
      95分代码:

    无法获得数据:

    • 这时候就可以利用std,随机数据生成与对拍来找出我这个95分程序的bug
    • 首先我们先把四个程序写好放在同一个文件夹

      其中bf是std,sol是我的95分程序,rand是针对这道题目写出来的随机数据生成器,对拍即上文提到的那个模板。
    • 接着我们编译运行前三个程序,得到3个exe类型的程序,及两个答案文件,一个输入文件
    • 然后我们编译运行那个名为对拍的程序就可以了,会出现如下结果
    • 我们可以看到,如果我们的sol程序的输出结果和bf的输出结果一样,那么它就会输出Accepted和我们这个col程序对于这一组数据的运行时间
    • 而要是有不同的地方,这个对拍程序就会指出在哪里出现差异,这时我们只要打开data.in这个文件开始思考自己程序的bug就行了

    完结撒花 
    typed by zbwer 2019-08-31
    部分代码引用李煜东的《算法竞赛进阶指南》
    
  • 相关阅读:
    Windows下安装使用OpenLDAP
    LDAP安装配置(windows)
    LDAP概念和原理介绍
    JDK自带的keytool证书工具详解
    递归算法讲解
    Creating an archive from a directory without the directory name being added to the archive
    Overview of the M&A Process
    Make sure base method gets called in C#
    How to delete specific nodes from an XElement?
    如何学习数据结构?
  • 原文地址:https://www.cnblogs.com/fengzi8615/p/11699820.html
Copyright © 2020-2023  润新知