Signs:
One difference I noticed (besides the performance) that the signs are handled differently, your hand implemented Euclidean algorithm GCD
and my gcdLongIterative
behave the same, but both are different from BigInteger
which tends to 'keep' the signs as they are. It seems that in essence the GCD
and gcdLongIterative
can return a negative number, while BigInteger will return positive only.
GCD and gcdLongIterative implementations:
-4/-2 => 2/1
-10/200 => 1/-20
10/-200 => 1/-20
BigInteger implementation tends to 'keep' the signs:
-4/-2 => -2/-1
-10/200 => -1/20
10/-200 => 1/-20
All results when used for fractions are valid, but it's worth considering post-process normalization if you expect the numbers in a specific 'style'.
Performance:
If you want to use the GCD
as that is faster then you should consider an iterative implementation of it:
public static long gcdLongIterative(long a, long b) {
long tmp;
while (0 != b) {
tmp = b;
b = a % b;
a = tmp;
}
return a;
}
Inspired by @Xabster benchmark I extended it to test all 3 implementations, in some cases both recursive and iterative were performing the same, however the iterative is slightly faster in most of the cases:
(100 000 000 iterations)
gcd recursive: 3113ms
gcd iterative: 3079ms
gcd BigInteger: 13672ms
The benchmark code is here:
import java.math.BigInteger;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class Test {
private static final int BENCHMARK_ITERATIONS = 100000000;
public static long gcdLong(long a, long b) {
return b == 0 ? a : gcdLong(b, a % b);
}
public static long gcdLongIterative(long a, long b) {
long tmp;
while (0 != b) {
tmp = b;
b = a % b;
a = tmp;
}
return a;
}
public static long gcdLongBigInteger(long a, long b) {
return BigInteger.valueOf(a).gcd(BigInteger.valueOf((b))).longValue();
}
public static String asFractionGcdLong(long a, long b) {
long gcd = gcdLong(a, b);
return (a / gcd) + "/" + (b / gcd);
}
public static String asFractionGcdLongIterative(long a, long b) {
long gcd = gcdLongIterative(a, b);
return (a / gcd) + "/" + (b / gcd);
}
public static String asFractionGcdLongBI(long a, long b) {
long gcd = gcdLongBigInteger(a, b);
return (a / gcd) + "/" + (b / gcd);
}
public static void test(String actual, String expected) {
boolean match = expected.equals(actual);
if (match) {
System.out.println("Actual and expected match=" + expected);
} else {
System.out.println("NO match expected=" + expected + " actual=" + actual);
}
}
public static class Values {
public long a;
public long b;
public String expected;
public Values(long a, long b, String expected) {
this.a = a;
this.b = b;
this.expected = expected;
}
}
public static void validityTest() {
List<Values> vals = new LinkedList<Values>(Arrays.asList(
new Values(500, 1000, "1/2"),
new Values(17, 3, "17/3"),
new Values(462, 1071, "22/51"),
new Values(-4, -2, "2/1"),
new Values(-10, 200, "1/-20"),
new Values(10, -200, "1/-20")
));
System.out.println("------ Recursive implementation -------");
vals.forEach(v -> test(asFractionGcdLong(v.a, v.b), v.expected));
System.out.println();
System.out.println("------ Iterative implementation -------");
vals.forEach(v -> test(asFractionGcdLongIterative(v.a, v.b), v.expected));
System.out.println();
System.out.println("------ BigInteger implementation -------");
vals.forEach(v -> test(asFractionGcdLongBI(v.a, v.b), v.expected));
System.out.println();
}
public static void benchMark() {
Random r = new Random();
long[] nums = new long[BENCHMARK_ITERATIONS];
for (int i = 0 ; i < nums.length ; i++) nums[i] = r.nextLong();
System.out.println("Waming up for benchmark...");
for (int i = 0 ; i < nums.length-1; i++) gcdLong(i, i + 1);
for (int i = 0 ; i < nums.length-1; i++) gcdLongIterative(i, i + 1);
for (int i = 0 ; i < nums.length-1; i++) gcdLongBigInteger(i, i + 1);
System.out.println("Started benchmark...");
long s = System.currentTimeMillis();
for (int i = 0 ; i < nums.length-1; i++) gcdLong(i, i + 1);
System.out.println("recursive: " + (System.currentTimeMillis() - s) + "ms");
s = System.currentTimeMillis();
for (int i = 0 ; i < nums.length-1; i++) gcdLongIterative(i, i + 1);
System.out.println("iterative: " + (System.currentTimeMillis() - s) + "ms");
s = System.currentTimeMillis();
for (int i = 0 ; i < nums.length-1; i++) gcdLongBigInteger(i, i + 1);
System.out.println("BigInteger: " + (System.currentTimeMillis() - s) + "ms");
}
public static void main(String[] args) {
validityTest();
benchMark();
}
}