Суть такая:
smart
— быстрое, но в котором есть баг, который хотим найтиstupid
— медленное, но точно корректноеgen
— печатает какой-то корректный тест, сгенерированный случайноchecker
, который n раз генерирует тест, даёт его на ввод stupid
-у и smart
-у, сравнивает выводы и останавливается, когда они отличаютсяЗадача. Есть массив чисел \(1 \le a_1 ... a_n \le 10^9\). Найдите значение минимального элемента.
Приведем код решения stupid
, который будем использовать в качестве эталонного:
int a[maxn];
void stupid() {
int n;
>> n;
cin for (int i = 0; i < n; i++)
>> a[i];
cin int ans = 1e9;
for (int i = 0; i < n; i++)
= min(ans, a[i]);
ans << ans;
cout }
Пусть у нас есть решение smart
, которое содержит ошибку в границах цикла:
int a[maxn];
void smart() {
int n;
>> n;
cin for (int i = 0; i < n; i++)
>> a[i];
cin int ans = 1e9;
for (int i = 1; i < n; i++)
= min(ans, a[i]);
ans << ans;
cout }
Даже в таком примере можно долго искать ошибку, если подбирать случайные тесты руками и проверять ответ на правильность, поэтому мы хотим найти тест, на котором два решения будут давать разный ответ, чтобы впоследствии найти ошибку в smart
.
Примечание. Автор не рекомендует так делать, но многим такой подход кажется проще для понимания.
Суть в следующем:
int a[maxn];
int n;
int stupid() {
int n;
>> n;
cin int ans = 1e9;
for (int i = 0; i < n; i++)
= min(ans, a[i]);
ans return ans;
}
int smart() {
int n;
>> n;
cin int ans = 1e9;
for (int i = 1; i < n; i++)
= min(ans, a[i]);
ans return ans;
}
void gen() {
= rand() % 10 + 1;
n for (int i = 0; i < n; i++) {
[i] = rand();
a}
}
int main() {
for (int i = 0; i < 100; i++) {
();
genif (smart() != stupid()) {
<< "WA" << endl;
cout << n << endl;
cout for (int j = 0; j < n; j++) {
<< a[j] << ' ';
cout }
break;
}
<< "OK" << endl;
cout }
return 0;
}
Суть в следующем:
Файлы stupid.cpp
, smart.cpp
и gen.py
содержат уже понятный нам код.
Вот примерный код скрипта checker.py
:
import os, sys
= sys.argv # первый аргумент - 'checker.py' поэтому "откинем" его с помощью _
_, f1, f2, gen, iters
for i in range(int(iters)):
print('Test', i+1)
'python3 %s > test.txt' % gen)
os.popen(= os.popen('./%s < test.txt' % f1).readlines()
v1 = os.popen('./%s < test.txt' % f2).readlines()
v2 if v1 != v2:
print("FAIL!\nInput:")
print(*(open("text.txt").readlines()))
print("Correct output:")
print(*v1)
print("Wrong output:")
print(*v2)
sys.exit()print("No output differences found.")
python3 checker.py stupid smart gen.py 100
, предварительно скомпилировав stupid
и smart
в ту же директорию, что и сам checker.py
.v1 != v2
следует использовать сторонний скрипт compare.py
../
» во всех системных вызовах и вместо “python3” писать “python”.Примечание. Ну такой вот примерно рецепт усредненный, потому что вариаций масса. Берется неправильное решение, оно не работает, рабочий код — это не про код моего бати. Он берет это решение, вываливает его в скрипт и начинает запускать. Добавляет огромное количество тестов, крайних случаев, рандома и МАКСТЕСТОВ! для проверки. Все это прогоняется вместе с медленным решением. Потом скрипт находит баг и системный блок остужается на балконе. Потом батя заносит тест и щедро заполнив код отладочным выводом начинает дебажить. При этом параллельно ест и засыпает крошками клавиатуру. Ест и приговаривает полушепотом ух ###. При этом у него на лбу аж пот выступает. Любезно мне иногда предлагает подебажить, но я отказываюсь. Надо ли говорить о том какой код получается потом? Вонища такая, что тестирующая система падает.