我正在开发一些需要使用GCD算法的东西,并且我希望它尽可能快。我已经尝试过常规方法,二进制方法和记忆方法,我认为它会比它更好。我从here复制了二进制方法,并进行了一些小调整。
我一直在使用名为TestGCD的课程进行测试,这就是整个事情:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestGCD
{
private static class Pair<A>
{
private final A a_one;
private final A a_two;
public Pair(A a_one, A a_two)
{
this.a_one = a_one;
this.a_two = a_two;
}
@Override
public boolean equals(Object object)
{
if (this == object)
return true;
if (object == null)
return false;
if (!(object instanceof Pair))
return false;
final Pair other = (Pair) object;
if (a_one == null)
if (other.a_one != null)
return false;
if (a_two == null)
if (other.a_two != null)
return false;
if (a_one.equals(other.a_one))
if (a_two.equals(other.a_two))
return true;
if (a_one.equals(other.a_two))
if (a_two.equals(other.a_one))
return true;
return false;
}
public A getFirst()
{
return a_one;
}
public A getSecond()
{
return a_two;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
final int aOneHash = a_one == null ? 0 : a_one.hashCode();
final int aTwoHash = a_two == null ? 0 : a_two.hashCode();
int resultOneWay = prime * result + aOneHash;
resultOneWay += prime * result + aTwoHash;
int resultOtherWay = prime * result + aTwoHash;
resultOtherWay += prime * result + aOneHash;
result += resultOneWay + resultOtherWay;
return result;
}
@Override
public String toString()
{
return String.format("%s, %s", a_one, a_two);
}
}
private final static Map<Pair<Integer>, Integer> STORAGE = new HashMap<>();
private static void addNewPairs(List<Pair<Integer>> newPairs, int result)
{
for (final Pair<Integer> pair : newPairs)
STORAGE.put(pair, result);
}
private static int gcd(int x, int y)
{
if (x == 0)
return y;
if (y == 0)
return x;
int gcdX = Math.abs(x);
int gcdY = Math.abs(y);
if (gcdX == 1 || gcdY == 1)
return 1;
while (gcdX != gcdY)
if (gcdX > gcdY)
gcdX -= gcdY;
else
gcdY -= gcdX;
return gcdX;
}
private static int gcdBinary(int x, int y)
{
int shift;
/* GCD(0, y) == y; GCD(x, 0) == x, GCD(0, 0) == 0 */
if (x == 0)
return y;
if (y == 0)
return x;
int gcdX = Math.abs(x);
int gcdY = Math.abs(y);
if (gcdX == 1 || gcdY == 1)
return 1;
/* Let shift := lg K, where K is the greatest power of 2 dividing both x and y. */
for (shift = 0; ((gcdX | gcdY) & 1) == 0; ++shift)
{
gcdX >>= 1;
gcdY >>= 1;
}
while ((gcdX & 1) == 0)
gcdX >>= 1;
/* From here on, gcdX is always odd. */
do
{
/* Remove all factors of 2 in gcdY -- they are not common */
/* Note: gcdY is not zero, so while will terminate */
while ((gcdY & 1) == 0)
/* Loop X */
gcdY >>= 1;
/*
* Now gcdX and gcdY are both odd. Swap if necessary so gcdX <= gcdY,
* then set gcdY = gcdY - gcdX (which is even). For bignums, the
* swapping is just pointer movement, and the subtraction
* can be done in-place.
*/
if (gcdX > gcdY)
{
final int t = gcdY;
gcdY = gcdX;
gcdX = t;
} // Swap gcdX and gcdY.
gcdY = gcdY - gcdX; // Here gcdY >= gcdX.
}while (gcdY != 0);
/* Restore common factors of 2 */
return gcdX << shift;
}
private static int gcdMemoised(int x, int y)
{
if (x == 0)
return y;
if (y == 0)
return x;
int gcdX = Math.abs(x);
int gcdY = Math.abs(y);
if (gcdX == 1 || gcdY == 1)
return 1;
final List<Pair<Integer>> newPairs = new ArrayList<>();
while (gcdX != gcdY)
{
final Pair<Integer> pair = new Pair<>(gcdX, gcdY);
final Integer result = STORAGE.get(pair);
if (result != null)
{
addNewPairs(newPairs, result);
return result;
}
else
newPairs.add(pair);
if (gcdX > gcdY)
gcdX -= gcdY;
else
gcdY -= gcdX;
}
addNewPairs(newPairs, gcdX);
return gcdX;
}
那么有没有办法让这个算法更快,或者原始版本是我最快的版本?请不要使用其他语言的建议,我正在寻找算法改进。很明显,我的记忆尝试完全失败了,但也许这里有人可以看到它的缺陷/改进。
答案 0 :(得分:4)
您可以使用Euclid的算法。它实现起来非常简单,效率更高。这是一个代码:
static int gcd(int a, int b) {
while (b != 0) {
int t = a;
a = b;
b = t % b;
}
return a;
}
时间复杂度为O(log(A + B))
,而您使用的算法为O(A + B)
。它可以更好地扩展,并且对于小a
和b
也很有效。
答案 1 :(得分:0)
以下是我提出的内容,与@ILoveCoding
相同public static long gcd(long first, long second) {
long big = 0;
long small = 0;
if(first > second) {
big=first;
small=second;
}
else {
big=second;
small=first;
}
long temp = big % small;
while( (temp) > 1 ) {
big = small;
small = temp;
temp = big % small;
}
if( temp == 0 ) {
return small ;
}
else if( temp == 1) {
return 1;
}
else {
return -1; // will never occur. hack for compilation error.
}
}
编辑:测试用例!
System.out.println( gcd(10L, 5L));
System.out.println( gcd(11L, 7L));
System.out.println( gcd(15L, 21L));
System.out.println( gcd(-2L, -5L));
System.out.println( gcd(-2L, 2L));
答案 2 :(得分:0)
Euclidean Algorithm似乎都没有Binary GCD Algorithm那样有效,因此这里是Java中的代码(取自Wikipidea)
static long gcd(long u, long v) {
int shift;
if (u == 0) return v;
if (v == 0) return u;
for (shift = 0; ((u | v) & 1) == 0; ++shift) {
u >>= 1;
v >>= 1;
}
while ((u & 1) == 0) {
u >>= 1;
}
do {
while ((v & 1) == 0) {
v >>= 1;
}
if (u > v) {
long t = v;
v = u;
u = t;
}
v = v - u;
} while (v != 0);
return u << shift;
}
但是,二进制算法并不是最快的gcd算法。更多here。
答案 3 :(得分:0)
对GCD使用 Euclidean 算法
该算法基于以下事实。
代码:
import java.util.*;
import java.lang.*;
class GFG
{
public static int gcd(int a, int b)
{
if (a == 0)
return b;
return gcd(b%a, a);
}
public static void main(String[] args)
{
int a = 10, b = 15, g;
g = gcd(a, b);
System.out.println("GCD(" + a + " , " + b+ ") = " + g);
}
}
时间复杂度:O(Log min(a,b))