Java: lấy ước số chung lớn nhất


91

Tôi đã thấy rằng một chức năng như vậy tồn tại cho BigInteger, tức là BigInteger#gcd. Có những chức năng khác trong Java mà cũng làm việc với nhiều loại khác ( int, longhay Integer)? Có vẻ như điều này sẽ có ý nghĩa như java.lang.Math.gcd(với tất cả các loại quá tải) nhưng nó không có ở đó. Nó ở một nơi nào khác?


(Đừng nhầm lẫn câu hỏi này với "làm cách nào để tôi tự thực hiện điều này", làm ơn!)


7
Tại sao câu trả lời được chấp nhận là câu trả lời cho bạn biết cách tự triển khai nó - mặc dù gói một triển khai hiện có? =)
djjeck

Tôi đồng ý với quan sát của bạn. GCD nên là một lớp có một loạt các phương thức tĩnh đã được nạp chồng có hai số và cho nó là gcd. Và nó phải là một phần của gói java.math.
anu

Câu trả lời:


79

Đối với int và long, như là nguyên thủy, không thực sự. Đối với Integer, có thể ai đó đã viết một.

Cho rằng BigInteger là một tập siêu số (toán học / chức năng) của int, Integer, long và Long, nếu bạn cần sử dụng các loại này, hãy chuyển đổi chúng thành BigInteger, thực hiện GCD và chuyển đổi kết quả trở lại.

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()là tốt hơn nhiều.
Albert


4
Nếu hàm này được gọi thường xuyên (tức là hàng triệu lần), bạn không nên chuyển đổi int hoặc long thành BigInteger. Một hàm chỉ sử dụng các giá trị nguyên thủy có thể sẽ nhanh hơn. Kiểm tra các câu trả lời khác.
jcsahnwaldt Phục hồi Monica

@Bhanu Pratap Singh Để tránh ép kiểu hoặc cắt ngắn, tốt hơn nên sử dụng các phương thức riêng biệt cho int và long. Tôi đã chỉnh sửa câu trả lời cho phù hợp.
jcsahnwaldt Phục hồi Monica

1
Điều này không chỉ không trả lời câu hỏi (gcd ở đâu cho int hoặc long trong Java) mà việc triển khai được đề xuất khá không hiệu quả. Đây không phải là câu trả lời được chấp nhận. Theo như tôi biết thời gian chạy Java không có nó, nhưng nó tồn tại trong các thư viện của bên thứ ba.
Florian F

134

Theo như tôi biết, không có bất kỳ phương pháp tích hợp nào cho các nguyên thủy. Nhưng một cái gì đó đơn giản như thế này sẽ thực hiện được mẹo:

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

Bạn cũng có thể viết một dòng nếu bạn quan tâm đến vấn đề đó:

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

Cần lưu ý rằng hoàn toàn không có sự khác biệt giữa cả hai khi chúng biên dịch sang cùng một mã byte.


Theo như tôi có thể nói nó hoạt động tốt. Tôi chỉ chạy 100.000 số ngẫu nhiên mặc dù cả hai phương pháp và họ đã đồng ý mỗi lần.
Tony Ennis

19
Đó là thuật toán Euclide ... Nó rất cũ và đã được chứng minh là đúng. vi.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Đúng, tôi có thể thấy nó nhưng tôi cần thêm thời gian để làm việc với nó. Tôi thích nó.
Tony Ennis

1
@Albert, bạn luôn có thể dùng thử với loại chung và xem nó có hoạt động không. Tôi không biết chỉ là một suy nghĩ, nhưng thuật toán ở đó để bạn thử nghiệm. Đối với một số thư viện hoặc lớp học tiêu chuẩn, tôi chưa bao giờ thấy một. Mặc dù vậy, bạn vẫn sẽ cần chỉ định khi tạo đối tượng rằng đối tượng đó là int, long, v.v..
Matt

1
@Albert, tốt, mặc dù Matt đã cung cấp một bản triển khai, nhưng bản thân bạn có thể làm cho nó hoạt động theo cách "chung chung" hơn, phải không? :)
Bart Kiers

33

Hoặc thuật toán Euclid để tính 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
Chỉ cần làm rõ: Đây hoàn toàn không phải là những gì tôi yêu cầu.
Albert

11
Trong trường hợp này, bạn đã không chỉ định rằng bạn không muốn triển khai thay thế vì một triển khai không tồn tại. Chỉ sau này, bạn mới chỉnh sửa bài đăng của mình mà không tìm cách triển khai. Tôi tin rằng những người khác đã trả lời "không" nhiều hơn một cách đầy đủ.
Xorlev

2
Điều này sẽ chậm nếu a rất lớn và b nhỏ. Các giải pháp '%' sẽ nhanh hơn nhiều.
Bruce Feist

12

Sử dụng Ổi LongMath.gcd()IntMath.gcd()


2
Điều thú vị là Guava không sử dụng phương pháp "modulo" của Euclid mà là thuật toán GCD nhị phân mà họ cho là nhanh hơn 40%. Có thể nói rằng nó khá hiệu quả và đã được thử nghiệm tốt.
Florian F

12

Trừ khi tôi có Ổi, tôi định nghĩa như thế này:

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


7

Bạn có thể sử dụng việc triển khai thuật toán Binary GCD này

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

}

Từ http://introcs.cs.princeton.edu/java/23recursion/BinaryGCD.java.html


Đó là một biến thể của thuật toán Stein khai thác điều đó trên hầu hết các máy, dịch chuyển là một hoạt động tương đối rẻ. Đó là một thuật toán tiêu chuẩn.
Bastian J

6

Một số triển khai ở đây không hoạt động chính xác nếu cả hai số đều âm. gcd (-12, -18) là 6, không phải -6.

Vì vậy, một giá trị tuyệt đối sẽ được trả về, giống như

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

Một trường hợp lợi hại cho điều này là nếu cả hai abđều Integer.MIN_VALUE, bạn sẽ nhận được Integer.MIN_VALUEkết quả là kết quả âm. Điều này có thể chấp nhận được. Vấn đề là gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, nhưng 2 ^ 31 không thể được biểu thị dưới dạng số nguyên.
Michael Anderson

Tôi cũng khuyên bạn nên sử dụng if(a==0 || b==0) return Math.abs(a+b);để hành vi thực sự đối xứng với không đối số.
Michael Anderson

3

chúng ta có thể sử dụng hàm đệ quy để tìm 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

Nếu bạn đang sử dụng Java 1.5 trở lên thì đây là một thuật toán GCD nhị phân lặp đi lặp lại, sử dụng Integer.numberOfTrailingZeros()để giảm số lần kiểm tra và lặp lại cần thiết.

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

Bài kiểm tra đơn vị:

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

Tương tự như MutableBigInteger.binaryGcd (int, int), tiếc là không thể truy cập được sau này. Nhưng mát dù sao!
Mostowski Thu gọn vào

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

Phương pháp này sử dụng thuật toán Euclid để lấy "Số chia chung lớn nhất" của hai số nguyên. Nó nhận hai số nguyên và trả về gcd của chúng. chỉ đơn giản vậy thôi!


1

Nó ở một nơi nào khác?

Apache!- nó có cả gcd và lcm, quá tuyệt!

Tuy nhiên, do độ sâu của việc triển khai, nó chậm hơn so với phiên bản viết tay đơn giản (nếu nó quan trọng).


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

bạn có thể giải thích chi tiết cách này có thể giúp ích gì không?
kommradHomer, 30-07-16

0

Tôi đã sử dụng phương pháp này mà tôi đã tạo ra khi tôi 14 tuổi.

    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

Các hàm GCD do Commons-MathGuava cung cấp có một số khác biệt.

  • Commons-Math ném ArithematicException.classchỉ cho Integer.MIN_VALUEhoặc Long.MIN_VALUE.
    • Nếu không, xử lý giá trị dưới dạng giá trị tuyệt đối.
  • Guava ném một IllegalArgumentException.classgiá trị âm cho bất kỳ giá trị âm nào.

-3

% Sẽ cung cấp cho chúng ta gcd Giữa hai số, nó có nghĩa là: -% hoặc mod của big_number / small_number are = gcd, và chúng ta viết nó trên java như thế này big_number % small_number.

EX1: cho hai số nguyên

  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: cho ba số nguyên

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
Điều này là sai, ví dụ như gcd(42, 30)nên có 6nhưng đó là 12bởi ví dụ của bạn. Nhưng 12 không phải là ước của 30 và cũng không phải của 42. Bạn nên gọi gcdđệ quy. Xem câu trả lời của Matt hoặc tìm thuật toán Euclide trên Wikipedia.
Albert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.