8000 Merge pull request #989 from duckladydinh/patch-1 · cp-algorithms/cp-algorithms@de564d8 · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit de564d8

Browse files
authored
Merge pull request #989 from duckladydinh/patch-1
Update modular inverse using Euclidean Division.
2 parents 13ca02d + 36df05d commit de564d8

File tree

2 files changed

+54
-29
lines changed

2 files changed

+54
-29
lines changed

src/algebra/module-inverse.md

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -76,45 +76,42 @@ From these results, we can easily find the modular inverse using the [binary exp
7676

7777
Even though this method is easier to understand than the method described in previous paragraph, in the case when $m$ is not a prime number, we need to calculate Euler phi function, which involves factorization of $m$, which might be very hard. If the prime factorization of $m$ is known, then the complexity of this method is $O(\log m)$.
7878

79-
## Finding the modular inverse for every number modulo $m$ { #mod-inv-all-num data-toc-label="Finding the modular inverse for every number modulo m"}
79+
## Finding the modular inverse using Euclidean Division
8080

81-
The problem is the following:
82-
we want to compute the modular inverse for every number in the range $[1, m-1]$.
81+
Given that $m > i$ (or we can modulo to make it smaller in 1 step), according to [Euclidean Division](https://en.wikipedia.org/wiki/Euclidean_division)
8382

84-
Applying the algorithms described in the previous sections, we can obtain a solution with complexity $O(m \log m)$.
83+
$$m = k \cdot i + r$$
8584

86-
Here we present a better algorithm with complexity $O(m)$.
87-
However for this specific algorithm we require that the modulus $m$ is prime.
85+
where $k = \left\lfloor \frac{m}{i} \right\rfloor$ and $r = m \bmod i$, then
8886

89-
We denote by $\text{inv}[i]$ the modular inverse of $i$. Then for $i > 1$ the following equation is valid:
90-
91-
$$\text{inv}[i] = - \left\lfloor \frac{m}{i} \right\rfloor \cdot \text{inv}[m \bmod i] \bmod m$$
87+
$$
88+
\begin{align*}
89+
& \implies & 0 & \equiv k \cdot i + r & \mod m \\
90+
& \iff & r & \equiv -k \cdot i & \mod m \\
91+
& \iff & r \cdot i^{-1} & \equiv -k & \mod m \\
92+
& \iff & i^{-1} & \equiv -k \cdot r^{-1} & \mod m
93+
\end{align*}
94+
$$
9295

93-
Thus the implementation is very simple:
96+
From there we can have the following recursive function (in C++) for computing the modular inverse for number $i$ with respect to module $m$
9497

95-
```cpp
96-
inv[1] = 1;
97-
for(int i = 2; i < m; ++i)
98-
inv[i] = m - (m/i) * inv[m%i] % m;
98+
```{.cpp file=modular_inverse_euclidean_division}
99+
int inv(int i) {
100+
return i <= 1 ? i : m - (long long)(m/i) * inv(m % i) % m;
101+
}
99102
```
100103
101-
### Proof
102-
103-
We have:
104+
The exact time complexity of the this recursion is not known. It's is somewhere between $O(\frac{\log m}{\log\log m})$ and $O(n^{\frac{1}{3} - \frac{2}{177} + \epsilon})$.
105+
See [On the length of Pierce expansions](https://arxiv.org/abs/2211.08374).
106+
In practice this implementation is fast, e.g. for the modulus $10^9 + 7$ it will always finish in less than 50 iterations.
104107
105-
$$m \bmod i = m - \left\lfloor \frac{m}{i} \right\rfloor \cdot i$$
108+
Applying this formula, we can also precompute the modular inverse for every number in the range $[1, m-1]$ in $O(m)$.
106109
107-
Taking both sides modulo $m$ yields:
108-
109-
$$m \bmod i \equiv - \left\lfloor \frac{m}{i} \right\rfloor \cdot i \mod m$$
110-
111-
Multiply both sides by $i^{-1} \cdot (m \bmod i)^{-1}$ yields
112-
113-
$$(m \bmod i) \cdot i^{-1} \cdot (m \bmod i)^{-1} \equiv -\left\lfloor \frac{m}{i} \right\rfloor \cdot i \cdot i^{-1} \cdot (m \bmod i)^{-1} \mod m,$$
114-
115-
which simplifies to:
116-
117-
$$i^{-1} \equiv -\left\lfloor \frac{m}{i} \right\rfloor \cdot (m \bmod i)^{-1} \mod m,$$
110+
```{.cpp file=modular_inverse_euclidean_division_all}
111+
inv[1] = 1;
112+
for(int i = 2; i < m; ++i)
113+
inv[i] = m - (long long)(m/i) * inv[m%i] % m;
114+
```
118115

119116
## Finding the modular inverse for array of numbers modulo $m$
120117

test/test_modular_inverse.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <cassert>
2+
#include <initializer_list>
3+
#include <vector>
4+
using namespace std;
5+
6+
namespace EuclideanDivision {
7+
int m;
8+
#include "modular_inverse_euclidean_division.h"
9+
} // namespace EuclideanDivision
10+
11+
int main() {
12+
for (int m : {307, 1'000'000'007}) {
13+
for (int x : {1, 5, 100, 152, 299}) {
14+
EuclideanDivision::m = m;
15+
int x_inv = EuclideanDivision::inv(x);
16+
assert((long long)x * x_inv % m == 1);
17+
}
18+
}
19+
20+
for (int m : {307, 12347}) {
21+
vector<int> inv(m);
22+
#include "modular_inverse_euclidean_division_all.h"
23+
24+
for (int x = 1; x < m; x++) {
25+
assert((long long)x * inv[x] % m == 1);
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)
0