← Все вопросы

Как вычислять C(n, k) mod p, когда n и k огромны (теорема Люка)?

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

Нужно посчитать C(n, k) mod p, где p — небольшое простое (например, 13 или 1009), а n и k могут быть до 10^18. Предподсчитать факториалы до n нельзя — слишком много. Слышал про теорему Люка (Lucas). Как она работает и как её реализовать?

2 ответа

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

Теорема Люка: запиши n и k в системе счисления по основанию p как n = (n_m ... n_1 n_0)_p и k = (k_m ... k_0)_p. Тогда

C(n, k) ≡ ∏ C(n_i, k_i) (mod p),

где все «цифры» n_i, k_i лежат в [0, p-1]. Если хоть в одном разряде k_i > n_i, всё произведение равно 0.

Идея: маленькие C(n_i, k_i) с обоими аргументами < p считаем напрямую (предподсчёт факториалов до p), а большие n,k разбираем поразрядно.

long long power(long long a, long long b, long long p){long long r=1;a%=p;while(b){if(b&1)r=r*a%p;a=a*a%p;b>>=1;}return r;}

long long C_small(long long n, long long k, long long p) {
    if (k > n) return 0;
    long long num = 1, den = 1;
    for (long long i = 0; i < k; i++) {
        num = num * ((n - i) % p) % p;
        den = den * ((i + 1) % p) % p;
    }
    return num * power(den, p - 2, p) % p; // p простое
}

long long lucas(long long n, long long k, long long p) {
    if (k == 0) return 1;
    return C_small(n % p, k % p, p) * lucas(n / p, k / p, p) % p;
}

Сложность: глубина рекурсии O(log_p n), на каждом шаге C_small за O(p log p). Итого O(p · log_p(n) · log p). Работает только для простого p.

4

Важно: Люка требует именно простой модуль. Для p = 1e9+7 и небольших n обычный предподсчёт факториалов проще и быстрее — Люка нужен ровно когда n огромен, а p мало. Если модуль составной (например, 1e6), нужен обобщённый Люка через китайскую теорему об остатках и факторизацию модуля — это заметно сложнее.

Ваш ответ

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