Hello everyone, this is my first time writing a blog. So pardon any mistakes. I am writing this because I genuinely curious if there is sub-linear approach to find factorials of a large number (1e9) under some modulo. Like any approach better than O(n)? Such as O(n/B), O(√n) or any less/more around them.








Auto comment: topic has been updated by Niyati_Parekh (previous revision, new revision, compare).
As far as I know, there is no sublinear way to compute n! mod m in general. Since n! = 1 × 2 × … × n, every number contributes to the result, so you basically have to process all terms. That makes it O(n) in most cases. There are some special cases though. For example, if m is prime and n >= m, then n! == 0 (mod m). Also, when n is close to a prime modulus, Wilson’s theorem can sometimes help. But for large n like 1e9 and general m, I dont think anything better than linear time exists.
Hey! I saw the previous reply and while it's a solid baseline, it actually misses out on some high-level "magic" used in competitive programming and number theory to break that O(n) barrier. If you're looking for 10 9 specifically, you're right on the edge where O(n) might TLE (Time Limit Exceeded), but sub-linear approaches absolutely exist.
Here is the deep dive into how we actually go sub-linear:
logn) Polynomial Approach When the modulus m is a prime p, we don't actually have to multiply every number. We can treat the factorial as a product of values of a polynomial.
Imagine we split n into B blocks, where B≈ n
. We define a polynomial:
P(x)=(x+1)(x+2)…(x+B)
Now, if we evaluate this polynomial at x=0,B,2B,…,(B−1)B, we get the product of each block. For example:
P(0)=1⋅2⋅⋯⋅B
P(B)=(B+1)⋅(B+2)⋅⋯⋅2B
By using Fast Multipoint Evaluation (which uses the Fast Fourier Transform or FFT), we can evaluate a degree-B polynomial at B different points in O(Blog 2 B) time. Since B= n
, your total complexity becomes roughly O( n
log 2 n). For n=10 9 ,
n
is only about 31,622, which is well within the limits for a 1-second execution time!
(p−1)!≡−1(modp)
Since (p−1)!=n!⋅(n+1)⋅(n+2)…(p−1), you can find n! by calculating the product of the small range from (n+1) to (p−1) and then finding its modular inverse. This turns an O(n) problem into an O(p−n) problem.
E p (n!)= k=1 ∑ ∞ ⌊ p k
n ⌋
This is O(log p n), which is incredibly fast—practically instantaneous for 10 9 .
Summary Table of Approaches Scenario Complexity Technique General n<10 8 O(n) Standard Iteration Prime m, n≈10 9 O( n
logn) FFT + Multipoint Evaluation n close to m O(m−n) Wilson's Theorem + Modular Inverse n≥m O(1) Result is 0(modm) So, while the "naive" approach is linear, the mathematical reality is that we can squeeze a lot of performance out of the structure of factorials. If you're dealing with a prime modulus, that
n
jump is your best friend.
Would you like to see a breakdown of how the Chinese Remainder Theorem allows us to apply these tricks even when the modulus isn't prime?
Thank you ChatGPT.
Yes, please, proceed with the breakdown of how Chinese Remainder Theorem allows us to apply these tricks even when the modulus isn't prime.
you can precompute
n!forn % 1e7 == 0orn % 1e6 == 0, and it is O(1e6).First of all, when I need to compute large factorials modulo, say, $$$m = 998\,244\,353$$$, I run it locally up to one billion and print every like 500000-th and then store this as a precomputed vector in my code (like
const vector<int> facts = {1, 832944090, 373341033, 797725335, 45596018, ...}). Then I can compute any $$$k!\bmod{m}$$$ by just taking the closest precomputed factorial not exceeding $$$k$$$ and multiply by at most 500000 remaining numbers, usually that's okay for my purposes.But answering your question, let us choose some $$$B$$$ and find the coefficients of the polynomial $$$P(x) = x(x+1)(x+2)\dots(x+B-1)$$$. It can be done in $$$O(B\log{B})$$$, see "Stirling numbers of the first kind". Then compute the values of this polynomial at points $$$1$$$, $$$B+1$$$, $$$2B+1$$$ and so on. It can be done in $$$O((m/B)\log{B}\log\max(B, m/B))$$$ or something, see "multipoint evaluation".
Now, to compute $$$k! \bmod{m}$$$, you multiply about $$$k/B$$$ precomputed values and at most $$$B$$$ single remaining values. So taking $$$B\approx\sqrt{m}$$$ gives you $$$\sqrt{m}\log^2m$$$ precalc time + $$$\sqrt{m}$$$ per query, taking $$$B\approx\sqrt{m\log{m}}$$$ gives you $$$\sqrt{m}\log^{3/2}m$$$ precalc time + $$$\sqrt{m\log{m}}$$$ per query, etc.
If you're processing the queries in offline, there's an even better solution: https://github.com/yosupo06/library-checker-problems/issues/1058. As stated, it runs in $$$O((Q+\sqrt{M}) \log^2(Q+\sqrt{M}))$$$ time.
For the actual fastest solution that I know, which is optimized $$$O(M+Q \log(M))$$$: https://codeforces.me/blog/entry/143279.
For the online algorithm, https://judge.yosupo.jp/submission/296988 is relatively fast for $$$O(M+Q\cdot B \log(M))$$$ (in this case $$$B = 256$$$).
I don't know if it answers Your question, but fast (say, polylogarithmic) oracle for $$$n! (mod k)$$$ would imply fast (polylogarithmic) oracle for factorization of $$$k$$$, which is thought to be hard at least on classical computer/Turing machine.
Why: binary search for $$$x$$$ over $$$[1, k]$$$, query oracle to get $$$f = x! (mod k)$$$, compute $$$gcd(f,k)$$$ (Euclid, polylog too) and compare to $$$1$$$. Smallest $$$x$$$ with $$$gcd \neq 1$$$ is the smallest prime factor $$$p$$$ of $$$k$$$. Divide $$$k$$$ by $$$p$$$ and repeat until $$$k = 1$$$ to get all factors.
Also: if entire $$$n!$$$ is needed (not just modulo): $$$n!$$$ contains $$$O(nlogn)$$$ digits, so can't be quicker than that.
Thank you everyone for you support and suggestions. I will surely try them out.