对拍教程

本文将简单介绍对拍的使用方法。

什么是对拍?对拍有什么用?

对拍就是利用一个正确程序和一个未知程序进行多组答案比较,判定未知程序的正确性的方法。

对拍在算法竞赛中也被用于检验两个未知程序的正确性,竞赛中一般可以认为两个可过样例的本质不同的程序能拍出相同结果,等价于两个程序均正确;但注意由于两个程序同样的取模和上下溢出导致的错误可能无法被用对拍检查出。

怎么使用对拍?

以下使用方法中的文件定义(均保存在当前目录下):

  • brute.cpp / brute.exe / brute:“暴力”求解程序(或正确程序)。
  • code.cpp / code.exe / code:待验证程序。
  • generator.cpp / generator.exe / generator:符合两个程序时空复杂度和题目数据范围的数据生成器(基于随机数)。
  • duipai.cpp / duipai.exe / duipai:对拍程序。

以上程序均使用文件读写,其中 *.cpp 是 C++ 语言源代码文件,*.exe 是 windows 下的可执行文件,无后缀名的是 linux 下的可执行文件。

windows 使用方法

编写好程序 brute.cppcode.cppgenerator.cpp 并编译为可执行文件,保存在当前目录下。

编写程序 duipai.cpp,伪代码如下:

\[\begin{array}{ll} 1 & \textbf{Input.}\ \text{Expected number of test cases}\ n \text{.}\\ 2 & \textbf{Output.}\ \text{The verdict of the code.}\\ 3 & \textbf{Method.}\\ 4 & \textbf{while}\ T\gets 1\ \textbf{to}\ n\\ 5 & \qquad\text{generate input file with generator.exe}\\ 6 & \qquad\text{generate answer file with brute.exe}\\ 7 & \qquad\text{generate output file with code.exe}\\ 8 & \qquad\text{compare the answer file and the output file}\\ 9 & \qquad\text{give the verdict} \end{array} \]

下面是两个常识,有关需要用到的 windows 指令:

重定向程序 test.exe 的输入输出,让它从 1.in 读入并输出到 1.out 的方法是:test.exe < 1.in > 1.outtest < 1.in > 1.out

比较文件 1.out1.ans(忽略空白字符)的方法是:fc 1.out 1.ans。为了排版美观,也可以重定向输出到 1.logfc 1.out 1.ans > 1.log

其中判断是否有差异的方法是,没有差异 fc 的返回值为 \(0\),否则不为 \(0\)

本文最后会有示例。

linux 使用方法

实现方法相同,第一个指令改成 ./test < 1.in > 1.out,第二个指令改成 diff 1.out 1.ans 即可。

使用示例(windows)

以 A+B Problem 为例,下面是对拍的一个示例:

brute.cpp

#include <bits/stdc++.h>
using namespace std;

int main() {
    int a, b;
    cin>>a>>b;
    cout<<a+b<<endl;
    return 0;
}

code.cpp

#include <bits/stdc++.h>
using namespace std;

int main() {
    srand(time(0));
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%d\n", a+b+(rand()%5==4)); // 故意制造一些错误
    return 0;
}

generator.cpp

#include <bits/stdc++.h>
using namespace std;
const int lim = 1e8;

int myrand(int L, int R) {
    int rnd = rand() << 15 | rand(); // 注意由于 windows 下 rand() 的返回值最大为 32767,为了获得 int 范围内的随机数需要这么做
    if(rnd < 0) rnd = -rnd;
    return rnd % (R - L + 1) + L;
}

int main() {
    srand(time(0));
    int a, b;
    a = myrand(1, lim);
    b = myrand(1, lim);
    printf("%d %d\n", a, b);
    return 0;
}

duipai.cpp (windows)

#include <bits/stdc++.h>
using namespace std;

int main() {
    int expected = 0, verdict = 1;
    printf("Input number of test cases: ");
    scanf("%d", &expected);
    for(int test_case=1;test_case<=expected;test_case++) {
        printf("Running on test #%d: ", test_case);
        if(!verdict) {
            printf("Skipped\n");
            continue;
        }
        system("generator > 1.in");
        system("code < 1.in > 1.out");
        system("brute < 1.in > 1.ans");
        if(system("fc 1.out 1.ans > 1.log")) {
            printf("WA\n");
            verdict = 0;
            continue;
        }
        printf("AC\n");
    }
    if(verdict) printf("Verdict: AC\n");
    else printf("Verdict: WA\n");
    return 0;
}

错误信息可以在 1.log 找到,错误测试点的输入输出和答案在对应的 .in / .out / .ans 文件中。

posted @ 2021-05-28 19:04  rui_er  阅读(822)  评论(0)    收藏  举报