E016. Вычисление факториала по модулю

e-maxx algorithm original: C/C++ #algorithm #emaxx #math #number-theory
题目文本会按所选界面语言从俄语翻译;代码保持不变。

Источник: e-maxx.ru/algo, страница PDF 45.

В некоторых случаях необходимо считать по некоторому простому модулю

сложные формулы, которые в том

числе могут содержать факториалы. Здесь мы рассмотрим случай, когда модуль

сравнительно мал. Понятно, что

эта 题目 имеет смысл только в том случае, когда факториалы 输入ят и в числитель, и в знаменатель

дробей. Действительно, факториал

и все последующие обращаются в ноль по модулю

, однако в дробях

все множители, содержащие

, могут сократиться, и полученное выражение уже будет отлично от нуля по модулю .

Таким образом, формально 题目 такая. it is required вычислить

по простому модулю

, при этом не given

все кратные

множители, 输入ящие в факториал. Научившись эффективно вычислять такой факториал, мы сможем быстро вычислять значение различных комбинаторных формул (на示例, Биномиальные коэффициенты).

算法

Выпишем этот "модифицированный" факториал в явном виде:

При такой записи видно, что "модифицированный" факториал распадается на несколько блоков длины

(последний

блок, возможно, короче), которые все одинаковы, за исключением последнего elementа:

Общую часть блоков посчитать легко — это просто

, которую можно посчитать программно или

по теореме Вильсона (Wilson) сразу find

. Чтобы перемножить эти общие части

всех блоков, надо найденную величину возвести в степень по модулю

, что можно сделать за

операций

(см. Binary exponentiation; впрочем, можно заметить, что мы фактически возводим минус единицу в какую-

то степень, а потому результатом всегда будет либо

, либо

, в зависимости от чётности показателя. Значение

в последнем, неполном блоке тоже можно посчитать отдельно за

. Остались только последние elementы

блоков, рассмотрим их внимательнее: И мы снова пришли к "модифицированному" факториалу, но уже меньшей размерности (столько, сколько было

полных блоков, а их было

). Таким образом, вычисление "модифицированного" факториала

мы свели

за

операций к вычислению уже

. Раскрывая эту рекуррентную зависимость, мы получаем, что

глубина рекурсии будет

, итого Asymptotic complexity 算法а получается

.

实现

Понятно, что при реализации не обязательно использовать рекурсию в явном виде: поскольку рекурсия хвостовая, её легко развернуть в цикл.

int factmod (int n, int p) {
int res = 1;
while (n > 1) {

res = (res * ((n/p) % 2 ? p-1 : 1)) % p;

for (int i=2; i<=n%p; ++i)

res = (res * i) % p;

n /= p;

}

return res % p;

}

Эта 实现 работает за

.

C# 解法

自动草稿,提交前请检查
using System;
using System.Collections.Generic;
using System.Linq;

public static class AlgorithmDraft
{
    // Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
    int factmod (int n, int p) {
            int res = 1;
            while (n > 1) {
                    res = (res * ((n/p) % 2 ? p-1 : 1)) % p;
                    for (int i=2; i<=n%p; ++i)
                            res = (res * i) % p;
                    n /= p;
            }
            return res % p;
    }
}

C++ 解法

匹配/原始
int factmod (int n, int p) {
        int res = 1;
        while (n > 1) {
                res = (res * ((n/p) % 2 ? p-1 : 1)) % p;
                for (int i=2; i<=n%p; ++i)
                        res = (res * i) % p;
                n /= p;
        }
        return res % p;
}

Java 解法

自动草稿,提交前请检查
import java.util.*;
import java.math.*;

public class AlgorithmDraft {
    // Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
    int factmod (int n, int p) {
            int res = 1;
            while (n > 1) {
                    res = (res * ((n/p) % 2 ? p-1 : 1)) % p;
                    for (int i=2; i<=n%p; ++i)
                            res = (res * i) % p;
                    n /= p;
            }
            return res % p;
    }
}

Материал разбит как 算法ическая 题目: изучить постановку, понять асимптотику и реализовать 算法 на выбранном языке.

Vacancies for this task

活跃职位 with overlapping task tags are 已显示.

所有职位
目前还没有活跃职位。