Sam
Oct 05 2020 at 15:14 GMT
With Euclid's algorithm, we can find the greatest common divisor (GCD) of two integers and . It can be proven that the GCD is the smallest positive integer linear combination of and . Thus, we can write the GCD as a linear combination of and :
The extended Euclidean algorithm allows to find not only the GCD, but also the values of the coefficients and .
I would like some explanations of how this algorithm works.
Carl
Oct 05 2020 at 17:47 GMT
The idea of the extended Euclidean algorithm is to keep track of how each encountered remainder can be written as a linear combination of and . This way, once we reach the last non-zero remainder, which corresponds to the GCD, we know how to express it as a linear combination of and .
Let's find the GCD of 221 and 91 using the extended Euclidean algorithm.
According to the Division theorem, we can write 221 as 91 times the quotient plus the remainder:
Thus, .
We can express this first remainder (39) as a linear combination of 221 and 91 by rearranging the terms:
Let's continue with the algorithm:
By the Division theorem:
So, .
Let's express 13 as a linear combination of 221 and 91:
Let's continue:
By the Division theorem:
Thus, we reached a zero remainder: .
Thus, the GCD of 221 and 91 is 13, and we know how to express 13 as a linear combination of 221 and 91:
That is, and are two possible coefficients in a linear combination of 221 and 91 that makes up the GCD.
Let's now see how to implement a function extendedGCD
in C++ that computes both the GCD and the coefficients and using the extended Euclidean algorithm.
First we need a way to express the return value of such a function. It consists of 3 items:
One way to express this is using a struct
that has 3 fields. Since this struct represents the value of a linear combination along with the coefficients and , let's call it LinearCombination
and define it as follows:
struct LinearCombination {
int value;
int s;
int t;
};
So, the GCD of the previous example would correspond to:
LinearCombination gcd = {13, -2, 5};
We now have a way to express the GCD and the coefficients, so the signature of the extendedGCD
function will be:
LinearCombination extendedGCD(int a, int b);
Let's think about how the extendedGCD
function would work.
We start with and , which trivially are linear combinations of and :
We compute the remainder using the Division theorem:
Since is equal to the difference between linear combinations of and , is also a linear combination of and .
Next, we recursively compute . Thus, becomes the new and becomes the new .
Since and are initially linear combinations of and , and the remainder that we compute is also a linear combination of and , it follows that and are always linear combinations of and :
Knowing this, we can determine how to compute the coefficients and for the remainder :
Thus, the coefficients for the remainder are and .
We keep doing this until we reach a zero-remainder, that is, until becomes 0. When that happens, we simply return , which is the last non-zero remainder, and thus it corresponds to the GCD.
Let's look at the implementation:
struct LinearCombination {
int value; // the value of sa + tb
int s; // the coefficient s in sa + tb
int t; // the coefficient t in sa + tb
};
LinearCombination extendedGCDRec(LinearCombination x, LinearCombination y) {
if (y.value == 0) {
return x;
}
int q = x.value / y.value;
int s = x.s - q * y.s;
int t = x.t - q * y.t;
LinearCombination r = {x.value - q * y.value, s, t};
return extendedGCDRec(y, r);
}
LinearCombination extendedGCD(int a, int b) {
if (a == 0 && b == 0) {
return LinearCombination({-1, 0, 0});
}
if (a == 0) {
return LinearCombination({b, 0, 1});
}
LinearCombination x = {a, 1, 0};
LinearCombination y = {b, 0, 1};
return extendedGCDRec(x, y);
}
In extendedGCD
, we first check whether both a
and b
are zero. If that's the case, the GCD is undefined, so we return a linear combination with the sentinel value -1
to indicate that.
Then, we check whether a
is zero. If that's the case, the GCD is simply b
with s = 0
and t = 1
.
Otherwise, we express the initial values of x
and y
as a linear combinations of a
and b
, and call extendedGCDRec
, which is the recursive function that does all the work.
In extendedGCDRec
, we check if y
is zero. If that's the case, we found the GCD, which is x
.
Otherwise, we compute the remainder and its coefficients s
and t
.
Then, we call the function recursively with y
becoming the new x
and r
becoming the new y
.
We keep doing this until we reach a zero-remainder, in which case the condition y == 0
will be true and we return the last non-zero remainder x
.