Support Ukraine πŸ‡ΊπŸ‡¦Help Ukrainian ArmyHumanitarian Assistance to Ukrainians

Extended Euclidean Algorithm (a.k.a. the Pulverizer)

Sam

Oct 05 2020 at 15:14 GMT

With Euclid's algorithm, we can find the greatest common divisor (GCD) of two integers aa and bb. It can be proven that the GCD is the smallest positive integer linear combination of aa and bb. Thus, we can write the GCD as a linear combination of aa and bb:

gcd⁑(a,b)=sa+tb    (for some s,t)\gcd(a, b) = sa + tb \ \ \ \text { (for some } s,t \text{)}

The extended Euclidean algorithm allows to find not only the GCD, but also the values of the coefficients ss and tt.

I would like some explanations of how this algorithm works.

1 Answer

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 aa and bb. 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 aa and bb.

Example: gcd(221, 91)

Let's find the GCD of 221 and 91 using the extended Euclidean algorithm.

gcd⁑(221,91)=gcd⁑(91,rem(221,91))\gcd(221, 91) = \gcd(91, \text{rem}(221, 91))

According to the Division theorem, we can write 221 as 91 times the quotient plus the remainder:

221=2β‹…91+39221 = 2 \cdot91 + 39

Thus, rem(221,91)=39\text{rem}(221, 91) = 39.

We can express this first remainder (39) as a linear combination of 221 and 91 by rearranging the terms:

39=221+(βˆ’2)β‹…9139 = 221 + (-2)\cdot91

Let's continue with the algorithm:

gcd⁑(91,39)=gcd⁑(39,rem(91,39))\gcd(91, 39) = \gcd(39, \text{rem}(91, 39))

By the Division theorem:

91=2β‹…39+1391 = 2 \cdot 39 + 13

So, rem(91,39)=13\text{rem}(91, 39) = 13.

Let's express 13 as a linear combination of 221 and 91:

13=91βˆ’2β‹…39=91βˆ’2β‹…(221βˆ’2β‹…91)=91βˆ’2β‹…221+4β‹…91=βˆ’2β‹…221+5β‹…91\begin{aligned} 13 &= 91 - 2\cdot 39 \\ &= 91 - 2 \cdot (221 - 2 \cdot 91) \\ &= 91 - 2 \cdot 221 + 4 \cdot 91 \\ &= -2 \cdot 221 + 5 \cdot 91 \end{aligned}

Let's continue:

gcd⁑(39,13)=gcd⁑(13,rem(39,13))\gcd(39, 13) = \gcd(13, \text{rem}(39, 13))

By the Division theorem:

39=3β‹…13+039 = 3 \cdot 13 + 0

Thus, we reached a zero remainder: rem(39,13)=0\text{rem}(39, 13) = 0.

gcd⁑(13,0)=13\gcd(13, 0) = 13

Thus, the GCD of 221 and 91 is 13, and we know how to express 13 as a linear combination of 221 and 91:

13=βˆ’2β‹…221+5β‹…9113 = -2 \cdot 221 + 5 \cdot 91

That is, s=βˆ’2s=-2 and t=5t=5 are two possible coefficients in a linear combination of 221 and 91 that makes up the GCD.

C++ Implementation

Let's now see how to implement a function extendedGCD in C++ that computes both the GCD and the coefficients ss and tt using the extended Euclidean algorithm.

First we need a way to express the return value of such a function. It consists of 3 items:

  • The GCD
  • The coefficient ss in sa+tbsa+tb
  • The coefficient tt in sa+tbsa + tb

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 ss and tt, 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 x=ax = a and y=by = b, which trivially are linear combinations of aa and bb:

a=a+0β‹…bb=0β‹…a+b\begin{aligned} a &= a + 0 \cdot b \\ b &= 0 \cdot a + b \end{aligned}

We compute the remainder using the Division theorem:

x=qy+rr=xβˆ’qy\begin{aligned} x &= qy + r \\ r &= x - qy \end{aligned}

Since rr is equal to the difference between linear combinations of aa and bb, rr is also a linear combination of aa and bb.

Next, we recursively compute gcd⁑(y,r)\gcd(y,r). Thus, yy becomes the new xx and rr becomes the new yy.

Since xx and yy are initially linear combinations of aa and bb, and the remainder rr that we compute is also a linear combination of aa and bb, it follows that xx and yy are always linear combinations of aa and bb:

x=sxa+txby=sya+tyb\begin{aligned} x &= s_x a + t_x b \\ y &= s_y a + t_y b \end{aligned}

Knowing this, we can determine how to compute the coefficients ss and tt for the remainder rr:

r=xβˆ’qy=(sxa+txb)βˆ’q(sya+tyb)=sxa+txbβˆ’qsyaβˆ’qtyb=(sxβˆ’qsy)a+(txβˆ’qty)b\begin{aligned} r &= x - qy \\ &= (s_x a + t_x b) - q(s_y a + t_y b) \\ &= s_x a + t_x b - qs_y a - qt_y b \\ &= (s_x - qs_y)a + (t_x - qt_y)b \end{aligned}

Thus, the coefficients for the remainder rr are s=sxβˆ’qsys = s_x - qs_y and t=txβˆ’qtyt = t_x - qt_y.

We keep doing this until we reach a zero-remainder, that is, until yy becomes 0. When that happens, we simply return xx, 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.

claritician Β© 2022