class Algorithms {
public:
Algorithms lecture {
.author = "Р.А. Нестеров"
.year = "2026/27"
.course = "НИУ ВШЭ ПИ, 2 курс"
};
// 01. Мастер-теоремы для быстрого анализа рекуррентных соотношений
void divide_and_conquer();
void decrease_and_conquer();
void akra_bazzi();
// 02. Генерация случайных чисел — основа случайных алгоритмов
class TrueRandomNumberGenerator {...};
class PseudoRandomNumberGenerator {...};
// 03. Алгоритм Монте-Карло со случайной корректностью
double estimate_area();
bool verify_mmult(const Matrix& A, const Matrix& B, const Matrix& C);
->05
05.00
первая мастер-теорема "разделяй-и-властвуй"
$T(n) = a\cdot T(n\,/\,b) +\mathrm{O}(n^k\cdot f(n))$
вторая мастер-теорема "уменьшай-и-властвуй"
$T(n) = a\cdot T(n - b) +\mathrm{O}(n^k\cdot f(n))$
теорема Акра-Баззи для рекуррентных
соотношений "разделяй-и-властвуй" общего вида
void multiply_dac(const Matrix& A, const Matrix& B);
семь рекурсивных умножений вместо восьми
для перемножения двух матриц размерности $2 \times 2$ необходимо и достаточно 7 умножений
разработать такой рекурсивный алгоритм перемножения двух матриц $A$ и $B$ размерности $n \times n$, что число рекурсивных умножений блоков матриц составляет 7:
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant 1 \\ 7\cdot T(n\,/\,2) + kn^2 & n > 1 \end{cases}$
$\left( \begin{array}{c|c} A_{11} & A_{12} \\ \hline A_{21} & A_{22} \end{array} \right) \cdot \left( \begin{array}{c|c} B_{11} & B_{12} \\ \hline B_{21} & B_{22} \end{array} \right) = \left( \begin{array}{c|c} C_{11} & C_{12} \\ \hline C_{21} & C_{22} \end{array} \right)$
выразить в общем виде решение рекуррентного соотношения
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant b \\ a \cdot T\left(\dfrac{n}{b}\right) + \mathrm{O}(n^k \cdot f(n)) & n > b \end{cases}$
$\begin{aligned} T(n) &\leqslant a \cdot T\left(\dfrac{n}{b}\right) + c n^k f(n) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &\leqslant a\left(a \cdot T\left(\dfrac{n}{b^2}\right) + c\left(\dfrac{n}{b}\right)^k f\left(\frac{n}{b}\right) \right) + c n^k f(n) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^2 \cdot T\left(\dfrac{n}{b^2}\right) + cn^k \left( f(n) + \left(\frac{a}{b^k} \right)f\left(\frac{n}{b}\right)\right) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^3 \cdot T\left(\dfrac{n}{b^3}\right) + cn^k \left( f(n) + \left(\frac{a}{b^k} \right) f\left(\frac{n}{b}\right) + \left(\frac{a}{b^k} \right)^2 f\left(\frac{n}{b^2}\right)\right) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= \dots \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^h \cdot T\left(\dfrac{n}{b^h}\right) + cn^k \cdot \sum\limits_{i = 0}^{h-1}\left(\frac{a}{b^k} \right)^i f\left(\frac{n}{b^i}\right) \end{aligned}$
с учетом предельного значения $h = \log_b n$ получаем:$\begin{aligned} T(n) &= n^{\log_b a} \cdot \mathrm{O}(1) + \mathrm{O}(n^k \cdot f(n)) \cdot \sum\limits_{i = 0}^{\log_b-1}\left(\frac{a}{b^k} \right)^i \end{aligned}$
void divide_and_conquer();
быстрый анализ рекуррентных соотношений «разделяй-и-властвуй»
выразить в общем виде решение рекуррентного соотношения
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant b \\ a \cdot T\left(\dfrac{n}{b}\right) + \mathrm{O}(n^k \cdot f(n)) & n > b \end{cases}$
$\begin{aligned} T(n) &= n^{\log_b a} \cdot \mathrm{O}(1) + \mathrm{O}(n^k \cdot f(n)) \cdot \sum\limits_{i = 0}^{\log_b n-1}\left(\frac{a}{b^k} \right)^i \end{aligned}$
сравниваем значения порядков $k$ и $\log_b a$:
void divide_and_conquer();
быстрый анализ рекуррентных соотношений «разделяй-и-властвуй»
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant b \\ a \cdot T\left(\dfrac{n}{b}\right) + \mathrm{O}(n^k \cdot f(n)) & n > b \end{cases}$
верны следущие утверждения о сложности:найти верхнюю асимптотическую границу $\mathrm{O}(g(n))$ для соотношения $T(n) = T\left(\dfrac{n}{2}\right) + T\left(\dfrac{n}{4}\right) + cn\log n$
в силу монотонности, $T\left(\dfrac{n}{b}\right) \leqslant T(n)$ для любого $b \geqslant 1$
ответ $g(n) = n \cdot \log^2 n$
выразить в общем виде решение рекуррентного соотношения
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant b \\ a \cdot T\left(n-b\right) + \mathrm{O}(n^k \cdot f(n)) & n > b \end{cases}$
$\begin{aligned} T(n) &\leqslant a \cdot T\left(n-b\right) + c n^k f(n) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &\leqslant a\left(a \cdot T\left(n-2b\right) + cn^k f\left(n-b\right) \right) + c n^k f(n) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^2 \cdot T\left(n-2b\right) + cn^k \left( f(n) + a \cdot f(n-b)\right) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^3 \cdot T\left(n-3b\right) + cn^k \left( f(n) + a \cdot f(n-b) + a^2 \cdot f(n-2b) \right) \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= \dots \end{aligned}$
$\begin{aligned} \phantom{T(n)} &= a^h \cdot T\left(n-hb\right) + cn^k \cdot \sum\limits_{i = 0}^{h-1}a^i f\left(n-ib\right) \end{aligned}$
с учетом предельного значения $h = n\,/\,b$ получаем:$\begin{aligned} T(n) &= a^{n\,/\,b} \cdot \mathrm{O}(1) + \mathrm{O}(n^k \cdot f(n)) \cdot \sum\limits_{i = 0}^{n\,/\,b - 1}a^i \end{aligned}$
void decrease_and_conquer();
быстрый анализ рекуррентных соотношений «уменьшай-и-властвуй»
выразить в общем виде решение рекуррентного соотношения
$T(n) = \begin{cases} \mathrm{O}(1) & n \leqslant b \\ a \cdot T\left(n-b\right) + \mathrm{O}(n^k \cdot f(n)) & n > b \end{cases}$
$\begin{aligned} T(n) &= a^{n\,/\,b} \cdot \mathrm{O}(1) + \mathrm{O}(n^k \cdot f(n)) \cdot \sum\limits_{i = 0}^{n\,/\,b - 1}a^i \end{aligned}$
значение $a$ определяет порядок роста верхней границы $T(n)$:
void akra_bazzi();
быстрый анализ рекуррентных соотношений «разделяй-и-властвуй» с подзадачами разного размера
$T(n) = \begin{cases} \mathrm{\Theta}(1) & 1\leqslant n \leqslant n_0 \\ \sum\limits_{i=1}^{k} a_i \cdot T(b_i\cdot n) + g(n) & n > n_0 \end{cases}$
пусть $p$ — это решение уравнения $a_1b_1^p + ... + a_kb_k^p = 1$, тогда верно соотношение для точной асимптотической границы:
$\displaystyle T(n) = \Theta\left(n^p \cdot \left(1 + \int_{1}^{n} \dfrac{g(u)}{u^{p+1}}du \right) \right)$
найти точную асимптотическую границу $\mathrm{\Theta}(g(n))$ для соотношения $T(n) = 2\cdot T\left(\dfrac{n}{4}\right) + 3\cdot T\left(\dfrac{n}{6}\right) + dn\log_2 n$
ответ $g(n) = n \cdot \log^2 n$
05.01
простой линейный конгруэнтный генератор
стандартные средства библиотеки C++
и продакшен-библиотеки
проверка качества генераторов случайных чисел
class TrueRandomNumberGenerator {...};
аппаратные источники энтропии
| категория | источник | принцип работы | применение |
|---|---|---|---|
| механика | монеты, кубики | микроизменения силы броска и трения | настольные игры, казино |
| поведение | ввод с мыши и клавиш | замер пауз между кликами / нажатиями | генерация криптокошельков |
| оптика | лавовые лампы | съемка хаотичного движения воска | защита веб-трафика (Cloudflare) |
| термодинамика | шум радиоэфира | фиксация атмосферных помех и гроз | генераторы в серверах (Random.org) |
| кванты | поведение фотонов | измерение направления частиц света | научные суперкомпьютеры |
class PseudoRandomNumberGenerator {...};
детерминированные алгоритмы в стандартной библиотеке C++
| генератор | основа в c++ | принцип работы | применение и риски |
|---|---|---|---|
| линейный конгруэнтный · LCG | std::rand() |
$X_{n+1} = (aX_n + c) \mod m$ | быстрые тесты, высокая предсказуемость результатов |
| вихрь Мерсенна | std::mt19937 |
рекуррентные соотношения над двоичными полями | стандарт для игр и симуляций Монте-Карло с периодом $2^{19937}-1$ |
| перестановочный конгруэнтный · PCG | внешняя библиотека | комбинация LCG и нелинейных перестановок | современный продакшен, быстрее и компактнее mt19937 |
| на основе счетчиков · Philox (добавлен С++26) | std::philox4x64 |
блочное шифрование без состояний | масштабируемые параллельные вычисления, симуляции на GPU |
#include <random>
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(1, 6);
int dice = distrib(gen);
#include <iostream>
#include <cstdint>
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
uint64_t state = 1;
while (true) {
state = (65539 * state) % 2147483648ULL;
uint32_t val = static_cast<uint32_t>(state);
std::cout.write(
reinterpret_cast<const char*>(&val),
sizeof(val)
);
if (!std::cout) break;
}
return 0;
}
#include <iostream>
#include <cstdint>
#include <cstdlib>
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
std::srand(42);
while (true) {
uint32_t val = static_cast<uint32_t>(
std::rand()
);
std::cout.write(
reinterpret_cast<const char*>(&val),
sizeof(val)
);
if (!std::cout) break;
}
return 0;
}
#include <iostream>
#include <cstdint>
#include <cstdlib>
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
std::mt19937 gen(42);
while (true) {
uint32_t val = gen();
std::cout.write(
reinterpret_cast<const char*>(&val),
sizeof(val)
);
if (!std::cout) break;
}
return 0;
}
#include <iostream>
#include <cstdint>
#include "pcg_random.hpp"
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
pcg32 rng;
rng.seed(42ULL, 54ULL);
while (true) {
uint32_t val = rng();
std::cout.write(
reinterpret_cast<const char*>(&val),
sizeof(val)
);
if (!std::cout) break;
}
return 0;
}
| std::rand() | std::mt19937 | pcg32 | |
|---|---|---|---|
| статус | <cstdlib>, наследие C | <random>, стандарт ISO | внешняя библиотека, де-факто стандарт |
| алгоритм | LCG, параметры зависят от реализации | вихрь Мерсенна | LCG + нелинейная перестановка (XSH-RR) |
| состояние | ≈ 4-8 байт, глобальное | ≈ 2.5 килобайта | 16 байт |
| период | $2^{31}$ | $2^{19937}-1$ | $2^{64}$ на поток $\times\,2^{63}$ потоков |
| качество · PractRand | провал на сотнях МБ | чисто до многих ГБ, отдельные провалы в BigCrush | чисто на масштабах ТБ |
| портируемость последовательности | ❌ glibc / macOS / MSVC дают разные | ✅ гарантирована стандартом | ✅ фиксирована библиотекой |
| потоки и параллелизм | нет: глобальное состояние | только ручные seed'ы | seed(seed, stream) |
| распределения <random> | ❌ ручной % со смещением |
✅ | ✅ |
| скорость | ★★☆ формально высокая, качество нулевое | ★★☆ средняя: состояние не живёт в регистрах | ★★★ высокая: всё в регистрах |
* std::mt19937 без явного значения seed всегда выдает одну и ту же последовательность — воспроизводимость (default = 5489)
05.02
оценка площади фигуры: пробуем,
а не исследуем пространство
верификация матричного умножения: случайная
линейная проекция матрицы ошибок
double estimate_area();
стандартное отклонение результатов симуляции
$X_i = 1$, если точка $X_i$ попала в круг и $X_i = 0$, если не попала
ответ $\sigma(\hat{\pi}) = 1.64 \,/\,\sqrt{N} = \mathrm{O}(1\,/\,\sqrt{N})$
| точность | общее число точек |
|---|---|
| $\pm 0.1$ | $\approx 300$ |
| $\pm 0.01$ | $\approx 30\,\,000$ |
| $\pm 0.001$ | $\approx 3\,\,000\,\,000$ |
| $\pm 10^{-6}$ | $\approx 3\cdot 10^{12}$ |
* каждая следующая значащая цифра стоит в 100 раз больше точек
[ погрешность в одно стандартное отклонение $\sigma$ ]
template<typename F> double integrate(F f, int d, uint64_t steps);
когда метод Монте-Карло безальтернативен
для хорошей точности вычислений используем сетку $10^d$ точек
| $d$ | сетка | Монте-Карло | 🏆 |
|---|---|---|---|
| 1 | $N^{-2}$ | $N^{-1\,/\,2}$ | сетка |
| 2 | $N^{-1}$ | $N^{-1\,/\,2}$ | сетка |
| 4 | $N^{-1\,/\,2}$ | $N^{-1\,/\,2}$ | паритет |
| 10 | $N^{-1\,/\,5}$ | $N^{-1\,/\,2}$ | Монте-Карло |
| 100 | $N^{-1\,/\,50}$ | $N^{-1\,/\,2}$ | Монте-Карло |
* ошибка метода Монте-Карло не зависит от размерности!
bool verify_mmult(const Matrix& A, const Matrix& B, const Matrix& C);
в чем сложность проверки $AB = C$ и где закралась ошибка?
на проверку поступили три матрицы:
$A = \begin{pmatrix} a & b \\ c & d \end{pmatrix} B = \begin{pmatrix} e & f \\ g & h \end{pmatrix} C = \begin{pmatrix} \textcolor{#FB7171}{X} & y \\ z & w \end{pmatrix}$
определим матрицу ошибок $D$:
$D = AB - C = \begin{pmatrix} \textcolor{#FB7171}{\Delta} & 0 \\ 0 & 0 \end{pmatrix}$
умножаем матрицу ошибок $D$ на случайный бинарный вектор-детектор $r = (r_1 \,\, r_2)^T$, то есть, $r \in \{0, 1\}^2$:
$Dr = \begin{pmatrix} \textcolor{#FB7171}{\Delta} & 0 \\ 0 & 0 \end{pmatrix} \cdot \begin{pmatrix} r_1 \\ r_2 \end{pmatrix} = \begin{pmatrix} \textcolor{#FB7171}{\Delta}\cdot r_1 \\ 0 \end{pmatrix}$
bool verify_mmult(const Matrix& A, const Matrix& B, const Matrix& C);
обман вычислительной сложности
на проверку поступили три матрицы:
$A = \begin{pmatrix} a & b \\ c & d \end{pmatrix} B = \begin{pmatrix} e & f \\ g & h \end{pmatrix} C = \begin{pmatrix} \textcolor{#FB7171}{X} & y \\ z & w \end{pmatrix}$
боремся с тяжелым матричным умножением в:
$Dr = (AB - C)r = \textcolor{#FB7171}{(}AB\textcolor{#FB7171}{)}r - Cr$
умножение матриц ассоциативно, поэтому:
$\textcolor{#FB7171}{(}AB\textcolor{#FB7171}{)}r - Cr = A\textcolor{#34D399}{(}Br\textcolor{#34D399}{)} - Cr$
итоговая последовательность шагов вычисления $Dr$:
сложность $\mathrm{O}(n^2)$, и никакого умножнеия матриц!
bool verify_mmult(const Matrix& A, const Matrix& B, const Matrix& C);
вероятность ошибки ниже 50%
на проверку поступили три матрицы:
$A = \begin{pmatrix} a & b \\ c & d \end{pmatrix} B = \begin{pmatrix} e & f \\ g & h \end{pmatrix} C = \begin{pmatrix} \textcolor{#FB7171}{X} & y \\ z & w \end{pmatrix}$
$r$ — случайный бинарный вектор в пространстве $\{0, 1\}^n$
$d_{i1}r_1 + \dots + \textcolor{#FB7171}{d_{ij}}r_j + \dots + d_{in}r_n = 0 \Rightarrow r_j = \dfrac{\sum_{k \neq j} d_{ik}r_k}{\textcolor{#FB7171}{d_{ij}}}$
ошибка $\mathbb{P}(Dr = 0 \mid D \neq 0) \leqslant 0.5$
bool verify_mmult(const Matrix& A, const Matrix& B, const Matrix& C);
усиление надежности через независимые запуски
на проверку поступили три матрицы:
$A = \begin{pmatrix} a & b \\ c & d \end{pmatrix} B = \begin{pmatrix} e & f \\ g & h \end{pmatrix} C = \begin{pmatrix} \textcolor{#FB7171}{X} & y \\ z & w \end{pmatrix}$
$r$ — случайный бинарный вектор в пространстве $\{0, 1\}^n$
один случайный вектор $r$ дает вероятность ошибки $\leqslant$ 50% $\Rightarrow$
$k$ раз повторяем тест $A(Br) - Cr = 0$
с независимыми векторами $r^{(1)},r^{(2)} \dots, r^{(k)}$
$\mathbb{P}(\text{глобальная ошибка}) \leqslant \dfrac{1}{2} \cdot \dots \dfrac{1}{2} = \dfrac{1}{2^k}$
итог сложность возрастает до $\mathrm{O}(kn^2)$, а вероятность неверного ответа падает экспоненциально
| ошибок в матрице | k = 1 | k = 2 | k = 3 | k = 4 | k = 5 |
|---|---|---|---|---|---|
| 1 ошибка | 50.13% | 24.65% | 12.79% | 6.10% | 3.20% |
| 2 ошибки | 24.76% | 6.22% | 1.55% | 0.38% | 0.09% |
| 3 ошибки | 25.00% | 6.19% | 1.49% | 0.40% | 0.11% |
* эмпирическая оценка вероятности неверного ответа алгоритма Фрейвальдса по 50 000 запускам
05.03
получение новых задач из upstream-репозитория
задачи и баллы новой работы
void set_hw_environment();
разделяй-и-властвуй · асимптотический анализ · рекуррентные соотношения
| ID | Баллы | Комментарий |
|---|---|---|
| a1_rec_complexity CORE | 7 | анализ временной сложности $T(n)$ рекурсивного алгоритма |
| a2_faster_strassen CORE | 6 | сравнение рекуррентных соотношений |
| a3_master_theorem CORE | 11 | применение мастер-теорем и метода Акра-Баззи |
| p1_intervals CORE | 7 | применение парадигмы "разделяй-и-властвуй" |
| p2_nmult CORE | 9 | "разделяй-и-властвуй" умножение больших целых чисел |
| p3_mmult CORE | 8 | "разделяй-и-властвуй" умножение квадратных матриц |
| p4_dunes CORE | 6 | применение парадигмы "разделяй-и-властвуй" |
| p5_clpair2d CORE | 7 | "разделяй-и-властвуй" поиск пары ближайших точек в 2D |
| p6x_clpair3d EXTRA | 10 | "разделяй-и-властвуй" поиск пары ближайших точек в 3D |
| k_mreduce CORE | 10 + 5 | реализация ядра движка MapReduce |
// Лекция 05 завершена
// Резюме основных моментов
быстрый вывод асимптотической сложности
по виду рекуррентного соотношения
основа надежности работы случайного алгоритма,
оценка качества генераторов
случайная корректность и вероятность ошибки,
оценка площадей и проверка матричного умножения
--> на следующей лекции
--> алгоритм Лас-Вегас, быстрая сортировка,
--> ADT Очередь с приоритетами и HEAP SORT
};
return 0;
05->