Java: en büyük ortak böleni alın


91

Böyle bir işlev için var olduğunu gördük BigIntegeryani BigInteger#gcd. Java diğer fonksiyonlar da hangi diğer türleri için çalışma (var mı int, longyoksa Integer)? Görünüşe göre bu java.lang.Math.gcd(her türlü aşırı yüklemede) mantıklı geliyor ama orada değil. Başka bir yerde mi


(Bu soruyu "Bunu kendim nasıl uygularım" ile karıştırmayın, lütfen!)


7
Neden kabul edilen cevap, mevcut bir uygulamayı tamamlamanıza rağmen, bunu kendiniz nasıl uygulayacağınızı söyleyen bir cevap? =)
djjeck

Gözleminize katılıyorum. GCD, iki sayıyı alan ve ona gcd veren bir grup aşırı yüklenmiş statik yöntem içeren bir sınıf olmalıdır. Ve java.math paketinin bir parçası olmalıdır.
anu

Yanıtlar:


81

İnt ve long için, ilkeller kadar, gerçekten değil. Tamsayı için, birinin yazması mümkündür.

BigInteger'ın int, Integer, long ve Long'un (matematiksel / işlevsel) bir üst kümesi olduğu göz önüne alındığında, bu türleri kullanmanız gerekiyorsa, bunları bir BigInteger'a dönüştürün, GCD'yi yapın ve sonucu geri dönüştürün.

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()daha iyi.
Albert


5
Bu işlev sık sık çağrılırsa (yani milyonlarca kez) int veya long'u BigInteger'a dönüştürmemelisiniz. Yalnızca ilkel değerleri kullanan bir işlev, büyük olasılıkla daha hızlı olacaktır. Diğer cevapları kontrol edin.
jcsahnwaldt Monica'yı eski haline getir

@Bhanu Pratap Singh Döküm veya kesmeyi önlemek için, int ve long için ayrı yöntemler kullanmak daha iyidir. Cevabı buna göre düzenledim.
jcsahnwaldt Monica'yı eski haline getir

1
Bu sadece soruyu yanıtlamakla kalmaz (Java'da int veya long için gcd nerede bulunur), ancak önerilen uygulama oldukça verimsizdir. Kabul edilen cevap bu olmamalı. Bildiğim kadarıyla Java çalışma zamanı buna sahip değil, ancak üçüncü taraf kitaplıklarında var.
Florian F

135

Bildiğim kadarıyla, ilkeller için yerleşik bir yöntem yok. Ancak bu kadar basit bir şey hile yapmalı:

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

Bu tür şeylerle ilgileniyorsanız, tek satırlık da yapabilirsiniz:

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

Aynı bayt koduna göre derledikleri için ikisi arasında kesinlikle hiçbir fark olmadığına dikkat edilmelidir .


Anladığım kadarıyla iyi çalışıyor. Her iki yöntemle de 100.000 rastgele sayı çalıştırdım ve her seferinde anlaştılar.
Tony Ennis

20
Öklid algoritması ... Çok eski ve doğru olduğu kanıtlandı. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Evet, biraz görebiliyorum ama üzerinde çalışmak için daha fazla zamana ihtiyacım var. Bunu sevdim.
Tony Ennis

1
@Albert, bunu her zaman genel bir türle deneyebilir ve işe yarayıp yaramadığını görebilirsin. Sadece bir düşünce bilmiyorum, ancak deney yapabileceğiniz algoritma orada. Standart bir kütüphane veya sınıfa gelince, hiç görmedim. Yine de nesneyi oluşturduğunuzda bunun bir int, uzun vb. Olduğunu belirtmeniz gerekecektir.
Mat

1
@Albert, Matt bir uygulama sağlasa da, sizin deyiminizle "daha genel" bir şekilde çalışmasını kendiniz sağlayabilirsiniz, değil mi? :)
Bart Kiers

33

Veya OBEB'yi hesaplamak için Öklid algoritması ...

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
Sadece açıklığa kavuşturmak için: Bu kesinlikle istediğim şey değil.
Albert

11
Bu durumda, mevcut olmadığı için alternatif uygulamalar istemediğinizi belirtmemişsinizdir. Ancak daha sonra yayınınızı, uygulamaları aramadan düzenlediniz. Diğerlerinin yeterince "hayır" cevabını fazlasıyla verdiğine inanıyorum.
Xorlev

2
Bu, a çok büyük ve b küçükse yavaş olacaktır. '%' Çözümleri çok daha hızlı olacaktır.
Bruce Feist

12

Guava kullanın LongMath.gcd()veIntMath.gcd()


2
İlginç bir şekilde Guava, Öklid "modulo" yöntemini değil,% 40 daha hızlı olduğunu iddia ettikleri ikili GCD algoritmasını kullanıyor. Oldukça verimli ve iyi test edilmiş olduğunu söylemek güvenlidir.
Florian F

12

Guava'ya sahip olmadıkça, şöyle tanımlıyorum:

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


7

Bu İkili GCD algoritması uygulamasını kullanabilirsiniz

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 adresinden


Bu, çoğu makinede vites değiştirmenin nispeten ucuz bir işlem olduğundan yararlanan, Stein'ın algoritmasının bir varyasyonudur. Bu standart bir algoritmadır.
Bastian J

6

Her iki sayı da negatifse buradaki bazı uygulamalar düzgün çalışmıyor. gcd (-12, -18) 6'dır, -6 değil.

Bu nedenle, mutlak bir değer döndürülmelidir.

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

Bunun için bir kenarı durumda hem eğer olduğunu ave bvardır Integer.MIN_VALUE, elde edeceğiniz Integer.MIN_VALUEnegatif sonucu olarak geri. Bu kabul edilebilir olabilir. Sorun şu ki gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, ancak 2 ^ 31 bir tamsayı olarak ifade edilemez.
Michael Anderson

Ayrıca if(a==0 || b==0) return Math.abs(a+b);, davranışın sıfır bağımsız değişken için gerçekten simetrik olması için kullanmanızı tavsiye ederim .
Michael Anderson

3

gcd'yi bulmak için özyinelemeli işlevi kullanabiliriz

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 veya sonraki bir sürümünü kullanıyorsanız, bu, Integer.numberOfTrailingZeros()gereken denetim ve yineleme sayısını azaltmak için kullanılan yinelemeli bir ikili GCD algoritmasıdır .

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

Ünite testi:

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'ye (int, int) benzer şekilde, maalesef ikincisine erişilemez. Ama yine de harika!
Mostowski Çöküşü

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

Bu yöntem, iki tamsayının "En Büyük Ortak Bölenini" elde etmek için Öklid algoritmasını kullanır. İki tamsayı alır ve bunların gcd'sini döndürür. sadece bu kadar kolay!


1

Başka bir yerde mi

Apaçi! - hem gcd hem de lcm'ye sahip, çok havalı!

Bununla birlikte, uygulamalarının derinliği nedeniyle, basit elle yazılmış sürüme kıyasla (önemliyse) daha yavaştır.


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

bunun nasıl yardımcı olabileceğine dair bir açıklama yapabilir misin?
kommradHomer

0

14 yaşında oluşturduğum bu yöntemi kullandım.

    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-Math ve Guava tarafından sağlanan GCD işlevlerinin bazı farklılıkları vardır.

  • Commons-Math ArithematicException.classyalnızca for Integer.MIN_VALUEveya Long.MIN_VALUE.
    • Aksi takdirde, değeri mutlak bir değer olarak ele alır.
  • Guava IllegalArgumentException.classherhangi bir negatif değer için bir atar .

-3

%, Bize gcd'yi verecek İki sayı arasında, bunun anlamı: -% veya mod big_number / small_number = gcd ve bunu java'ya şöyle yazıyoruz big_number % small_number .

EX1: iki tam sayı için

  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: üç tam sayı için

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
Bu yanlış, örneğin gcd(42, 30)olmalı 6ama 12senin örneğine göre. Ancak 12, 30'un bölen değildir ve 42'nin de bölen değildir gcd. Özyinelemeli aramalısınız . Öklid algoritması için Matt'in cevabına bakın veya Wikipedia'ya bakın.
Albert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.