34

I know that Euclid’s algorithm is the best algorithm for getting the GCD (great common divisor) of a list of positive integers. But in practice you can code this algorithm in various ways. (In my case, I decided to use Java, but C/C++ may be another option).

I need to use the most efficient code possible in my program.

In recursive mode, you can write:

static long gcd (long a, long b){
    a = Math.abs(a); b = Math.abs(b);
    return (b==0) ? a : gcd(b, a%b);
  }

And in iterative mode, it looks like this:

static long gcd (long a, long b) {
  long r, i;
  while(b!=0){
    r = a % b;
    a = b;
    b = r;
  }
  return a;
}

There is also the Binary algorithm for the GCD, which may be coded simply like this:

int gcd (int a, int b)
{
    while(b) b ^= a ^= b ^= a %= b;
    return a;
}
PJTraill
  • 389
  • 3
  • 13
Jonathan Prieto-Cubides
  • 2,229
  • 3
  • 18
  • 26

8 Answers8

26

Your two algorithms are equivalent (at least for positive integers, what happens with negative integers in the imperative version depends on Java's semantics for % which I don't know by heart). In the recursive version, let $a_i$ and $b_i$ be the argument of the $i$th recursive call: $$\begin{gather*} a_{i+1} = b_i \\ b_{i+1} = a_i \mathbin{\mathrm{mod}} b_i \\ \end{gather*}$$

In the imperative version, let $a'_i$ and $b'_i$ be the values of the variables a and b at the beginning of the $i$th iteration of the loop. $$\begin{gather*} a'_{i+1} = b'_i \\ b'_{i+1} = a'_i \mathbin{\mathrm{mod}} b'_i \\ \end{gather*}$$

Notice a resemblance? Your imperative version and your recursive version are calculating exactly the same values. Furthermore, they both end at the same time, when $a_i=0$ (resp. $a'_i=0$), so they perform the same number of iterations. So algorithmically speaking, there is no difference between the two. Any difference will be a matter of implementation, highly dependent on the compiler, the hardware it runs on, and quite possibly the operating system and what other programs are running concurrently.

The recursive version makes only tail recursive calls. Most compilers for imperative languages do not optimize these, and so it is likely that the code they generate will waste a little time and memory constructing a stack frame at each iteration. With a compiler that optimizes tail calls (compilers for functional languages almost always do), the generated machine code may well be the same for both (assuming you harmonize those calls to abs).

Gilles 'SO- stop being evil'
  • 44,159
  • 8
  • 120
  • 184
9

For numbers that are small, the binary GCD algorithm is sufficient.

GMP, a well maintained and real-world tested library, will switch to a special half GCD algorithm after passing a special threshold, a generalization of Lehmer's Algorithm. Lehmer's uses matrix multiplication to improve upon the standard Euclidian algorithms. According to the docs, the asymptotic running time of both HGCD and GCD is O(M(N)*log(N)), where M(N) is the time for multiplying two N-limb numbers.

Full details on their algorithm can be found here.

qwr
  • 628
  • 1
  • 7
  • 22
3

As I know Java doesn’t support tail recursion optimization in general, but you can test your Java implementation for it; if it doesn’t support it, a simple for-loop should be faster, otherwise recursion should be just as fast. On the other hand, these are bit optimizations, choose the code you think is easier and more readable.

I should also note that the fastest GCD algorithm is not Euclid’s algorithm, Lehmer’s algorithm is a bit faster.

PJTraill
  • 389
  • 3
  • 13
2

First, don't use recursivity to replace a tight loop. It is slow. Don't rely on the compiler to optimize it out. Second, in your code, you call Math.abs() within every recursive calls, which is useless.

In your loop, you can easily avoid temporary variables and swapping a and b all the time.

int gcd(int a, int b){
    if( a<0 ) a = -a;
    if( b<0 ) b = -b;
    while( b!=0 ){
        a %= b;
        if( a==0 ) return b;
        b %= a;
    }
    return a;
}

Swapping using the a ^= b ^= a ^= b makes the source shorter but takes many instructions to execute. It will be slower than the boring swap with a temporary variable.

Florian F
  • 187
  • 5
1

Disclaimer: Practical performance questions are highly application-specific and can only be satisfyingly answered by application-specific benchmarks that sufficiently represent field conditions. This answer uses a synthetic benchmark instead, which may not at all be representative of Your specific application.

A while ago, I ran a little synthetic benchmark (long version here) to answer, once and for all, which one of the following GCD algorithms is the fastest:

  • a) Subtraction-based Euclid's Algorithm
  • b) Division/Modulo-based Euclidean Algorithm
  • c) Binary GCD

The answer was a clear and resounding: d) A combination of all of the above. So without further ado, I proudly present to You: The tribrid GCD algorithm!

public static long tribridGCD( long x, long y )
{
  x = abs(x);
  y = abs(y);
  int l = Long.numberOfTrailingZeros(x); x >>>= l;
  int r = Long.numberOfTrailingZeros(y); y >>>= r;
  int shift = min(l,r);
  if( x > y ) {
    long z = x; x = y; y = z;
  }
  while( x != 0 ) {
    long z = x > (y>>>1) ? y-x : y%x;
    y = x;
    x = z;
    // Invariant: x <= y && (y is odd)
    x >>>= Long.numberOfTrailingZeros(x);
  }
  return y << shift;
}
DirkT
  • 1,021
  • 2
  • 13
0

What you present as the Binary GCD algorithm is not the Binary GCD algorithm, because it still uses the modulo (remainder) operator $\%$ (in fact it is the standard algorithm with the swap-via-xor trick, which probably makes it slower). This is unfair in the comparison as modulo is a costly operation. The real Binary algorithm only uses shifts and subtractions, notoriously fast.

Anyway, telling the most efficient algorithm is not possible for several reasons:

  • the answer can be machine-dependent,

  • the answer can even be dependent on the input value, and

  • the GCD is pretty fast to compute, and in practice it is impossible to measure short execution times accurately.

0

For small numbers, % is quite an expensive operation, perhaps the simpler recursive

GCD[a,b] := Which[ 
   a==b , Return[a],
   b > a, Return[ GCD[a, b-a]],
   a > b, Return[ GCD[b, a-b]]
];

is quicker? (Sorry, Mathematica code and not C++)

Per Alexandersson
  • 1,314
  • 8
  • 8
-2

Euclid Algorithm is most efficient for calculating GCD:

Static long gcd(long a,long b)
{
if(b==0)
return a;
else
return gcd(,a%b);
}

example:-

Let A = 16, B = 10.
GCD(16, 10) = GCD(10, 16 % 10) = GCD(10, 6)
GCD(10, 6) = GCD(6, 10 % 6) = GCD(6, 4)
GCD(6, 4) = GCD(4, 6 % 4) = GCD(4, 2)
GCD(4, 2) = GCD(2, 4 % 2) = GCD(2, 0)


Since B = 0 so GCD(2, 0) will return 2. 
Rohit-Pandey
  • 101
  • 2