Skip to content

Commit a6cba9a

Browse files
committed
Modular Multiplicative Inverse
1 parent f5a2664 commit a6cba9a

File tree

7 files changed

+224
-1
lines changed

7 files changed

+224
-1
lines changed

lib/cpalgo/algebra/README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,25 @@ We can compute the solution in `O(log(min(a,b)))`.
109109
- [Extended Euclidean Algorithm - Competitive Programming Algorithms](https://cp-algorithms.com/algebra/extended-euclid-algorithm.html)
110110

111111
### Challenges
112-
- [Extended Euclidean Algorithm - AOJ NTL1E](https://onlinejudge.u-aizu.ac.jp/courses/library/6/NTL/1/NTL_1_E)
112+
- [Extended Euclidean Algorithm - AOJ NTL1E](https://onlinejudge.u-aizu.ac.jp/courses/library/6/NTL/1/NTL_1_E)
113+
114+
115+
## Modular Multiplcative Inverse
116+
A *Modular Multiplicative Inverse* of an integer `a` is an integer `x` such that the product `ax` is congruent to 1 in modulus `m`.
117+
```math
118+
ax = 1 (mod m)
119+
x = a^{-1} (mod m)
120+
```
121+
122+
- [Modular Multiplicative Inverse (using Fermat's little theorem and Binary Exponentation) | C++ code](modinv_binexp.hpp)
123+
- [Modular Multiplicative Inverse (using Extended Euclidean Algorithm) | C++ code](modinv_extgcd.hpp)
124+
- [All Modular Multiplicative Inverse | C++ code](modinvall.hpp)
125+
126+
### References in English
127+
- [Modular multiplicative inverse - Wikipedia](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse)
128+
- [Modular Inverse - Competitive Programming Algorithms](https://cp-algorithms.com/algebra/module-inverse.html)
129+
130+
### Challenges
131+
- [D - 動的計画法](https://atcoder.jp/contests/abc024/tasks/abc024_d)
132+
- [E - Flatten](https://atcoder.jp/contests/abc152/tasks/abc152_e)
133+
- [E - Throne](https://atcoder.jp/contests/abc186/tasks/abc186_e)

lib/cpalgo/algebra/modinv_binexp.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
#include "binexp.hpp"
4+
5+
6+
// Modular Multiplicative Inverse
7+
//
8+
// (a ** -1) mod m
9+
// where `m` is an prime number
10+
// undefined behavior if `m` is not an prime number
11+
//
12+
// Complexity:
13+
// Time: O(log(m))
14+
// Space: O(1)
15+
//
16+
// Algorithm:
17+
// Fermat's little theorem and Binary Exponentation
18+
// { a ** (m - 1) = 1 } mod m
19+
// { a ** (m - 2) = a ** -1 } mod m
20+
//
21+
// NOTE:
22+
// If `m` is not an prime but `a` and `m` is coprime,
23+
// there is the Modular Multiplicative Inverse of `a`.
24+
// Use an another solution using Extended Euclidean Algorithm.
25+
//
26+
// We can solve it using Euler's theorem and Binary Exponentation, but it might be hard.
27+
// Because we have to calculate Euler's Totient function that requires prime factorization of `m`.
28+
// { a ** phi(m) = 1 } mod m
29+
// { a ** (phi(m) - 1) = a ** -1 } mod m
30+
// where phi(m) is Euler's Totient function.
31+
//
32+
int64_t modinv_binexp(int64_t a, int64_t m) {
33+
return binexp<int64_t>(a, m - 2, 1, [&](auto const& lhs, auto const& rhs){
34+
return lhs * rhs % m;
35+
});
36+
}

lib/cpalgo/algebra/modinv_extgcd.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include "extgcd_iterative.hpp"
4+
5+
6+
// Modular Multiplicative Inverse
7+
//
8+
// returns (a ** -1) mod m
9+
// where gcd(a,m) == 1
10+
// returns -1 if gcd(a,m) != 1
11+
//
12+
// Complexity:
13+
// Time: O( log(min(a,b)) )
14+
// Space: O(1)
15+
//
16+
// Algorithm:
17+
// Extended Euclidean Algorithm
18+
// a * x + m * y = 1
19+
// (a * x + m * y = 1) mod m
20+
// (a * x = 1) mod m
21+
// (x = a ** -1) mod m
22+
//
23+
int64_t modinv(int64_t a, int64_t m) {
24+
int64_t x, y;
25+
auto g = extgcd(a, m, x, y);
26+
if (g != 1) {
27+
// There is no modular multiplicative inverse
28+
return -1;
29+
}
30+
return (x % m + m) % m;
31+
}

lib/cpalgo/algebra/modinvall.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
#include <vector>
4+
5+
6+
// Modular Multiplicative Inverse for every number modulo `m`
7+
// `m` must be an prime number
8+
// undefined behavior if `m` is not the prime number
9+
//
10+
// Complexity:
11+
// Time: O(M)
12+
// Space: O(M)
13+
//
14+
// Algorithm:
15+
// m mod i = m - floor(m / i) * i
16+
// m mod i = { m - floor(m / i) * i } mod m
17+
// m mod i = { - floor(m / i) * i } mod m
18+
// (m mod i) * (i ** -1) * ((m mod i) ** -1) =
19+
// { - floor(m / i) * i * (i ** -1) * ((m mod i) ** 0) } mod m
20+
// i ** -1 =
21+
// - floor(m / i) * { (m mod i) ** - 1 } mod m
22+
// i ** -1 =
23+
// m - floor(m / i) * { (m mod i) ** -1 } mod m
24+
//
25+
// NOTE:
26+
// inverse[M % i] can be calculated using inverse[1..i-1]
27+
// - i < M
28+
// - M % i < i
29+
// - M % i != 0 if M is the prime number
30+
//
31+
std::vector<int> modinvall(int M) {
32+
std::vector<int> inverse(M, 0);
33+
// inserve[0] is undefined
34+
inverse[1] = 1;
35+
for (int i = 2; i < M; ++i) {
36+
inverse[i] = M - (M / i) * inverse[M % i] % M;
37+
}
38+
return inverse;
39+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <bits/stdc++.h>
2+
#include "gtest/gtest.h"
3+
#include "cpalgo/algebra/modinv_binexp.hpp"
4+
5+
TEST(ModinvBinexpTest, ComputeInverseInModulo7) {
6+
EXPECT_EQ(1, modinv_binexp(1, 7));
7+
EXPECT_EQ(4, modinv_binexp(2, 7));
8+
EXPECT_EQ(5, modinv_binexp(3, 7));
9+
EXPECT_EQ(2, modinv_binexp(4, 7));
10+
EXPECT_EQ(3, modinv_binexp(5, 7));
11+
EXPECT_EQ(6, modinv_binexp(6, 7));
12+
}
13+
14+
TEST(ModinvBinexpTest, ComputeInverseInModulo13) {
15+
EXPECT_EQ( 1, modinv_binexp( 1, 13));
16+
EXPECT_EQ( 7, modinv_binexp( 2, 13));
17+
EXPECT_EQ( 9, modinv_binexp( 3, 13));
18+
EXPECT_EQ(10, modinv_binexp( 4, 13));
19+
EXPECT_EQ( 8, modinv_binexp( 5, 13));
20+
EXPECT_EQ(11, modinv_binexp( 6, 13));
21+
EXPECT_EQ( 2, modinv_binexp( 7, 13));
22+
EXPECT_EQ( 5, modinv_binexp( 8, 13));
23+
EXPECT_EQ( 3, modinv_binexp( 9, 13));
24+
EXPECT_EQ( 4, modinv_binexp(10, 13));
25+
EXPECT_EQ( 6, modinv_binexp(11, 13));
26+
EXPECT_EQ(12, modinv_binexp(12, 13));
27+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include <bits/stdc++.h>
2+
#include "gtest/gtest.h"
3+
#include "cpalgo/algebra/modinv_extgcd.hpp"
4+
5+
TEST(ModinvExtgcdTest, ComputeInverseInModulo7) {
6+
EXPECT_EQ(1, modinv(1, 7));
7+
EXPECT_EQ(4, modinv(2, 7));
8+
EXPECT_EQ(5, modinv(3, 7));
9+
EXPECT_EQ(2, modinv(4, 7));
10+
EXPECT_EQ(3, modinv(5, 7));
11+
EXPECT_EQ(6, modinv(6, 7));
12+
}
13+
14+
TEST(ModinvExtgcdTest, ComputeInverseInModulo9) {
15+
EXPECT_EQ( 1, modinv(1, 9));
16+
EXPECT_EQ( 5, modinv(2, 9));
17+
EXPECT_EQ(-1, modinv(3, 9));
18+
EXPECT_EQ( 7, modinv(4, 9));
19+
EXPECT_EQ( 2, modinv(5, 9));
20+
EXPECT_EQ(-1, modinv(6, 9));
21+
EXPECT_EQ( 4, modinv(7, 9));
22+
EXPECT_EQ( 8, modinv(8, 9));
23+
}
24+
25+
TEST(ModinvExtgcdTest, ComputeInverseInModulo13) {
26+
EXPECT_EQ( 1, modinv( 1, 13));
27+
EXPECT_EQ( 7, modinv( 2, 13));
28+
EXPECT_EQ( 9, modinv( 3, 13));
29+
EXPECT_EQ(10, modinv( 4, 13));
30+
EXPECT_EQ( 8, modinv( 5, 13));
31+
EXPECT_EQ(11, modinv( 6, 13));
32+
EXPECT_EQ( 2, modinv( 7, 13));
33+
EXPECT_EQ( 5, modinv( 8, 13));
34+
EXPECT_EQ( 3, modinv( 9, 13));
35+
EXPECT_EQ( 4, modinv(10, 13));
36+
EXPECT_EQ( 6, modinv(11, 13));
37+
EXPECT_EQ(12, modinv(12, 13));
38+
}
39+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <bits/stdc++.h>
2+
#include "gtest/gtest.h"
3+
#include "cpalgo/algebra/modinvall.hpp"
4+
5+
TEST(ModInvAllTest, ComputeAllInverseInModulo7) {
6+
auto inv = modinvall(7);
7+
EXPECT_EQ(1, inv[1]);
8+
EXPECT_EQ(4, inv[2]);
9+
EXPECT_EQ(5, inv[3]);
10+
EXPECT_EQ(2, inv[4]);
11+
EXPECT_EQ(3, inv[5]);
12+
EXPECT_EQ(6, inv[6]);
13+
}
14+
15+
TEST(ModinvExtgcdTest, ComputeAllInverseInModulo13) {
16+
auto inv = modinvall(13);
17+
EXPECT_EQ( 1, inv[1]);
18+
EXPECT_EQ( 7, inv[2]);
19+
EXPECT_EQ( 9, inv[3]);
20+
EXPECT_EQ(10, inv[4]);
21+
EXPECT_EQ( 8, inv[5]);
22+
EXPECT_EQ(11, inv[6]);
23+
EXPECT_EQ( 2, inv[7]);
24+
EXPECT_EQ( 5, inv[8]);
25+
EXPECT_EQ( 3, inv[9]);
26+
EXPECT_EQ( 4, inv[10]);
27+
EXPECT_EQ( 6, inv[11]);
28+
EXPECT_EQ(12, inv[12]);
29+
}
30+

0 commit comments

Comments
 (0)