-
-
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
Conversation
Visit the preview URL for this PR (for commit 0a38326): https://cp-algorithms--preview-1016-hbaim2kn.web.app (expires 2023-01-23T21:41:25.445532701Z) |
Also I can add a note about relationship between Bezout's identity and modular inverses for the general case |
Visit the preview URL for this PR (for commit 59fa075): https://cp-algorithms--preview-1016-hbaim2kn.web.app (expires 2023-01-24T23:42:41.035506657Z) |
@@ -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 \\ |
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.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
Dito
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
\bmod{m_i}
looks better here.
Observe $M_i$ is a multiple of $m_j$ for $i \neq j$, and | ||
|
||
$$a \equiv a_i N_i M_i \equiv a_i (1 - n_i m_i) \equiv a_i \pmod{m_i}$$ | ||
|
||
## Garner's Algorithm |
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.
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:
"Garners algorithm is an alternative approach for computing
And I'm not sure if we should move this paragraph about representing a very large number using a system of smaller ones.
I want to keep it somewhere, as it's important. I think I used this technique a couple of times before. Where this really had some time benefit. But it's a bit lost in this section.
Observe $M_i$ is a multiple of $m_j$ for $i \neq j$, and | ||
|
||
$$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 comment
The 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 we don't need to worry about large numbers. In most contests, as far as I remember, M is not that big, and usually fits inside an 32 bit integer. So doing all computations is with 64 bit integers is fine.
E.g. a prime example is Code Golf: Golf Gophers. The solution requires the CRT to find the solution, but all computations can be done without any big integer library.
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.
It's not necessary for an educational website to provide copy-pasteable snippets for every single usecase.
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;
};
|
||
$$a = \sum_{i=1}^k a_i M_i (M_i^{-1} \mod{m_i})$$ | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
it makes more sense to write
Can you make the requested changes? I'm not familiar with PRs, so do I need to make the changes? Any changes are fine by me. When I get back to my computer I can make the modifications. |
Yes, I should have the rights to make the changes (and in fact if I don't, I could also just merge the PR and make the changes afterwards). But I'm not gonna do it today. |
Maybe Garner's algorithm should have its own article. It seems different enough |
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.
I'm gonna merge this, and then apply my fixes afterwards.
I've improved some of the stuff I've mentioned above, moved Garner's algorithm to another article, and also added a section about non-coprime moduli.
For #1008 since the current page is lacking explanation on actual solutions.
TODO: add C++ sample code