C++ 對拍詳解
阿新 • • 發佈:2020-08-15
# 對拍(C++)
## 對拍是什麼
對拍,是一個比較實用的工具。它能夠非常方便地對於兩個檔案進行比較,可以幫助我們實現一些自動化的問題。
眾所周知,每一道程式設計題目,都會有某種正解能拿到滿分;當我們想不出正解時,我們往往可以打暴力程式碼來獲取部分分數。
但是,當我們有思路寫正解,但又擔心自己正解寫的不對,而恰好,我們又有一個能夠暴力騙分的程式碼。這個時候就可以用到對拍。 暴力騙分程式碼必須有正確性,最多隻是超時。
這樣,我們可以造幾組資料,讓暴力騙分程式碼跑一遍,再讓我們自己寫的正解跑一遍,二者對比一下。如果拍出來多組資料都顯示二者的結果一樣,那麼這個正解大概率沒問題。相反地,如果兩組資料不同,我們就找到了一組錯誤資料,方便除錯,找到正解哪裡出了問題。
這便是對拍。其作用也在上文提出。
---
## 對拍的實現
### 1.準備基本程式碼
首先,我們要有2份程式碼,一份是這一道題“你寫的正解”程式碼,另一份是同一道題“你打的暴力”程式碼。
為了方便,我們先用A+B problem來演示對拍。
自己的程式碼: std.cpp
```c++
#include
using namespace std;
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
return 0;
}
```
暴力程式碼:baoli.cpp
```c++
#include
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份程式碼有了,我們把它放在同一個資料夾裡。這樣算是做好了對拍的準備。
![](https://img2020.cnblogs.com/blog/2101156/202008/2101156-20200815165653867-1256484910.png)
### 2.製作資料生成器
我們製作的資料要求格式和上面兩份程式碼的輸入格式一樣。
根據上面,我們可以知道輸入的資料為2個數,中間有空格分隔。那麼,我們的資料生成器就要輸出2個數,中間也要用空格分隔。
```c++
#include
#include
#include
int main()
{
srand(time(0));
//這是一個生成隨機數隨機種子,需要用到 ctime 庫
printf("%d %d\n",rand(),rand());
//這樣就生成了2個隨機數
return 0;
}
```
執行一下,果然生成了2個隨機數。
![](https://img2020.cnblogs.com/blog/2101156/202008/2101156-20200815165702242-1374040412.png)
注:如果不加那個隨機種子,生成的隨機數每次都是一樣的數。
#### 如果我們對於資料範圍有要求,那怎麼辦呢?
要讓隨機數限定在一個範圍,可以採用模除加加法的方式。
對於任意數,$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$
```c++
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< 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")`
```c++
#include
#include
#include
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 程式。如下:
![](https://img2020.cnblogs.com/blog/2101156/202008/2101156-20200815165725693-705820205.png)
然後,開啟 duipai.exe ,我們可以看到程式正在對兩個輸出檔案進行比較
![](https://img2020.cnblogs.com/blog/2101156/202008/2101156-20200815165731056-107143106.png)
找不到差異,說明這兩份程式碼輸出的兩個檔案是一樣的。
那麼我們可以一直拍著,如果長時間都是找不到差異,那麼你寫的正解就可能是對的了。
如果找到差異,它會分別返回兩個檔案的資料,這樣我們就有了一組錯誤資料,方便我們 debug 。
![](https://img2020.cnblogs.com/blog/2101156/202008/2101156-20200815184847095-267125585.png)
這是存在差異的情況。
### 5.程式的優化
眾所周知,每一道編寫程式題都有時間限制。那麼我們可以用一個計時函式"clock()",來計算我們寫的正解用的時間,判斷它是否超時,並把所用時間在對拍程式上體現出來。
我們還可以給把一個通過的資料當作一個測試點,還可以給他賦予編號,這些都能在對拍程式體現出來,像下面這樣:
```c++
#include
#include
#include
#include
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<EdisonBa
2020.