Java:获得最大公约数


91

我已经看到存在这样的功能BigInteger,即BigInteger#gcd。是否有在Java中的其它功能也适用于其他类型的工作(intlongInteger)?似乎这是有意义的java.lang.Math.gcd(带有各种重载),但它不存在。在别的地方吗?


(请不要将此问题与“我如何自己实现”混淆!)


7
为什么接受的答案会告诉您如何自己实现-尽管包装了现有的实现?=)
djjeck

我同意你的观察。GCD应该具有一类带有一堆重载静态方法的类,该类接受两个数字并赋予其gcd。并且它应该是java.math包的一部分。
2014年

Answers:


79

对于int和long而言,作为原语,并非如此。对于Integer,可能有人写过一个。

假设BigInteger是int,Integer,long和Long的(数学/函数)超集,如果您需要使用这些类型,请将它们转换为BigInteger,执行GCD,然后将结果转换回。

private static int gcdThing(int a, int b) {
    BigInteger b1 = BigInteger.valueOf(a);
    BigInteger b2 = BigInteger.valueOf(b);
    BigInteger gcd = b1.gcd(b2);
    return gcd.intValue();
}

63
BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).intValue()好多了。
艾伯特2010年


4
如果经常(即数百万次)调用此函数,则不应将int或long转换为BigInteger。仅使用原始值的函数可能会快一个数量级。检查其他答案。
jcsahnwaldt恢复莫妮卡2015年

@Bhanu Pratap Singh为避免强制转换或截断,最好对int和long使用单独的方法。我相应地编辑了答案。
jcsahnwaldt恢复莫妮卡2015年

1
这不仅不能回答问题(对于int来说,gcd是int还是在Java中是long),但是所提出的实现却效率很低。这不应是公认的答案。据我所知,Java运行时没有它,但是它存在于第三方库中。
Florian F

134

据我所知,没有任何内置的基元方法。但是像这样简单的事情就可以解决问题:

public int gcd(int a, int b) {
   if (b==0) return a;
   return gcd(b,a%b);
}

如果您喜欢这种事情,也可以单行处理:

public int gcd(int a, int b) { return b==0 ? a : gcd(b, a%b); }

应该注意的是,两者编译成相同的字节码时绝对没有区别。


据我所知,它工作正常。通过这两种方法,我只运行了100,000个随机数,并且每次都同意。
托尼·恩尼斯

19
这是欧几里得算法...它已经很老了并且被证明是正确的。 en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

是的,我可以看到它,但是我需要更多时间来完成它。我喜欢。
托尼·恩尼斯

1
@Albert,那么您可以随时使用泛型类型尝试一下,看看它是否有效。我不知道这是什么想法,但是您可以尝试使用该算法。至于某些标准库或类,我从未见过。不过,在创建对象时,仍然需要指定它是int,long等。
马特2010年

1
@Albert,好吧,尽管Matt提供了一个实现,但是您自己可以使它以“更通用”的方式工作,不是吗?:)
Bart Kiers 2010年

33

或用于计算GCD的欧几里得算法...

public int egcd(int a, int b) {
    if (a == 0)
        return b;

    while (b != 0) {
        if (a > b)
            a = a - b;
        else
            b = b - a;
    }

    return a;
}

3
只是要澄清一下:这绝对不是我要的。
艾伯特2010年

11
在这种情况下,您没有指定您不想要替代实现,因为它不存在。直到后来,您才编辑帖子而不寻找实现。我相信其他人对“否”的回答足够多。
Xorlev

2
如果a很大而b很小,这将很慢。“%”解决方案将更快。
布鲁斯·费斯特

12

使用番石榴LongMath.gcd()IntMath.gcd()


2
有趣的是,番石榴没有使用欧几里得的“模”方法,而是使用了二进制GCD算法,声称它们的速度要快40%。可以肯定地说,它非常有效且经过了充分的测试。
Florian F

12

除非我有番石榴,否则我会这样定义:

int gcd(int a, int b) {
  return a == 0 ? b : gcd(b % a, a);
}


7

您可以使用Binary GCD算法的此实现

public class BinaryGCD {

public static int gcd(int p, int q) {
    if (q == 0) return p;
    if (p == 0) return q;

    // p and q even
    if ((p & 1) == 0 && (q & 1) == 0) return gcd(p >> 1, q >> 1) << 1;

    // p is even, q is odd
    else if ((p & 1) == 0) return gcd(p >> 1, q);

    // p is odd, q is even
    else if ((q & 1) == 0) return gcd(p, q >> 1);

    // p and q odd, p >= q
    else if (p >= q) return gcd((p-q) >> 1, q);

    // p and q odd, p < q
    else return gcd(p, (q-p) >> 1);
}

public static void main(String[] args) {
    int p = Integer.parseInt(args[0]);
    int q = Integer.parseInt(args[1]);
    System.out.println("gcd(" + p + ", " + q + ") = " + gcd(p, q));
}

}

来自http://introcs.cs.princeton.edu/java/23recursion/BinaryGCD.java.html


这是Stein算法的一种变体,它利用了大多数机器上的移位相对便宜的操作。这是一种标准算法。
巴斯蒂安·J

6

如果两个数字均为负,则此处的某些实现将无法正常工作。gcd(-12,-18)是6,而不是-6。

因此,应该返回一个绝对值,例如

public static int gcd(int a, int b) {
    if (b == 0) {
        return Math.abs(a);
    }
    return gcd(b, a % b);
}

一个极端的情况是,如果ab都为Integer.MIN_VALUE,您将得到Integer.MIN_VALUE结果,结果为负。这可能是可以接受的。问题是gcd(-2 ^ 31,-2 ^ 31)= 2 ^ 31,但是2 ^ 31不能表示为整数。
Michael Anderson

我还建议使用,if(a==0 || b==0) return Math.abs(a+b);以便行为对于零参数而言是真正对称的。
Michael Anderson

3

我们可以使用递归函数来查找gcd

public class Test
{
 static int gcd(int a, int b)
    {
        // Everything divides 0 
        if (a == 0 || b == 0)
           return 0;

        // base case
        if (a == b)
            return a;

        // a is greater
        if (a > b)
            return gcd(a-b, b);
        return gcd(a, b-a);
    }

    // Driver method
    public static void main(String[] args) 
    {
        int a = 98, b = 56;
        System.out.println("GCD of " + a +" and " + b + " is " + gcd(a, b));
    }
}

2

如果您使用的是Java 1.5或更高版本,则这是一个迭代的二进制GCD算法,用于Integer.numberOfTrailingZeros()减少所需的检查和迭代次数。

public class Utils {
    public static final int gcd( int a, int b ){
        // Deal with the degenerate case where values are Integer.MIN_VALUE
        // since -Integer.MIN_VALUE = Integer.MAX_VALUE+1
        if ( a == Integer.MIN_VALUE )
        {
            if ( b == Integer.MIN_VALUE )
                throw new IllegalArgumentException( "gcd() is greater than Integer.MAX_VALUE" );
            return 1 << Integer.numberOfTrailingZeros( Math.abs(b) );
        }
        if ( b == Integer.MIN_VALUE )
            return 1 << Integer.numberOfTrailingZeros( Math.abs(a) );

        a = Math.abs(a);
        b = Math.abs(b);
        if ( a == 0 ) return b;
        if ( b == 0 ) return a;
        int factorsOfTwoInA = Integer.numberOfTrailingZeros(a),
            factorsOfTwoInB = Integer.numberOfTrailingZeros(b),
            commonFactorsOfTwo = Math.min(factorsOfTwoInA,factorsOfTwoInB);
        a >>= factorsOfTwoInA;
        b >>= factorsOfTwoInB;
        while(a != b){
            if ( a > b ) {
                a = (a - b);
                a >>= Integer.numberOfTrailingZeros( a );
            } else {
                b = (b - a);
                b >>= Integer.numberOfTrailingZeros( b );
            }
        }
        return a << commonFactorsOfTwo;
    }
}

单元测试:

import java.math.BigInteger;
import org.junit.Test;
import static org.junit.Assert.*;

public class UtilsTest {
    @Test
    public void gcdUpToOneThousand(){
        for ( int x = -1000; x <= 1000; ++x )
            for ( int y = -1000; y <= 1000; ++y )
            {
                int gcd = Utils.gcd(x, y);
                int expected = BigInteger.valueOf(x).gcd(BigInteger.valueOf(y)).intValue();
                assertEquals( expected, gcd );
            }
    }

    @Test
    public void gcdMinValue(){
        for ( int x = 0; x < Integer.SIZE-1; x++ ){
            int gcd = Utils.gcd(Integer.MIN_VALUE,1<<x);
            int expected = BigInteger.valueOf(Integer.MIN_VALUE).gcd(BigInteger.valueOf(1<<x)).intValue();
            assertEquals( expected, gcd );
        }
    }
}

与MutableBigInteger.binaryGcd(int,int)相似,不幸的是后者不可访问。但是还是很酷!
Mostowski

2
public int gcd(int num1, int num2) { 
    int max = Math.abs(num1);
    int min = Math.abs(num2);

    while (max > 0) {
        if (max < min) {
            int x = max;
            max = min;
            min = x;
        }
        max %= min;
    }

    return min;
}

此方法使用Euclid算法获得两个整数的“最大公约数”。它接收两个整数并返回它们的gcd。就这么简单!


1

在别的地方吗?

阿帕奇!-它同时具有gcd和lcm,太酷了!

但是,由于其实现的深刻性,与简单的手写版本相比(如果重要),它的速度较慢。


0
/*
import scanner and instantiate scanner class;
declare your method with two parameters
declare a third variable;
set condition;
swap the parameter values if condition is met;
set second conditon based on result of first condition;
divide and assign remainder to the third variable;
swap the result;
in the main method, allow for user input;
Call the method;

*/
public class gcf {
    public static void main (String[]args){//start of main method
        Scanner input = new Scanner (System.in);//allow for user input
        System.out.println("Please enter the first integer: ");//prompt
        int a = input.nextInt();//initial user input
        System.out.println("Please enter a second interger: ");//prompt
        int b = input.nextInt();//second user input


       Divide(a,b);//call method
    }
   public static void Divide(int a, int b) {//start of your method

    int temp;
    // making a greater than b
    if (b > a) {
         temp = a;
         a = b;
         b = temp;
    }

    while (b !=0) {
        // gcd of b and a%b
        temp = a%b;
        // always make a greater than b
        a =b;
        b =temp;

    }
    System.out.println(a);//print to console
  }
}

您能否详细解释一下这可能会有帮助?
kommradHomer '16

0

我使用了14岁时创建的这种方法。

    public static int gcd (int a, int b) {
        int s = 1;
        int ia = Math.abs(a);//<-- turns to absolute value
        int ib = Math.abs(b);
        if (a == b) {
            s = a;
        }else {
            while (ib != ia) {
                if (ib > ia) {
                    s = ib - ia;
                    ib = s;
                }else { 
                    s = ia - ib;
                    ia = s;
                }
            }
        }
        return s;
    }


-3

%将给我们gcd在两个数字之间,这意味着:-big_number / small_number的%或mod是= gcd,我们将其写在java上是这样的 big_number % small_number

EX1:两个整数

  public static int gcd(int x1,int x2)
    {
        if(x1>x2)
        {
           if(x2!=0)
           {
               if(x1%x2==0)     
                   return x2;
                   return x1%x2;
                   }
           return x1;
           }
          else if(x1!=0)
          {
              if(x2%x1==0)
                  return x1;
                  return x2%x1;
                  }
        return x2;
        } 

EX2:三个整数

public static int gcd(int x1,int x2,int x3)
{

    int m,t;
    if(x1>x2)
        t=x1;
    t=x2;
    if(t>x3)
        m=t;
    m=x3;
    for(int i=m;i>=1;i--)
    {
        if(x1%i==0 && x2%i==0 && x3%i==0)
        {
            return i;
        }
    }
    return 1;
}

2
这是错误的,例如gcd(42, 30)应该如此,612以您的示例为例。但是12不是30的除数,也不是42的除数。您应该gcd递归调用。请参阅Matt的答案或在Wikipedia上查找欧几里得算法。
艾伯特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.