This article discusses results concerning factorials modulo a given modulus and presents a method whose time complexity is linear in the modulus, so it is mainly suitable for relatively small moduli (∼106). Depending on the setting, polynomial techniques can also be used for fast computation.
By the Chinese remainder theorem, the factorial modulo problem can be reduced to the case where the modulus is a prime power pα. When handling such problems, it is often necessary, for a prime p and positive integer n, to extract from the factorial n! all factors p, yielding the decomposition:
n!=pνp(n!)(n!)p.
Here, νp(n!) denotes the exponent in the prime factorization of n! of p, while (n!)p denotes the integer obtained from n! after removing all powers of p. This article discusses how to compute the residue of (n!)p modulo a prime (or prime power) and the exponent νp(n!).
This decomposition is especially useful when factorials occur in both the numerator and denominator of an expression, for example when computing a binomial coefficient modulo a given modulus. In such problems, the powers of p in the numerator and denominator can be subtracted directly, while the part coprime to p, (n!)p, can be computed using a multiplicative inverse.
This article also introduces Wilson's theorem and its generalization, Legendre's formula, Kummer's theorem, and other related results.
Wilson's theorem
Wilson's theorem gives a necessary and sufficient condition for determining whether a natural number is prime.
Wilson's theorem
For a natural number n>1, n is prime if and only if (n−1)!≡−1(modn).
Proof
First, for a prime p, prove (p−1)!≡−1(modp). This can be shown by either of two concise proofs using a congruence equation or a primitive root; the details are omitted here. We give below a proof requiring less background knowledge:
When p=2, the statement is obvious. Now assume p≥3; we must show that the product of all nonzero elements (that is, residue classes) in Zp is −1. Every nonzero element of Zp, a, has an inverse a−1, so the products of mutually inverse elements in Zp are 1. However, a and a−1 may be equal: a=a−1 if and only if a2≡1(modp), namely
0≡a2−1≡(a+1)(a−1),(modp)
Therefore, a≡1(modp) or a≡−1(modp). This shows that the product of all elements in Zp∖{0,1,−1} is 1, and hence the product of all nonzero elements in Zp is −1.
Conversely, for a composite number n, we prove that (n−1)!≡−1(modn). By contradiction, suppose (n−1)!≡−1(modn), so there is an integer k such that (n−1)!=kn−1. Since n is composite, there is a prime p<n such that n=pm, and therefore (n−1)!=kpm−1≡−1(modp). But the product (n−1)! necessarily contains p, so (n−1)!≡0(modp). This contradiction proves that (n−1)!≡−1(modn).
Using the notation of this article, Wilson's theorem can be written as (p!)p≡−1(modp).
Generalization
Wilson's theorem can be generalized to arbitrary moduli.
Theorem (Gauss)
For a natural number m>1,
1≤k<m,k⊥m∏k≡±1(modm).
Moreover, the ±1 in the remainder takes the value −1 if and only if, for modulus m, a primitive root exists, that is, when m=2,4,pα,2pα, where p is an odd prime and α is a positive integer.
Proof
This theorem follows readily from the structure of the multiplicative group of integers modulo n. Here we give a proof with a similar idea but more elementary details.
For m=2, we have 1!=1≡−1(mod2). For the other cases where primitive roots exist, let the primitive root be g. Every positive integer less than m and coprime to it, denoted by k, can be uniquely represented as gimodm, where 0≤i<φ(m) and φ(m) is the Euler totient function. It is easy to verify that φ(m) is always even. Since gi and gφ(m)−i are multiplicative inverses, pair them in the product to obtain
Since gφ(m)/2modm is the unique element other than 1modm whose multiplicative inverse is itself, it must equal −1modm. Thus, the remainder in this case is −1.
For the case where no primitive root modulo m exists, we prove that the remainder is 1. First factor m=p1e1p2e2⋯pses, and then the Chinese remainder theorem shows that it suffices to prove
1≤k<m,k⊥m∏k≡1(modpjej)
This holds for every factor pjej. The Chinese remainder theorem states that every possible residue tuple (r1,r2,⋯,rs), where 1≤rj<pjej and pj⊥rj, corresponds uniquely to a 1≤k<m with k⊥m and k≡rj(modpjej). Therefore, for a given residue rj, exactly φ(m)/φ(pjej) values of k satisfy k≡rj(modpjej). Grouping the product using this fact gives
For the exponent φ(m)/φ(pjej)=φ(m/pjej) to be odd, it is necessary that m/pjej=1,2, because the Euler function φ(n) is even for every n≥3. If pj is an odd prime, then because no primitive root modulo m exists, we must have m/pjej=1,2. If pjej=2,4, then because no primitive root modulo m exists, m/pjej must contain an odd prime factor and hence be greater than 2. In both cases, the exponent φ(m)/φ(pjej) is even. The term in parentheses above has already been proved to be pjej-congruent to −1, so this power is pjej-congruent to 1. The only remaining case is pj=2 and ej>2; for this case, we can directly prove `
1≤rj<2ej,rj⊥2∏rj≡1(mod2ej).
Following the proof idea above, for 1≤rj<2ej, pair off and cancel the odd integers rj. Those that cannot be paired must be solutions of x2≡1(mod2ej). This equation means 2ej∣(x−1)(x+1). Let x=2y+1; then 2ej−2∣y(y+1). Since y and y+1 have opposite parity, we have y=t2ej−2 or y=t2ej−2−1. Hence x=t2ej−1±1 for an integer t. Among the residues modulo 2ej, the only four are ±1 and 2ej−1±1. Therefore,
We can see that, using the periodicity of residues modulo 5, this product can be divided into blocks of length 5, with the only difference between blocks being the residue of the last element. Since dividing 32 by 5 gives quotient 6 and remainder 2, the product can be divided into 6 complete blocks and a final incomplete block of length 2. Thus, we can extract the part of the first 6 blocks except for their last elements (which is exactly what Wilson's theorem handles), multiply by the product of the final incomplete block, and finally multiply by the product of the last elements of the first 6 blocks. The last element of every block is a multiple of 5; after removing powers of 5, their product is exactly (6!)5(mod5). This reduces the original problem to a smaller one.
Generalizing the recursive structure in this example gives the following recurrence:
Recurrence
For a prime p and a positive integer n,
(n!)p≡(−1)⌊n/p⌋⋅(nmodp)!⋅(⌊n/p⌋!)p(modp).Proof
Let (n)p be the result obtained from n by removing all powers of p from its prime factorization. Then
Apart from the last element of each block, the main part of a complete block, (p−1)!modp, is easy to compute; Wilson's theorem gives
(p−1)!≡−1(modp).
There are ⌊pn⌋ complete blocks, so we must place ⌊pn⌋ in the exponent of −1.
The value of the final partial block is (nmodp)!modp, which can be computed separately.
What remains are the last elements of the blocks. If we hide the elements already processed, we can see the following pattern:
(n!)p=…⋅1⋅…⋅2⋅…⋅…⋅(p−1)⋅…⋅1⋅…⋅1⋅…⋅2⋯
This is also a modified factorial, but much shorter. It is
(⌊pn⌋!)p.
Multiplying the parts together gives the recurrence above.
Using this recurrence, the recursion depth is O(logpn). If the intermediate term is recomputed at every step, each level has complexity O(p) and the total time complexity is O(plogpn). If, for all n=1,2,⋯,p−1, n!modp is precomputed, the preprocessing complexity is O(p), each level has complexity O(1), and the total time complexity is O(p+logpn).
In an implementation, this is a tail recursion and can be implemented iteratively. The implementation below precomputes the first p−1 factorials; if it is called many times, the precomputation can be moved outside the function.
Reference implementation
// Calculate (n!)_p mod p.intfactmod(int n, int p){
// Pretreatment.std::vector<int> f(p);
f[0] = 1;
for (int i = 1; i < p; ++i) {
f[i] = (longlong)f[i - 1] * i % p;
}
// Recursion.int res = 1;
while (n > 1) {
if ((n / p) & 1) res = p - res;
res = (longlong)res * f[n % p] % p;
n /= p;
}
return res;
}
If space is limited and it is impossible to store all factorials, we can compute the n!modp values actually used by the function calls, sort the corresponding n values, and then compute these factorials all at once at the end, combining them into the final result without storing every factorial value.
The prime-power-modulus case
The prime-power-modulus case can be handled similarly to the prime-modulus case, replacing Wilson's theorem with its generalized form. In the two conclusions in this section, ±1 is defined specifically as follows: when the modulus satisfies p=2 and α≥3, its value is 1, and otherwise its value is −1.
The proof is exactly analogous to the prime-modulus case. Let (k)p be the result obtained from k by removing all powers of p from its prime factorization. Then
The difference from the prime-modulus case is not only that −1 may need to be replaced by ±1, but also that the data to be precomputed is different. For a prime-power modulus, for every positive integer not exceeding pα, namely n, we need to precompute the product of all integers from 1 to n that are not multiples of p, namely
1≤k≤n,k⊥p∏kmodpα.
For a prime modulus, this degenerates to n!modp, but this expression is no longer applicable for a general prime power.
The following example computes a factorial residue modulo a prime power to illustrate the method above:
Example
To compute (32!)3mod9, we can proceed recursively:
The decomposition of the expression for (32!)3mod9 can likewise be divided into three parts:
Complete blocks: the product of all integers between 1∼9 that are not divisible by 3, with a total of ⌊32/9⌋=3 blocks;
Incomplete tail block: multiply all integers not divisible by 3 from 1 through 32mod9;
The product of all integers divisible by 3. Comparing it with the result at the penultimate equality, we see that this is the first ⌊32/3⌋=10 terms, namely (⌊32/3⌋!)3mod9.
The recurrence in the final parentheses can be solved recursively, reducing the original problem to a smaller one.
Thus, we obtain the following recurrence result:
Recurrence result
For a prime p and positive integers α,n,
(n!)p≡(±1)∑j≥α⌊n/pj⌋j≥0∏F(⌊n/pj⌋modpα),
where F(m)=∏1≤k≤m,k⊥pkmodpα, and the value of ±1 is the same as above.
The implementation for a prime-power modulus is similar to that for a prime modulus, with only a few details differing. As above, the precomputation can also be moved outside the function.
Reference implementation
// Calculate (n!)_p mod pa.intfactmod(int n, int p, int pa){
// Pretreatment.std::vector<int> f(pa);
f[0] = 1;
for (int i = 1; i < pa; ++i) {
f[i] = i % p ? (longlong)f[i - 1] * i % pa : f[i - 1];
}
// Recursion.bool neg = p != 2 || pa <= 4;
int res = 1;
while (n > 1) {
if ((n / pa) & neg) res = pa - res;
res = (longlong)res * f[n % pa] % pa;
n /= p;
}
return res;
}
The preprocessing time complexity is O(pα), and the time complexity of a single query is O(logpn).
Computing exponents
This section discusses the factorial n! and the exponent of p in it, denoted by νp(n!); it can be used to compute binomial-coefficient residues. Since factorials occur in both the numerator and denominator of a binomial coefficient, whether the powers of the prime p in them cancel becomes an important factor determining the final residue.
Legendre's formula
The factorial n! has an exponent of the prime p that can be computed using Legendre's formula, and this exponent is related to the representation of n in base p.
Legendre's formula
For a positive integer n, the factorial n! contains the prime p to the power νp(n!),
νp(n!)=i=1∑∞⌊pin⌋=p−1n−Sp(n),
Here, Sp(n) is the sum of the base-p digits of n. In particular, the power of 2 in a factorial is ν2(n!)=n−S2(n).
Proof
Because
n!=1×2×⋯×p×⋯×2p×⋯×⌊n/p⌋p×⋯×n.
The product of the multiples of p is p×2p×⋯×⌊n/p⌋p=p⌊n/p⌋⌊n/p⌋!, while ⌊n/p⌋! may contain further multiples of p. Therefore, the exponents satisfy the recurrence
νp(n!)=⌊n/p⌋+νp(⌊n/p⌋!).
Expanding it gives Legendre's formula.
To prove the second equality, first expand n in base p, which is equivalent to writing it as the following sum:
The reference implementation for finding the exponent of a prime in a factorial is as follows:
Reference implementation
// Obtain multiplicity of p in n!.intmultiplicity_factorial(int n, int p){
int count = 0;
do {
n /= p;
count += n;
} while (n);
return count;
}
Its time complexity is O(logn).
Kummer's theorem
The result of reducing binomial coefficients modulo a number often forms a fractal structure; for example, the Sierpiński triangle can be obtained from binomial coefficients modulo 2.
A careful analysis shows that whether p divides a binomial coefficient is related to whether subtracting the lower index from the upper index in base p requires a borrow. This gives Kummer's theorem.
Kummer's theorem
The exponent of the prime p in the binomial coefficient (nm) is exactly the number of borrows required when, in base p, subtracting m by n, namely
νp((nm))=p−1Sp(n)+Sp(m−n)−Sp(m).
In particular, the exponent of 2 in a binomial coefficient is ν2((nm))=S2(n)+S2(m−n)−S2(m).
Proof
We first prove the following expression. By Legendre's formula,
This expression can be understood as the number of borrows required when, in base p, subtracting m by n. If, when computing digit i (with the lowest digit indexed by 1), the prefix before digit i in the result, ⌊pim−n⌋, is actually the prefix of m before digit i, ⌊pim⌋, minus one (the borrowed one), minus the prefix of n before digit i, ⌊pin⌋, so the difference
⌊pim⌋−⌊pin⌋−⌊pim−n⌋=1
if and only if one borrow occurs; otherwise, this difference is 0. Therefore, the sum in the expression above can be understood as the number of borrows. This gives the verbal statement of Kummer's theorem.
Example problems
Example problem HDU 2973 - YAPTCHA
Given n, compute
k=1∑n⌊3k+7(3k+6)!+1−⌊3k+7(3k+6)!⌋⌋Solution idea
If 3k+7 is prime, then
(3k+6)!≡−1(mod3k+7)
Let (3k+6)!+1=k(3k+7)
Then
⌊3k+7(3k+6)!+1−⌊3k+7(3k+6)!⌋⌋=⌊k−⌊k−3k+71⌋⌋=1
If 3k+7 is not prime, then (3k+7)∣(3k+6)!, that is,
(3k+6)!≡0(mod3k+7)
Let (3k+6)!=k(3k+7); then
⌊3k+7(3k+6)!+1−⌊3k+7(3k+6)!⌋⌋=⌊k+3k+71−k⌋=0
Therefore,
k=1∑n⌊3k+7(3k+6)!+1−⌊3k+7(3k+6)!⌋⌋=k=1∑n[3k+7 is prime]Reference code
#include<iostream>constexprint M = 1e6 + 5, N = 3 * M + 7;
bool not_prime[N];
int sum[M];
intmain(){
for (int i = 2; i < N; ++i)
if (!not_prime[i])
for (int j = 2; j * i < N; ++j) not_prime[j * i] = true;
for (int i = 1; i < M; ++i) sum[i] = sum[i - 1] + !not_prime[3 * i + 7];
int t;
std::cin >> t;
while (t--) {
int n;
std::cin >> n;
std::cout << sum[n] << std::endl;
}
}
References
Feng Keqin. Elementary Number Theory and Its Applications.
This page is primarily translated from the blog post Вычисление факториала по модулю and its English translation Factorial modulo p. The Russian version is licensed under Public Domain + Leave a Link; the English version is licensed under CC-BY-SA 4.0. The content has been modified.