数据对拍是一种通过找到错误输出数据寻找bug的方法;
首先,我们可以跟据题意通过bfs,暴力等方法写出一份正确的代码,然后写个随机生成数据和验证输入输出的代码
我们可以拿一道题来熟悉这个流程:
题意很好理解,就是给出起点,和终点,求出起点到终点的所需的步数,其中上下左右,斜着走八个方向都算一步;
大致思路就是,优先斜着走,因为斜着走即在水平方向有位移,在竖直方向也有位移;
这里有一份根据该思路的错误代码:
//01.cpp
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
int main()
{
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
int dx = abs(c - b);
int dy = abs(d - b);
printf("%d\n",max(dx,dy));
return 0;
}
1.这时候如果我们想用数据对拍来找bug的话我们可以写一个用bfs思路的正确代码,以下是bfs的正确代码;
//02.cpp
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int INF = 0x3f3f3f3f;
struct node
{
int x,y,step;
node ()
{
}
node(int p_x,int p_y,int p_step)
{
x = p_x;
y = p_y;
step = p_step;
}
};
int dir[9][2] = {1,0,0,1,-1,0,0,-1,1,1,1,-1,-1,-1,-1,1};
int n,m;
int mark[305][305];
int main()
{
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
queue <node> que;
que.push(node(a,b,0));
int res = 0;
while(!que.empty())
{
node fro = que.front();
if(fro.x == c && fro.y == d)
{
res = fro.step;
break;
}
que.pop();
for(int i = 0; i < 8; i++)
{
int tx = fro.x + dir[i][0];
int ty = fro.y + dir[i][1];
if(tx >= -100 && tx <= 100 && ty >= -100 && ty <= 100 && mark[tx+105][ty+105] == 0)
{
mark[tx + 105][ty + 105] = 1;
que.push(node(tx,ty,fro.step + 1));
}
}
}
printf("%d\n",res);
return 0;
}
2.如果我们想通过验证输入数据来找出错误数据的话,我们需要讲以上两份代码的输入输出重定向到文件流。
也就是把输入的数据通过文件输入,并将输出的数据输出到文件;
以下:
//01.cpp
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
int main()
{
freopen("input0.in","r",stdin);
freopen("ouput1.out","w",stdout);
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
int dx = abs(c - b);
int dy = abs(d - b);
printf("%d\n",max(dx,dy));
fclose(stdin);
fclose(stdout);
return 0;
}
//02.cpp
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int INF = 0x3f3f3f3f;
struct node
{
int x,y,step;
node ()
{
}
node(int p_x,int p_y,int p_step)
{
x = p_x;
y = p_y;
step = p_step;
}
};
int dir[9][2] = {1,0,0,1,-1,0,0,-1,1,1,1,-1,-1,-1,-1,1};
int n,m;
int mark[305][305];
template <typename T>
T RandomData(T a, T b,T c)
{
if(c <= b )
{
return rand() % (b - a + 1) + a;
}
else
{
T temp;
while(true)
{
temp = rand() % (b - a + 1) + a;
if(temp != c)
{
return temp;
}
}
}
}
int main()
{
freopen("input0.in","r",stdin);
freopen("output2.out","w",stdout);
int a,b,c,d;
scanf("%d %d %d %d",&a,&b,&c,&d);
queue <node> que;
que.push(node(a,b,0));
int res = 0;
while(!que.empty())
{
node fro = que.front();
if(fro.x == c && fro.y == d)
{
res = fro.step;
break;
}
que.pop();
for(int i = 0; i < 8; i++)
{
int tx = fro.x + dir[i][0];
int ty = fro.y + dir[i][1];
if(tx >= -100 && tx <= 100 && ty >= -100 && ty <= 100 && mark[tx+105][ty+105] == 0)
{
mark[tx + 105][ty + 105] = 1;
que.push(node(tx,ty,fro.step + 1));
}
}
}
printf("%d\n",res);
fclose(stdin);
fclose(stdout);
return 0;
}
可以看到,两份代码都是通过input0.in输入数据的,这里的input0.in的数据我们可以通过第3步的随机数据来获得。然后错误
代码的数据输出到output1.out里面,正确代码的数据输出到output2.out里面。
3.我们需要造出随机数据来作为正确代码和错误代码的输入,然后对别两份代码的输出文件进行寻找错误数据
如下:
//03.cpp
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<random>
#include<algorithm>
#include <chrono>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
using namespace chrono;
const int INF = 0x3f3f3f3f;
mt19937 rng; //声明一个随机生成器
template <typename T>
T RandomData(T a, T b,T c)
{
uniform_int_distribution<T> dist6(a, b); //指定范围
if(c > b)
{
return dist6(rng);
}
else
{
T temp;
while(true)
{
temp = dist6(rng);
if(temp != c)
{
return temp;
}
}
}
}
void makeData()
{
FILE* fp=fopen("input0.in","w");
int a = RandomData(-100,100,INF);
int b = RandomData(-100,100,INF);
int c = RandomData(-100,100,INF);
int d = RandomData(-100,100,INF);
fprintf(fp,"%d %d %d %d\n",a,b,c,d);
printf("%d %d %d %d\n",a,b,c,d);
fclose(fp);
}
int main()
{
rng.seed(time(0)); //将时间作为随机生成器随机种子
for(int i = 1; i < 500; i++)
{
makeData();
system("01.exe");
system("02.exe");
printf("Number:%d\n",i);
if(system("fc output1.out output2.out"))
{
printf("Wrong Asnwer\n");
while(1); //使程序暂停1毫秒
}
}
return 0;
}
该代码会造出499组数据用来通过验证两个输出文件的差异来寻找bug;
当有差异的话该程序会自动停下并输出有差异的数据
请注意03.cpp中的53行代码和63行代码,47行是为了给随机函数通过当前时间设置一个随机种子,57行是通过使程序“休眠”
1毫秒使得随机种子起作用。
注意,在运行03.cpp之前,必须保证
1.三个cpp文件必须在同一文件夹下
2.01.cpp和02.cpp必须通过编译
如果想要生成小数的话,随机函数应该这样写
//03.cpp
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<random>
#include<algorithm>
#include <chrono>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
using namespace chrono;
const double INF = 0x3f3f3f3f;
std::mt19937 rng; //声明一个随机生成器
template <typename T>
T RandomData(T a, T b,T c)
{
uniform_real_distribution<T> dist6(a, b); //指定范围
if(c <= b)
{
return dist6(rng);
}
else
{
T temp;
while(true)
{
temp = dist6(rng);
if(temp != c)
{
return temp;
}
}
}
}
int main()
{
rng.seed(time(0));
while(true)
{
printf("%lf\n",RandomData(-10.0,25.0,INF));
}
return 0;
}
如果想生成随机字符应该这样写
//03.cpp
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<random>
#include<algorithm>
#include <chrono>
#define ll long long
#define mmset(a,b) memset(a,b,sizeof(a))
using namespace std;
using namespace chrono;
const double INF = 0x3f3f3f3f;
std::mt19937 rng; //声明一个随机生成器
template <typename T>
T RandomData(T a, T b,T c)
{
uniform_int_distribution<T> dist6(a, b); //指定范围
if(c <= b)
{
return dist6(rng);
}
else
{
T temp;
while(true)
{
temp = dist6(rng);
if(temp != c)
{
return temp;
}
}
}
}
int main()
{
rng.seed(time(0));
while(true)
{
printf("%c",RandomData('a','y','c'));
}
return 0;
}