1. 程式人生 > >【Tools】使用批處理檔案對拍程式

【Tools】使用批處理檔案對拍程式

關於對拍

OI比賽的時候經常會發生這麼一種情況:
第X道題,寫好了暴力並能夠保證暴力的正確性,但突然想到了一個更為高效的演算法,但無法確定程式是否正確,這個時候,就要使用對拍解決問題。
有些人是用c++程式進行對拍的,而我喜歡用bat檔案(因為寫起來短啊)。

我們假設這道題的題目名為problem,資料生成的可執行程式名為random.exe
兩個程式的可執行程式名名稱為problem1.exe和problem2.exe。

先發一波對拍效果圖
這裡寫圖片描述
最後一行是我打了Ctrl + C終止程式因為這樣才能截圖

兩個版本的bat對拍

cpp內重定向寫法

首先是第一種版本,就是三個源程式中都寫了freopen,且random.exe的輸出檔案為problem.in,而兩個程式所對應的輸出為problem1.out和problem2.out
接下來就是對應的bat

@echo off
:loop
    random.exe
    problem1.exe
    problem2.exe
    fc problem1.out problem2.out
    if not errorlevel 1 goto loop
    pause 
    goto loop

bat內重定向寫法

其次是第二種版本,就是三個程式的freopen都不被執行,相比來講也更加隨性,畢竟名字可以亂取了
對應的bat,效果和第一種是一樣的。

@echo off
:loop
    random.exe > problem.in
    problem1.exe < problem.in
> problem1.out problem2.exe < problem.in > problem2.out fc problem1.out problem2.out if not errorlevel 1 goto loop pause goto loop

兩種程式碼可以自己參考使用,各有優劣。
對於bat內部的東西是什麼意思還是解釋一下吧雖然對拍這種東西靠背啊。

我拿第二個bat解釋一下。
首先第一行@echo off關閉輸入顯示,如果你單純地想確認一下正確性的話這句話就一定要打上,否則東西會太多的,如果不打,上結果圖:
這裡寫圖片描述


東西有點多,就很亂。。如果不想看到這種東西模糊你的眼睛就還是打上吧。
其次是第二行的:loop這是個標誌,為了使用後面的goto。這個功能相信大家使用c++時一定為了好玩而使用過。
第三行random.exe > problem.in,意思是執行random.exe並將程式執行結果匯入problem.in。
第四行和第五行,基本格式就是problem.exe < problem.in > problem.out,基本意思就是執行problem.exe,輸入檔案定向到problem.in,輸出檔案重定向到problem.out,說的簡單點就是執行problem.exe,從problem.in中讀入資料,在將程式結果輸出到problem.out中。
第六行fc problem1.out problem2.outfc是比較的意思,這句話就是對兩個out檔案進行比較。
緊接著是第七行if not errorlevel 1 goto looperrorlevel是上一條命令的返回值,上一行的fc在比較的兩個檔案相同時返回0,不同時返回1,
這一行的意思就是,如果fc返回的不是1,就跳到:loop,進行下一個迴圈。
再下一行pause暫停,一旦fc返回1,就會執行到這一行,暫停程式,給你時間看資料。
具體效果:
這裡寫圖片描述
goto loop,看完資料,按下任意鍵結束暫停,繼續迴圈。

關於關於對拍的事兒就講到這裡,接下來貼一發在網上看到的隨機數的優化。

隨機數優化

優化起源:在我們寫隨機程式的時候會寫出如下的片段:

        srand((unsigned)time(NULL));

或者是

        int seed = time(NULL);
        srand(seed);

可惜的是time(NULL)每秒更新1次,相當於1秒內生成的隨機資料是一模一樣的!這個太慢了,我們需要更優秀的隨機函式。
那麼有沒有什麼變的更快的隨機數種子?有!windows自帶了一個隨機數發生器:%random%,它的值就是一個隨機整數,可以在命令列裡呼叫。
那接下來就好辦了,我們把這個數傳給rand.exe用來當隨機數種子就行了。

(⊙o⊙)…怎麼傳引數呢?
這就要使用到c++main()函式中那個被省略的引數了。
你也許會問,main()難道省略了什麼引數嗎???
不好意思,答案是是的。
本來的main()應該是長成這樣子的。

int main(int argc, char *argv[]) {

}

argcargv[]都有什麼用呢?
實際上這兩個就是傳入引數,argc 是引數個數,argv[] 是引數表,從1開始。
接下來就好辦了,我們只要把%random%傳給random.exe就行了。
具體操作就是把第二個bat中的random.exe > problem.in改為random.exe %random% problem.in,在random的源程式中也要對應地修改一下。板子在下面給出。

bat和random板子

bat

@echo off
:loop
    data.exe > in.txt
    solve1.exe < in.txt > out1.txt
    solve2.exe < in.txt > out2.txt  
    fc out1.txt out2.txt
    if not errorlevel 1 goto loop
    pause
    goto loop

random

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<sstream>
#define randint(l, r) ((l) + rand() % ((r) - (l) + 1))
int main(int argc, char *argv[]) {
    int seed = time(NULL);
    if (argc > 1) {
        std::stringstream ss;
        ss.clear();
        ss << argv[1];
        ss >> seed;
    }
    srand(seed);

    return 0;
}