1. 程式人生 > >對資訊學競賽中除錯方法的建議

對資訊學競賽中除錯方法的建議

 

資訊學之於其他競賽學科的不同,就在於需要通過寫程式來表達自己的思維和想法。如何儘可能又快又好地除錯程式,成了我們必須要思考的問題。相信很多同學都有過這樣的經歷:思考一個演算法只花了半個小時,但是把這個演算法寫對卻花了一天。。思考與實現的時間往往不成正比。

下面是我結合自己的經驗給出的一些小建議,僅供大家參考,如果有不太好的地方,也歡迎指正~

關於除錯有一個大前提,就是思考的方向一定得嚴謹正確,因為思考決定實現,如果思考的時候有漏洞,那麼實現的程式肯定也不強健。在想出演算法之後不要急著實現,一定要認真反覆地論證:我的演算法每一步的定義是否嚴謹,是不是哪裡還有漏洞,我的演算法所得到的東西是否就是題目需要求解的

。確認了自己的想法後再開始實現。

假設我們現在寫完了一個程式:

第一步:靜態查錯(俗稱裸眼觀察^_^),即不測試資料,而是通過反覆地看程式碼來檢查。靜態查錯首先要檢查是否有變數名打錯,語法是否正確,你打的程式碼和你的想法是否相符。然後要分析程式碼的邏輯性是否嚴謹正確,是否能在所有情況下都能正確執行。最後還要驗證是否在所有邊界情況都能得到正確的解,包括陣列是否開夠,會不會有n=0的情況等等。靜態查錯是最有效的查錯方法,為什麼有的大牛能夠一遍寫對很複雜的程式碼,因為他們靜態查錯的能力非常之強,並且在敲程式的過程中就已經在靜態查錯了。在實踐中,我建議大家儘量讓自己的程式模組化(即一個函式做一件事情),然後檢查的時候邊看邊打上註釋,這樣能讓自己更清醒地判斷,也方便以後能輕鬆閱讀自己的程式。

第二步:動態查錯,用資料來驗證程式碼的正確性。人眼往往不可靠,我們需要用更安全的資料來判定。一般而言在普通的OI競賽中,時間是比較充裕的,我們有時間來對拍。對拍需要三個程式,一個是你需要提交的標準程式(std),一個是能夠通過部分資料的暴力程式(plain),一個是用來生成資料的程式(mk_data)

關於plain程式,一般會比較好寫,所以一定要認真檢查,不要寫錯!

關於mk_data,一般需要得到一個隨機的整數,c++語言寫法如下:

我們可以執行一次mk_data,得到一組資料,然後分別用stdplain跑一遍,看結果是否一樣。當然這樣手動是比較麻煩的,我們可以用指令碼來自動對拍,下面是windows

下用bat指令碼對拍的普通寫法:

(約定plain的輸出檔案是1.outstd的輸出檔案是2.out

將這一段話寫在一個文字檔案中,將其重新命名為ck.bat,然後和上述三個程式放在同一資料夾下,雙擊ck.bat,就能夠實現對拍了。對拍的優勢在於能夠近似模擬出出題人的資料來檢驗你的程式,一般在考場上通過了對拍的程式很難寫掛。當然有的時候隨機資料不一定能夠滿足我們的需求,這時候需要我們從出題人的角度出發,去構造一些最壞情況下的資料,來檢驗我們程式的強健性。

第三步:提交之前,檢查檔名,再次編譯程式,執行一遍樣例,以防手抽。

靜態差錯大大降低了程式出錯的概率,減少了動態除錯的時間,動態查錯給程式上了雙保險,最後的檢查防止了意外情況的出現。這樣的程式的正確性能夠大大提高。

在除錯中,最關鍵的一點,是站在邏輯的高度去思考程式,而不是從某個資料的角度去檢視變數,這樣才能避免陷於除錯的泥潭不能自拔。

對拍程式

分享|2012-08-14 12:22cao199338|瀏覽 5418

程式語言

ACM的程式怎麼設定對拍程式??有沒有那個好心人給我和例子吧,基本思路是對於一道題目來講,寫出高效演算法後,時間充裕的情況下再寫一個純暴力的演算法,寫一個數據生成器,對你的程式和暴力程式的結果進行比對,出現錯誤後就人工檢查。一般來講資料範圍一開始可以小一點,小資料過了以後再慢慢擴大,為保證正確性你在寫下一題的時候可以讓對拍程式一直跑著。這是別人說的,可是那個資料生成器和高效程式和暴力程式都是什麼啊?不明白。有完整例子可以加分。。

---------------------對拍程式介紹,來自百度-----------------------------------------
在資訊學競賽中,尤其是考試的時候,經常會出現把水題寫水的情況,而題目那一兩個單薄的測試資料常常不足以讓我們發現自己程式中的BUG。在競賽時,水題悲劇了的話下場一定會很慘。在這種情況下,我們可以採用寫一個對拍程式的方法來大大降低出錯的可能。
基本思路是對於一道題目來講,寫出高效演算法後,時間充裕的情況下再寫一個純暴力的演算法,寫一個數據生成器,對你的程式和暴力程式的結果進行比對,出現錯誤後就人工檢查。一般來講資料範圍一開始可以小一點,小資料過了以後再慢慢擴大,為保證正確性你在寫下一題的時候可以讓對拍程式一直跑著。這裡的關鍵是要寫一個批處理讓Windows不停地反覆執行這一操作,程式碼如下。我會不停的加分。直到200分、

2012-08-20 18:11

提問者採納

對拍程式的寫法如下
@echo off 

:loop 
    datamaker   #資料生成器rand.exe > data.in 這裡的大於符號,小於符號為重定向符號std      #標準程式std.exe < data.in > std.out
    myprogram   #my.exe < data.in > my.out   我的程式
fc ans.out std.out    //比較兩個程式的輸出是否一致,一致則返回0
if not errorlevel 1 goto loop 

pause 
goto loop
#最後上邊的內容儲存為.bat檔案

#rand.exe 
std.exe  my.exe 以及windows下的這個 .bat檔案放在同一個目錄下
***************************************************************************************************
這裡資料生成器:就是你自己編寫一個應用程式 rand.exe,這個應用程式就是隨機的生成一組符合題意的輸入資料並存放到上邊提到的輸入資料檔案中data.in
高效程式:就是上邊提到的高效演算法寫的程式,即
my.exe
暴力程式:就是指能夠保證所有輸入資料都能輸出正確結果的程式,即這裡的
std.exe
上邊的.bat檔案的功能就是迴圈判斷高效程式和暴力程式在同樣的輸入資料情況下,輸出資料是否相同,從而實現了對拍程式的功能

追問:

厲害,不過可不可以提供一個現實程式的例子。發郵箱也行。。主要是不知道該怎麼寫資料生成器,高效程式和暴力程式、謝謝。
[email protected]

追答:

比如舉一個簡單的例子:求 1+2+3+……+n = ?

資料生成器:


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
 int n;
 srand((int)time(0));  //呼叫srand()函式,以系統時間為隨機種子
 n = 1 + rand()%10000;  //隨機生成一個1到10000的自然數
 printf("%d\n",n); // 輸出隨機生成的自然數
 return 0;
}

//編譯執行後,生成rand.exe可執行檔案 然後在批處理檔案中直接呼叫 rand.exe>data.in即將上邊的輸出結果重定向到data.in檔案中
 
暴力程式:演算法是直接從1加到n,執行n次迴圈時間複雜度為 O(n)
#include<stdio.h>
#include<stdlib.h>
int main()
{
 int i,n;
 long int sum = 0;
 scanf("%d",&n);
 for(i=1;i<=n;i++)
 {
  sum += i;
 }
 printf("%d\n",sum);
 return 0;
}
 
//編譯執行後,生成std.exe可執行檔案 然後在批處理檔案中直接呼叫 std.exe<data.in>std.out即將從data.in中獲取輸入,然後將輸出結果重定向到std.out檔案中
 
高效演算法:演算法為 1+2+……+n = n*(n+1)/2,  時間複雜度為O(1)
#include<stdio.h>
#include<stdlib.h>
int main()
{
 int n;
 long int sum = 0;
 scanf("%d",&n);
 sum = n*(n+1)/2;
 printf("%d\n",sum);
 return 0;
}
 
//編譯執行後,生成my.exe可執行檔案 然後在批處理檔案中直接呼叫 my.exe<data.in>std.out即將從data.in中獲取輸入,然後將輸出結果重定向到my.out檔案中

批處理檔案 test.bat內容如下
@echo off 
:loop 
    rand.exe>data.in 
    std.exe<data.in>std.out
    my.exe<data.in>my.out 
    fc my.out std.out 
if not errorlevel 1 goto loop 
pause 
goto loop


最後將這三個.exe檔案和批處理檔案放到同一個目錄下執行批處理檔案即可