-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Chinese Remainder Theorem solutions #1016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d1d7a59
a82f445
94eda35
52e3679
0a38326
59fa075
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ Let $m = m_1 \cdot m_2 \cdots m_k$, where $m_i$ are pairwise coprime. In additio | |
$$\begin{align} | ||
a &\equiv a_1 \pmod{m_1} \\ | ||
a &\equiv a_2 \pmod{m_2} \\ | ||
&\ldots \\ | ||
& \vdots \\ | ||
a &\equiv a_k \pmod{m_k} | ||
\end{align}$$ | ||
|
||
|
@@ -31,12 +31,59 @@ is equivalent to the system of equations | |
|
||
$$\begin{align} | ||
x &\equiv a_1 \pmod{m_1} \\ | ||
&\ldots \\ | ||
&\vdots \\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dito |
||
x &\equiv a_k \pmod{m_k} | ||
\end{align}$$ | ||
|
||
(As above, assume that $m = m_1 m_2 \cdots m_k$ and $m_i$ are pairwise coprime). | ||
|
||
## Solution for Two Moduli | ||
|
||
Consider a system of two equations for coprime $m_1, m_2$: | ||
|
||
$$ | ||
\begin{align} | ||
a &\equiv a_1 \pmod{m_1} \\ | ||
a &\equiv a_2 \pmod{m_2} \\ | ||
\end{align} | ||
$$ | ||
|
||
We want to find a solution for $a \pmod{m_1 m_2}$. Using the [Extended Euclidean Algorithm](extended-euclid-algorithm.md) we can find Bézout coefficients $n_1, n_2$ such that | ||
|
||
$$n_1 m_1 + n_2 m_2 = 1$$ | ||
|
||
Equivalently, $n_1 m_1 \equiv 1 \pmod{m_2}$ so $n_1 \equiv m_1^{-1} \pmod{m_2}$, and vice versa $n_2 \equiv m_2^{-1} \pmod{m_1}$. | ||
|
||
Then a solution will be | ||
|
||
$$a = a_1 n_2 m_2 + a_2 n_1 m_1$$ | ||
|
||
We can easily verify $a = a_1 (1 - n_1 m_1) + a_2 n_1 m_1 \equiv a_1 \pmod{m_1}$ and vice versa. | ||
|
||
## Solution for General Case | ||
|
||
### Inductive Solution | ||
|
||
As $m_1 m_2$ is coprime to $m_3$, we can inductively repeatedly apply the solution for two moduli for any number of moduli. For example, combine $a \equiv b_2 \pmod{m_1 m_2}$ and $a \equiv a_3 \pmod{m_3}$ to get $a \equiv b_3 \pmod{m_1 m_2 m_3}$, etc. | ||
|
||
### Direct Construction | ||
|
||
A direct construction similar to Lagrange interpolation is possible. Let $M_i = \prod_{i \neq j} m_j$, the product of all moduli but $m_i$. Again with the Extended Euclidean algorithm we can find $N_i, n_i$ such that | ||
|
||
$$N_i M_i + n_i m_i = 1$$ | ||
|
||
Then a solution to the system of congruences is | ||
|
||
$$a = \sum_{i=1}^k a_i N_i M_i$$ | ||
|
||
Again as $N_i \equiv M_i^{-1} \pmod{m_i}$, the solution is equivalent to | ||
|
||
$$a = \sum_{i=1}^k a_i M_i (M_i^{-1} \mod{m_i})$$ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
Observe $M_i$ is a multiple of $m_j$ for $i \neq j$, and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it makes more sense to write |
||
|
||
$$a \equiv a_i N_i M_i \equiv a_i (1 - n_i m_i) \equiv a_i \pmod{m_i}$$ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, I'm not sure if you saw this (as I posted it on a closed issue). #1013 I think we should add an implementation. E.g. one in C++. And if it's not fine for some harder problem, then the reader can himself use/implement a big integer library or switch to some other language. This here is my personal implementation for the algorithm you explained (with some different variable names though). struct Congruence
{
long long a, m, totient_m;
};
class CRT
{
public:
void add_congruence(long long a, long long m, long long totient_m) {
congruences.push_back({a, m, totient_m});
}
long long get_unique_solution() {
long long M = 1;
for (const auto& c : congruences) {
M *= c.m;
}
long long solution = 0;
for (const auto& c : congruences) {
auto b = M / c.m;
solution = (solution + c.a * b % M * power(b, c.totient_m - 1, c.m)) % M;
}
return solution;
}
std::vector<Congruence> congruences;
}; |
||
## Garner's Algorithm | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The transition to Garner's algorithm is a bit wonky. As Garner's algorithm computes the same thing, except with a worse time complexity... If we add your explanation on the top, we should write a couple of word, like: And I'm not sure if we should move this paragraph about representing a very large number using a system of smaller ones. |
||
|
||
Another consequence of the CRT is that we can represent big numbers using an array of small integers. For example, let $p$ be the product of the first $1000$ primes. From calculations we can see that $p$ has around $3000$ digits. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly, this looks a bit off. The dots are not aligned properly.

A fix would be to use an array instead of align: