Обратные элементы для всех чисел 1..n по простому модулю за O(n): откуда рекуррента?
Нашёл формулу inv[i] = -(p/i) * inv[p % i] % p для предподсчёта обратных всех чисел от 1 до n за O(n). Использую её, но не понимаю, откуда она берётся — выглядит как магия. Можете вывести?
2 ответа
Вывод чисто алгебраический. Пусть p простое, i — число от 2 до p-1. Запишем деление с остатком: p = (p / i)·i + (p mod i), где p / i — целая часть. Возьмём это равенство по модулю p (левая часть p ≡ 0):
0 ≡ (p/i)·i + (p mod i) (mod p)
Теперь домножим всё на inv[i] · inv[p mod i] (обратные к i и к остатку):
0 ≡ (p/i)·inv[p mod i] + inv[i] (mod p)
Отсюда:
inv[i] ≡ −(p/i)·inv[p mod i] (mod p)
Поскольку p mod i < i, значение inv[p mod i] уже посчитано раньше — рекуррента «снизу вверх».
vector<long long> inverses(int n, long long p) {
vector<long long> inv(n + 1);
inv[1] = 1;
for (int i = 2; i <= n; i++)
inv[i] = (p - (p / i) * inv[p % i] % p) % p;
return inv;
}
Знак минус заменён на (p - ...), чтобы остаток был неотрицательным. Сложность — O(n), против O(n log p) при наивном возведении каждого в степень. Требование: p простое и p > n.
Когда это реально нужно: формулы вроде C(n, k) через inv[k!], суммы гармонических рядов по модулю, или любые места, где делишь на все подряд числа 1..n. Если тебе нужны обратные только к факториалам, то ещё проще — посчитай inv_fact[n] одним Ферма и иди назад рекуррентой inv_fact[i-1] = inv_fact[i]·i. А приведённая формула универсальна для произвольных i, не только факториалов.