1. 程式人生 > >C++ 對拍詳解

C++ 對拍詳解

# 對拍(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.