4. Генерация случайных чисел
Для генерации простых чисел нужно следующее
| #include <random> #include <time.h> ... srand(time(0)); cout << rand() << endl; cout << rand() % MOD; random_shuffle(permutation.begin(), permutation.end()); |
Данный рандом крайне плох тем, что его диапазон всего 32 тысячи, и используется линейный конгруэнтный генератор.
| #include <random> #include <time.h> #include <chrono> ... mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); mt19937 rng(time(0)); // тоже работает uniform_int_distribution<int> uid(-1000000000, 1000000000); cout << uid(rng) << endl; shuffle(permutation.begin(), permutation.end(), rng); |
Данный генератор основан на вихре Мерсенна период которого составляет 219937 − 1
Иногда в олимпиадах блокируется доступ ко времени, тогда в качестве зерна вы можете использовать либо константу, либо адрес указателя
| int * x = new int[1]; cout << (int)x; // генерация случайного простого модуля int getMod() { uniform_int_distribution<int> u(1e9, 2e9); int x = u(rng); while (!isPrime(x)) ++x; return x; } // получение случайного числа для инициализации рандомизатора, при заблокированных функциях времени int getTrueRand() { int *a = 0; int rnd = 0; for (int i = 0; i < 3; ++i) { a = new int; rnd ^= (int)a; } return rnd; } |
5. Теория Гольдбаха
Утверждение о том, что любое чётное число, начиная с 4, можно представить в виде суммы двух простых чисел. Не доказано, но проверено для всех небольших чисел.
Тернарная проблема Гольдбаха, согласно которой любое нечётное число, начиная с 7, можно представить в виде суммы трёх простых чисел была доказана.
6. НОД и НОК
Алгоритм Евклида
Заметим, вначале, что gcd(a,b)=gcd(b,a)
gcd(a, b) = a, if b == 0
= gcd(b, a - b), b > 0
предполагается, что a > b, иначе поменять местами
Доказательство
Если g=gcd(a,b) делит и a, и b, то их разность (a−b) тоже будет делиться на g.
Никакой больший делитель d числа b не может делить число (a−b): если d > g, то d не может делить a, а значит и не делит (a−b).
Сложность данного алгоритма линейная.
Ускорим его, заменив a - b на a % b, так как это эквивалентно последовательности вычитаний до момента, когда a станет меньше b. Легко видеть, что если a > b, то после операции a уменьшится хотя бы в 2 раза. За счёт этого алгоритм имеет логарифмическую сложность, а именно O(log min(a,b))
Функции обобщаются на множество чисел
gcd(a,b,c,d)=gcd(gcd(gcd(a,b),c),d)
lcm(a,b,c,d)=lcm(lcm(lcm(a,b),c),d)
Также полезно знать, что нахождение gcd группы из n чисел от 1 до A будет работать не за O(n log A), а за O(n + log A) — это несложно доказать по индукции.
Ещё один интересный факт в том, что НОД чисел Фибоначчи вычисляется в среднем дольше, чем других чисел.
Также важно, что НОД двух чисел Фибоначчи равен числу Фибоначчи с индексом, равным наибольшему общему делителю индексов, то есть
lcm(a, b) = a / gcd(a, b) * b - чтобы избежать переполнения
| int gcd(int a, int b) { // O(log min(a,b)) if (b == 0) return a; return gcd(b, a % b); } int gcd_no_recursion (int a, int b) { while (b) { a %= b; swap(a, b); } return a; } int lcm (int a, int b) { return a / gcd(a, b) * b; } |






