자바 : 최대 공약수 얻기


91

나는 그런 기능이 존재 함을 알 한 BigIntegerBigInteger#gcd. 다른 유형 ( int, long또는 Integer) 에서도 작동하는 Java에 다른 기능이 있습니까? 이것은 java.lang.Math.gcd(모든 종류의 과부하와 함께) 이해가 될 것 같지만 거기에는 없습니다. 다른 곳에 있습니까?


(이 질문을 "내가 직접 구현하는 방법"과 혼동하지 마십시오!)


7
기존 구현을 래핑하더라도 직접 구현하는 방법을 알려주는 대답이 허용되는 이유는 무엇입니까? =)
djjeck

나는 당신의 관찰에 동의합니다. GCD는 두 개의 숫자를 받아서 gcd를 제공하는 오버로드 된 정적 메서드가있는 클래스 여야합니다. 그리고 이것은 java.math 패키지의 일부 여야합니다.
anu

답변:


81

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

65
BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).intValue()훨씬 낫습니다.
Albert


4
이 함수가 자주 호출되는 경우 (즉, 수백만 번) int 또는 long을 BigInteger로 변환해서는 안됩니다. 원시 값만 사용하는 함수는 훨씬 더 빠를 것입니다. 다른 답변을 확인하십시오.
jcsahnwaldt 모니카 복원

@Bhanu Pratap Singh 캐스트 또는 잘림을 방지하려면 int 및 long에 대해 별도의 메서드를 사용하는 것이 좋습니다. 그에 따라 대답을 편집했습니다.
jcsahnwaldt 모니카 복원

1
이것은 질문에 답할 수 없을뿐만 아니라 (자바에서 int의 gcd 또는 long의 위치) 제안 된 구현은 매우 비효율적입니다. 이것은 받아 들여진 대답이되어서는 안됩니다. 내가 아는 한 Java 런타임에는이 기능이 없지만 타사 라이브러리에 있습니다.
Florian F

135

내가 아는 한, 프리미티브에 대한 내장 메소드는 없습니다. 그러나 이렇게 간단한 것이 트릭을 수행해야합니다.

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 개의 난수를 실행했으며 매번 동의했습니다.
Tony Ennis

20
유클리드 알고리즘입니다. 아주 오래되었고 옳은 것으로 입증되었습니다. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

네, 볼 수는 있지만 해결하려면 시간이 더 필요합니다. 나는 그것을 좋아한다.
Tony Ennis

1
@Albert, 글쎄, 당신은 항상 일반 유형으로 시도하고 작동하는지 확인할 수 있습니다. 나는 단지 생각을 잘 모르겠지만 알고리즘은 당신이 실험 할 수있는 곳입니다. 표준 라이브러리 나 클래스에 관해서는 본 적이 없습니다. 객체를 만들 때 int, long 등을 지정해야합니다.
Matt

1
@Albert, 글쎄요, Matt가 구현을 제공 했음에도 불구하고 "더 일반적인"방식으로 작동하도록 직접 만들 수 있습니다. :)
Bart Kiers

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
명확히하기 위해 : 이것은 내가 요청한 것이 절대적으로 아닙니다.
Albert

11
이 경우 대체 구현이 존재하지 않기 때문에 원하지 않는다고 지정하지 않았습니다. 나중에 야 구현을 찾지 않고 게시물을 편집했습니다. 나는 다른 사람들이 적절하게 "아니오"라고 대답했다고 믿는다.
Xorlev

2
a가 매우 크고 b가 작 으면 느립니다. '%'솔루션은 훨씬 더 빠릅니다.
Bruce Feist

12

2
흥미롭게도 Guava는 유클리드 "모듈로"방법이 아니라 40 % 더 빠르다고 주장하는 이진 GCD 알고리즘을 사용합니다. 꽤 효율적이고 잘 테스트되었다고 말하는 것이 안전합니다.
Florian F

12

구아바가 없으면 다음과 같이 정의합니다.

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


7

바이너리 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 알고리즘의 변형입니다. 표준 알고리즘입니다.
Bastian 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);
}

이것에 대한 하나의 가장자리 경우는 두 경우이다 a하고 b있다 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);0 인수에 대해 동작이 진정으로 대칭이되도록 사용 하는 것이 좋습니다 .
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 이상을 사용 Integer.numberOfTrailingZeros()하는 경우 필요한 검사 및 반복 횟수를 줄이는 데 사용하는 반복 바이너리 GCD 알고리즘입니다 .

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 Collapse

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의 알고리즘을 사용하여 두 정수의 "Greatest Common Divisor"를 얻습니다. 두 개의 정수를 받고 그 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

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;
    }

0

Commons-MathGuava에서 제공하는 GCD 기능 에는 약간의 차이가 있습니다.

  • Commons-Math는 ArithematicException.classfor Integer.MIN_VALUE또는 Long.MIN_VALUE.
    • 그렇지 않으면 값을 절대 값으로 처리합니다.
  • Guava는 IllegalArgumentException.class모든 음수 값에 대해를 던집니다 .

-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 : 3 개의 정수

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)그래야 6하지만 12귀하의 예에 의한 것입니다. 그러나 12는 30과 42의 제수가 아닙니다 gcd. 재귀 적으로 호출해야합니다 . Matt의 답변을 보거나 Euclidean 알고리즘에 대한 Wikipedia를 참조하십시오.
Albert
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.