I suspect you have not yet taken any courses in number theory or linear algebra or the like.
The Chinese Remainder Theorem is a way of finding a number that satisfies two or more
congruences.
A
congruence is the relationship to a division’s remainder (not caring what the quotient is).
That is, the following two things mean the same thing:
x ≡ r (mod d)
x = dq + r
Where r is the remainder, d is the divisor, and q is the quotient, and all of them are non-negative integers. The three-line equal sign is read “is congruent to”. So “x is congruent to r modulo d”.
The second way of writing it is nice and algebraic.
The first way of writing it is just a convenience, and the standard notation (in the Western world) for writing it is “x = a (mod n)”. (And k is often used instead of q.)
To make sense, here is an example using concrete values.
16 ≡ 1 (mod 5) → 16 = 5k + 1 → 16 = 5(3) + 1
One other thing to notice is that x and a are interchangeable when written using the congruence notation. Hence, the following two are the same:
4 ≡ 1 (mod 3)
1 ≡ 4 (mod 3)
This is because the “(mod 3)” applies to both sides of the “≡”. Hopefully this point makes the congruence notation fairly obvious.
1 ≡ 1 (mod 3)
4 ≡ 1 (mod 3)
7 ≡ 1 (mod 3)
10 ≡ 1 (mod 3)
etc
You are asked to solve a system of two equations.
Fortunately, there is a very simple way of thinking about this. We can look at our last example, and realize we have an arithmetic sequence.
1 + 3 + 3 + 3 + … → 1, 4, 7, 10, …
Using our congruence notation, that’s:
x ≡ a (mod n) → a + n + n + n + …
Let us solve a simple system as an example. Given:
x = 2 (mod 3)
x = 3 (mod 5)
3 and 5 are coprime, so we are good to go:
x → 2 + 3 + … → 2, 5, 8, 11, 14, 17, 20, 23, …
x → 3 + 5 + … → 3, 8, 13, 18, 23, …
The common subsequence is:
x → 8, 23, …
Just choose the first element in the sequence: x = 8. That’s it!
Both of the following are true:
8 ≡ 2 (mod 3)
8 ≡ 3 (mod 5)
But, the modulus?
Yes, you also need to return a new modulus. Since all the modulii are coprime, just multiply them together. So:
x = 8 (mod 15)
Final answer.
Notice something cool? The answer is less than the divisor. 8 < 15. This makes sense, right?
How to turn this into code
You need
a loop that calculates the two arithmetic sequences, incrementing to the next element of whichever sequence has the smaller value. Using the example above:
x = a (mod m) → a = 2, m = 3
x = b (mod n) → b = 3, n = 5
Since (a < b), we’ll add m=3 to a:
a = 5
b = 3
Now (b < a), so we’ll add n=5 to b:
a = 5
b = 8
Again, (a < b), so add m=3 to a:
a = 8
b = 8
Finally, (a == b), and we can return a=b=8:
Again, put it in a loop.
This finishes your
long crt(long a, long m, long b, long n)
function.
Next, in your
Mod crt(Mod a, Mod b)
function, you only need call the first
crt() function with the appropriate pieces of the
a and
b arguments:
|
long x = crt(a.val, a.mod, b.val, b.mod);
|
Then compose a new
Mod object with the
x and (
a.mod*b.mod) values.
This is a very simple, brute force solution. For the numbers you are working with it will work just fine. But it is certainly possible to improve this significantly.
Error handling
It appears that your function will never be given non-coprime divisors. If that is the case, then you do not need to check. But if it is not, you should first verify that your two
mod values are coprime. C++ provides the
gcd() function (include <numeric>).
1 2
|
if (std::gcd( a.mod, b.mod ) != 1)
throw "divisors not coprime, cannot use Chinese Remainder Theorem";
|
I do not see a way to indicate failure in your code; hence I threw an exception. There are all kinds of other ways to indicate failure. For example, you could make your Mod object invalid by setting
mod = 0
.
Again, your instructions very loosely imply that the divisors are guaranteed to be coprime by the time your
crt() functions get it. This is a point I would clarify with the instructor (or with any notes you have not shared with us).
Hope this helps.
I’ve got to stop making answers this pretty. I’m gonna give myself carpal tunnel syndrome…