Java: dapatkan pembagi persekutuan terbesar


91

Saya telah melihat bahwa fungsi seperti itu ada untuk BigInteger, yaitu BigInteger#gcd. Apakah ada fungsi lain di Java yang juga berfungsi untuk tipe lain ( int, longatau Integer)? Tampaknya ini masuk akal karena java.lang.Math.gcd(dengan semua jenis kelebihan beban) tetapi tidak ada. Apakah itu di tempat lain?


(Jangan bingung pertanyaan ini dengan "bagaimana cara menerapkan ini sendiri", tolong!)


7
Mengapa jawaban yang diterima adalah jawaban yang memberi tahu Anda bagaimana menerapkannya sendiri - meskipun membungkus implementasi yang sudah ada? =)
djjeck

Saya setuju dengan pengamatan Anda. GCD harus merupakan kelas dengan sekumpulan metode statis kelebihan beban yang menggunakan dua angka dan memberikannya gcd. Dan itu harus menjadi bagian dari paket java.math.
anu

Jawaban:


81

Untuk int dan long, sebagai primitif, tidak juga. Untuk Integer, mungkin saja seseorang menulisnya.

Mengingat BigInteger adalah superset (matematis / fungsional) dari int, Integer, long, dan Long, jika Anda perlu menggunakan tipe ini, konversikan ke BigInteger, lakukan GCD, dan ubah hasilnya kembali.

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()jauh lebih baik.
Albert


5
Jika fungsi ini sering dipanggil (misalnya jutaan kali), Anda tidak boleh mengonversi int atau long menjadi BigInteger. Sebuah fungsi yang hanya menggunakan nilai primitif kemungkinan akan menjadi urutan besarnya lebih cepat. Periksa jawaban lainnya.
jcsahnwaldt Memulihkan Monica

@Bhanu Pratap Singh Untuk menghindari casting atau pemotongan, lebih baik menggunakan metode terpisah untuk int dan long. Saya mengedit jawabannya sesuai.
jcsahnwaldt Memulihkan Monica

1
Ini tidak hanya tidak menjawab pertanyaan (di mana gcd untuk int atau long di Java) tetapi implementasi yang diusulkan cukup tidak efisien. Ini seharusnya bukan jawaban yang diterima. Sejauh yang saya tahu runtime Java tidak memilikinya, tetapi ada di perpustakaan pihak ketiga.
Florian F

135

Sejauh yang saya tahu, tidak ada metode bawaan untuk primitif. Tetapi sesuatu yang sederhana seperti ini seharusnya berhasil:

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

Anda juga dapat membuat satu baris jika Anda menyukai hal semacam itu:

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

Perlu dicatat bahwa sama sekali tidak ada perbedaan antara keduanya saat mereka dikompilasi ke kode byte yang sama.


Sejauh yang saya tahu itu berfungsi dengan baik. Saya baru saja menjalankan 100.000 nomor acak melalui kedua metode dan mereka setuju setiap kali.
Tony Ennis

20
Ini algoritma Euclidean ... Ini sangat tua dan terbukti benar. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Ya, saya bisa melihatnya tetapi saya perlu lebih banyak waktu untuk mengatasinya. Saya suka itu.
Tony Ennis

1
@Albert, Anda selalu bisa mencobanya dengan tipe umum dan lihat apakah berhasil. Saya tidak tahu hanya sebuah pikiran, tetapi algoritme ada untuk Anda coba. Sejauh beberapa perpustakaan atau kelas standar, saya belum pernah melihatnya. Anda masih perlu menentukan saat membuat objek yang int, long, dll .. meskipun.
Matt

1
@Albert, yah, meskipun Matt menyediakan implementasi, Anda sendiri dapat membuatnya bekerja dengan, seperti yang Anda katakan, cara "lebih umum", bukan? :)
Bart Kiers

33

Atau algoritma Euclidean untuk menghitung 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
Hanya untuk memperjelas: Ini sama sekali bukan yang saya minta.
Albert

11
Dalam kasus ini, Anda belum menentukan bahwa Anda tidak menginginkan penerapan alternatif karena tidak ada penerapan alternatif. Baru kemudian Anda mengedit posting Anda tidak mencari implementasi. Saya percaya orang lain menjawab "tidak" lebih dari cukup.
Xorlev

2
Ini akan lambat jika a sangat besar dan b kecil. Solusi '%' akan jauh lebih cepat.
Bruce Feist

12

Gunakan Guava LongMath.gcd()danIntMath.gcd()


2
Menariknya, Guava tidak menggunakan metode "modulo" Euclidean tetapi algoritma GCD biner yang diklaim 40% lebih cepat. Aman untuk mengatakan itu cukup efisien dan teruji dengan baik.
Florian F

12

Kecuali saya punya Jambu biji, saya definisikan seperti ini:

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


7

Anda dapat menggunakan implementasi algoritme Binary GCD ini

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

}

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


Ini adalah variasi dari algoritma Stein yang mengeksploitasi hal itu pada kebanyakan mesin, perpindahan adalah operasi yang relatif murah. Ini algoritma standar.
Bastian J

6

Beberapa penerapan di sini tidak berfungsi dengan benar jika kedua bilangan tersebut negatif. gcd (-12, -18) adalah 6, bukan -6.

Jadi nilai absolut harus dikembalikan, seperti

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

Salah satu kasus tepi untuk ini adalah jika kedua adan byang Integer.MIN_VALUE, Anda akan mendapatkan Integer.MIN_VALUEkembali sebagai hasil, yang negatif. Ini mungkin bisa diterima. Masalahnya adalah bahwa gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, tetapi 2 ^ 31 tidak dapat diekspresikan sebagai bilangan bulat.
Michael Anderson

Saya juga merekomendasikan penggunaan if(a==0 || b==0) return Math.abs(a+b);sehingga perilakunya benar-benar simetris untuk argumen nol.
Michael Anderson

3

kita dapat menggunakan fungsi rekursif untuk menemukan 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

Jika Anda menggunakan Java 1.5 atau yang lebih baru, ini adalah algoritme GCD biner berulang yang digunakan Integer.numberOfTrailingZeros()untuk mengurangi jumlah pemeriksaan dan iterasi yang diperlukan.

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

Tes unit:

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

Mirip dengan MutableBigInteger.binaryGcd (int, int), sayangnya yang terakhir tidak dapat diakses. Tapi keren kok!
Mostowski runtuh

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

Metode ini menggunakan algoritma Euclid untuk mendapatkan "Pembagi Persekutuan Terbesar" dari dua bilangan bulat. Ini menerima dua bilangan bulat dan mengembalikan gcd-nya. semudah itu!


1

Apakah itu di tempat lain?

Apache! - ini memiliki gcd dan lcm, sangat keren!

Namun, karena penerapannya yang mendalam, ini lebih lambat dibandingkan dengan versi tulisan tangan sederhana (jika itu penting).


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

dapatkah Anda menjelaskan dengan penjelasan bagaimana hal ini dapat membantu?
kommradHomer

0

Saya menggunakan metode ini yang saya buat ketika saya berusia 14 tahun.

    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

Fungsi GCD yang disediakan oleh Commons-Math dan Guava memiliki beberapa perbedaan.

  • Commons-Math melempar ArithematicException.classhanya untuk Integer.MIN_VALUEatau Long.MIN_VALUE.
    • Jika tidak, tangani nilai sebagai nilai absolut.
  • Jambu biji melempar IllegalArgumentException.classuntuk nilai negatif apa pun.

-3

% Akan memberi kita gcd antara dua angka, artinya: -% atau mod dari bilangan_besar / bilangan_kecil adalah = gcd, dan kita menulisnya di java seperti ini big_number % small_number.

EX1: untuk dua bilangan bulat

  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: untuk tiga bilangan bulat

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
Ini salah, misalnya gcd(42, 30)seharusnya 6tetapi 12dengan teladan Anda. Tetapi 12 bukanlah pembagi dari 30 dan tidak satu pun dari 42. Anda harus memanggil gcdsecara rekursif. Lihat jawaban Matt atau lihat Wikipedia untuk algoritma Euclidean.
Albert
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.