← Все вопросы

Обратные элементы для всех чисел 1..n по простому модулю за O(n): откуда рекуррента?

Задан 21 месяц назад1.1к просмотров2 ответа
9

Нашёл формулу inv[i] = -(p/i) * inv[p % i] % p для предподсчёта обратных всех чисел от 1 до n за O(n). Использую её, но не понимаю, откуда она берётся — выглядит как магия. Можете вывести?

2 ответа

13
✓ Принятый ответ — помог автору

Вывод чисто алгебраический. Пусть 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.

4

Когда это реально нужно: формулы вроде C(n, k) через inv[k!], суммы гармонических рядов по модулю, или любые места, где делишь на все подряд числа 1..n. Если тебе нужны обратные только к факториалам, то ещё проще — посчитай inv_fact[n] одним Ферма и иди назад рекуррентой inv_fact[i-1] = inv_fact[i]·i. А приведённая формула универсальна для произвольных i, не только факториалов.

Ваш ответ

Войдите, чтобы ответить на вопрос.