简体   繁体   中英

How does the Euclidean Algorithm work?

I just found this algorithm to compute the greatest common divisor in my lecture notes:

public static int gcd( int a, int b ) {
    while (b != 0) {
        final int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

So r is the remainder when dividing b into a (get the mod). Then b is assigned to a , and the remainder is assigned to b , and a is returned. I can't for the life of my see how this works!

And then, apparently this algorithm doesn't work for all cases, and this one must then be used:

public static int gcd( int a, int b ) {
    final int gcd;
    if (b != 0) {
        final int q = a / b;
        final int r = a % b; // a == r + q * b AND r == a - q * b.
        gcd = gcd( b, r );
    } else {
        gcd = a;
    }
    return gcd;
}

I don't understand the reasoning behind this. I generally get recursion and am good at Java but this is eluding me. Help please?

The Wikipedia article contains an explanation, but it's not easy to find it immediately (also, procedure + proof don't always answer the question "why it works").

Basically it comes down to the fact that for two integers a, b (assuming a >= b), it is always possible to write a = bq + r where r < b.

If d=gcd(a,b) then we can write a=ds and b=dt. So we have ds = qdt + r. Since the left hand side is divisible by d, the right hand side must also be divisible by d. And since qdt is divisible by d, the conclusion is that r must also be divisible by d.

To summarise: we have a = bq + r where r < b and a, b and r are all divisible by gcd(a,b).

Since a >= b > r, we have two cases:

  1. If r = 0 then a = bq, and so b divides both b and a. Hence gcd(a,b)=b.
  2. Otherwise (r > 0), we can reduce the problem of finding gcd(a,b) to the problem of finding gcd(b,r) which is exactly the same number (as a, b and r are all divisible by d).

Why is this a reduction? Because r < b. So we are dealing with numbers that are definitely smaller. This means that we only have to apply this reduction a finite number of times before we reach r = 0.

Now, r = a % b which hopefully explains the code you have.

They're equivalent. First thing to notice is that q in the second program is not used at all. The other difference is just iteration vs. recursion.

As to why it works, the Wikipedia page linked above is good. The first illustration in particular is effective to convey intuitively the "why", and the animation below then illustrates the "how".

Here is an interesting blog post: Tominology .

Where a lot of the intuition behind the Euclidean Algorithm is discussed, it is implemented in JavaScript, but I believe that if one want's there is no difficult to convert the code to Java.

given that 'q' is never used, I don't see a difference between your plain iterative function, and the recursive iterative function... both do

gdc(first number, second number)
  as long as (second number > 0) {
      int remainder = first % second;
      gcd = try(second as first, remainder as second);
  }
}

Barring trying to apply this to non-integers, under which circumstances does this algorithm fail?

(also see http://en.wikipedia.org/wiki/Euclidean_algorithm for lots of detailed info)

Here is a very useful explanation that I found.

For those too lazy to open it, this is what it says :

Consider the example when you had to find the GCD of (3084,1424). Lets assume that d is the GCD. Which means d | 3084 and d | 1424 (using the symbol '|' to say 'divides').

It follows that d | (3084 - 1424). Now we'll try to reduce these numbers which are divisible by d (in this case 3084 and 1024) as much as possible, so that we reach 0 as one of the numbers. Remember that GCD (a, 0) is a.

Since d | (3084 - 1424), it follows that d | ( 3084 - 2(1424) ) which means d | 236.
Hint : (3084 - 2*1424 = 236)

Now forget about the initial numbers, we just need to solve for d, and we know that d is the greatest number that divides 236, 1424 and 3084. So we use the smaller two numbers to proceed because it'll converge the problem towards 0.

d | 1424 and d | 236 implies that d | (1424 - 236).
So, d | ( 1424 - 6(236) ) => d | 8.

Now we know that d is the greatest number that divides 8, 236, 1424 and 3084. Taking the smaller two again, we have

d | 236 and d | 8, which implies d | (236 - 8).
So, d | ( 236 - 29(8) ) => d | 4.

Again the list of numbers divisible by d increases and converges (the numbers are getting smaller, closer to 0). As it stands now, d is the greatest number that divides 4, 8, 236, 1424, 3084.

Taking same steps,

d | 8 and d | 4 implies d | (8-4).
So, d | ( 8 - 2(4) ) => d | 0.

The list of numbers divisible by d is now 0, 4, 8, 236, 1484, 3084. GCD of (a, 0) is always a. So, as soon as you have 0 as one of the two numbers, the other number is the gcd of original two and all those which came in between.

This is exactly what your code is doing. You can recognize the terminal condition as GCD (a, 0) = a.

The other step is to find the remainder of the two numbers, and choose that and the smaller of the previous two as the new numbers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM