【问题标题】:Java: get greatest common divisorJava:获得最大公约数
【发布时间】:2010-10-24 16:40:11
【问题描述】:

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


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

【问题讨论】:

  • 为什么接受的答案是告诉您如何自己实现它 - 尽管包装了现有的实现? =)
  • 我同意你的看法。 GCD 应该是一个具有一堆重载静态方法的类,这些方法接受两个数字并给出它的 gcd。它应该是 java.math 包的一部分。

标签: java greatest-common-divisor


【解决方案1】:

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

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 个随机数,并且它们每次都同意。
  • 这是欧几里得算法......它非常古老并且被证明是正确的。 en.wikipedia.org/wiki/Euclidean_algorithm
  • 是的,我可以看到它,但我需要更多时间来完成它。我喜欢它。
  • @Albert,您可以随时尝试使用泛型类型,看看它是否有效。我不知道只是一个想法,但该算法可供您试验。至于一些标准库或类,我从未见过。您仍然需要在创建对象时指定它是 int、long 等。
  • @Albert,好吧,虽然 Matt 提供了一个实现,但您自己可以让它以一种“更通用”的方式工作,不是吗? :)
【解决方案2】:

对于 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();
}

【讨论】:

  • BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).intValue() 好多了。
  • 如果这个函数经常被调用(即数百万次),你不应该将 int 或 long 转换为 BigInteger。仅使用原始值的函数可能会快一个数量级。检查其他答案。
  • @Bhanu Pratap Singh 为了避免强制转换或截断,最好对 int 和 long 使用单独的方法。我相应地编辑了答案。
  • 这不仅没有回答问题(Java 中的 gcd 是 int 还是 long),而且提议的实现效率很低。这不应该是公认的答案。据我所知,Java 运行时没有它,但它存在于第三方库中。
【解决方案3】:

或者计算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;
}

【讨论】:

  • 澄清一下:这绝对不是我想要的。
  • 在这种情况下,您没有指定您不想要替代实现,因为不存在替代实现。直到后来你才编辑你的帖子而不是寻找实现。我相信其他人已经充分回答了“不”。
  • 如果 a 很大而 b 很小,这会很慢。 '%' 解决方案会快得多。
  • 即使 a 和 b 之间的差异很小,这也会减慢。我现在用 a = Long.MAX_VALUE 和 b = Long.MAX_VALUE - 3 进行测试,然后等待几分钟得到结果
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

    使用 Guava LongMath.gcd()IntMath.gcd()

    【讨论】:

    【解决方案6】:

    Jakarta Commons Math 正是如此。

    ArithmeticUtils.gcd(int p, int q)

    【讨论】:

      【解决方案7】:

      你可以使用Binary GCD algorithm的这个实现

      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 算法的一种变体,它利用了在大多数机器上,移位是一种相对便宜的操作。这是一个标准算法。
      【解决方案8】:

      如果两个数字都是负数,这里的一些实现将无法正常工作。 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不能表示为整数。
      • 我还建议使用if(a==0 || b==0) return Math.abs(a+b);,这样行为对于零参数来说是真正对称的。
      【解决方案9】:

      我们可以使用递归函数来查找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));
          }
      }
      

      【讨论】:

        【解决方案10】:
        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;
        }
        

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

        【讨论】:

          【解决方案11】:

          如果您使用的是 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),遗憾的是后者不可访问。但无论如何都很酷!
          【解决方案12】:

          在别的地方吗?

          Apache! - 它有 gcd 和 lcm,太酷了!

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

          【讨论】:

            【解决方案13】:
            /*
            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
              }
            }
            

            【讨论】:

            • 您能否详细说明这可能有什么帮助?
            【解决方案14】:

            我使用了我 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;
                }
            

            【讨论】:

              【解决方案15】:

              Commons-MathGuava提供的那些GCD函数有些区别。

              • Commons-Math 仅针对 Integer.MIN_VALUELong.MIN_VALUE 抛出 ArithematicException.class
                • 否则,将值作为绝对值处理。
              • Guava 会为任何负值抛出 IllegalArgumentException.class

              【讨论】:

                【解决方案16】:

                % 将给我们两个数字之间的 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;
                }
                

                【讨论】:

                • 这是错误的,例如gcd(42, 30) 应该是 6 但它是 12 以你的例子。但是 12 不是 30 的除数,也不是 42 的除数。您应该递归调用 gcd。请参阅 Matt 的答案或在 Wikipedia 上查找欧几里得算法。
                猜你喜欢
                • 2023-03-16
                • 2020-12-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-10-01
                • 1970-01-01
                相关资源
                最近更新 更多