Блог пользователя WasylF

Автор WasylF, 11 лет назад, По-русски

Здравствуйте! Появилась следующая проблема. Решаю задачу с e-olimp, и мое решение проходит только первый тест. Подобрать вручную тест, который не пройдет мне не удалось. Тогда я вспомнил про стресс тесты (моя первая попытка), написал лобовое решение, генератор и батник. запустил все это дело и за 2 часа так и не удалось найти тест, на котором программы работают по-разному. Подскажите, пожалуйста, как лучше писать стресс тесты? или может я что-то не так понимаю?

вот ссылки:
задача
лобовое решение
решение с ошибкой
генератор
сам батник:

@ echo off
:1
echo good
gener.exe
brut.exe
2.exe
fc /L output.txt output_brut.txt 
if %errorlevel% == 0 goto 1
echo NeSovpalo
pause

спасибо за помощь

UPD новое лобовое решение + в генераторе изменил количество тесто на 10 000. запустил, жду...

  • Проголосовать: нравится
  • +7
  • Проголосовать: не нравится

»
11 лет назад, # |
Rev. 2   Проголосовать: нравится +19 Проголосовать: не нравится

Обычно так называемое "лобовое" решение подразумевает просто перевод условия задачи на язык программирования. Вы же в своем "лобовом" решении пользуетесь куском решения с ошибкой, возможно, ошибка именно в этом куске, и стресс-тест не найдет её.

В данной задаче проще было бы просто втупую посчитать количество различных простых делителей для каждого числа из отрезка.

Кроме того, полезно сдавать тупое решение в тестирующую систему и проверять, не получает ли оно Wrong Answer, и только после этого стрессить.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится

    спасибо, но я что-то не понял, где у меня ошибка. задачу я отсылал http://www.e-olimp.com/solutions/1396123, получил 1 ок, 5 таймлимитов. и проверку вроде делаю, как Вы пишите, для каждого числф из отрезка low high запускаю проверку:

    int chek(LL n)
    {
    if (n==1) return 0;
    for (int i=0; (i<cnt&&primes[i]<=n); i++)
    {
        if (!(n%primes[i]))
        {
            if (n==primes[i]) return 0;
            while (n%primes[i]==0) n/=primes[i];
            if (n!=1) return 0;
            else return 1;
        }
    }
    return 0;
    }
    

    который возвращает 1, если 1 простой делитель, иначе 0.

    Буду очень благодарен, если скажите, где именно ошибка)

»
11 лет назад, # |
  Проголосовать: нравится +27 Проголосовать: не нравится

На e-olimp'е правильное решение иногда может не заходить

»
11 лет назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится
  1. Ваш генератор выводит один и тот же тест в течение секунды из-за инициализации от time(). То есть за два часа вы протестировали всего на 7200 случайных тестах. То же самое можно было бы сделать за пару секунд. Правильный метод — инициализироваться либо от /dev/urandom на Linux, либо от более точных источников времени (clock() в их число не входит — он обычно меряет процессорное время текущего процесса, то есть примерно ноль в самом начале). Под виндой можно пробовать GetTickCount() из #include <windows.h>, либо rdtsc:
long long rdtsc() {
  long long x;
  asm("rdtsc" : "=A"(x)); // Выполнить ассемблерную команду rdtsc и сложить результат из регистров EDX:EAX в переменную x
  return x;
}

// ...

srand(rdtsc());
  1. Проверьте, какие значения выдаёт функция rand(). Вполне может оказаться, что она выдаёт значения от 0 до 65535 (константа называется MAXRAND или как-то так). Правильный вариант:
int randrange(int n) {
  int v = (rand() << 16) ^ rand();
  return abs(v % n);
}
  1. Слишком мало запросов за раз. Всего десять — это же так мало, запуск процесса — это очень и очень дорого. Гораздо лучше, когда решение работает, скажем, секунду, тогда накладные расходы меньше и получается больше тестов в единицу времени.

  2. Попробуйте вместо случайных сгенерировать вообще все маленькие тесты (скажем, до сотни).

  3. Зашлите в систему "тупое" решение и убедитесь, что оно получает TL на каком-нибудь нетривиальном тесте. Если получило WA — всё очень плохо, если получилось TL1 — немного лучше.

  4. Как уже указали, "тупое" решение вообще не должно содержать ни строчки кода из "умного". С нуля и максимально "в лоб", вообще без алгоритмов и оптимизаций, можно и за бесконечную асимптотику.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    спасибо, сейчас перепишу. а все пары меньшие 1000 я уже тоже перебрал, но безрезультатно..

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Тогда, скорее всего, проблемы с большими тестами. Может быть из-за:

      1. Продолбали где-нибудь long long (скажем, в умножении) или написали вместо него int
      2. Проблемы со считыванием/выводом при использовании C-style: %lld vs %I64d vs C++11 vs Windows XP
      3. Проблем с точностью. Вы используете вещественные числа, а они могут случайно округлиться не в ту сторону (если, скажем, результат или исходный аргумент функции нельзя точно представить). Попробуйте от них избавиться и в "тупом" (в целочисленных задачах идеально, если в тупом решении вообще вещественной арифметики нет), и в "умном" решениях.
  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Сейчас в GCC даже не нужно пользоваться ассемблером:

    #include <x86intrin.h>
    
    unsigned long long t = __rdtsc();
  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    я думаю, правильнее всего использовать C++11 для генерации случайных чисел:

    #include <chrono>
    #include <random>
    ...
    mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());
    for (int i = 0; i < 10; ++i) cout << rng() << '\n';
    
»
11 лет назад, # |
Rev. 5   Проголосовать: нравится 0 Проголосовать: не нравится

У меня немного другое решение: понятно что почти простые числа имеют вид pk. Например, почти простыми будут 4, 8, 16, 32, 9, 81, … . Сгенерируем все простые число до 106, а потом сгенерируем и отсортируем все почти простые числа (их будет 80070). Ответ ищем бинарним поиском или даже влоб. У меня такое решение прошло за 62мс.

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    спасибо, за решение. похоже, оно проще моего)) но в данном случае меня больше интересовало, как находить ошибки самостоятельно))