E016. Вычисление факториала по модулю
Источник: 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 已显示.