Java: Holen Sie sich den größten gemeinsamen Teiler


90

Ich habe gesehen, dass eine solche Funktion existiert für BigInteger, dh BigInteger#gcd. Gibt es noch andere Funktionen in Java , die auch Arbeit für andere Arten ( int, longoder Integer)? Es scheint, dass dies sinnvoll wäre java.lang.Math.gcd(bei allen Arten von Überlastungen), aber es ist nicht da. Ist es woanders?


(Verwechseln Sie diese Frage bitte nicht mit "Wie implementiere ich das selbst?")


7
Warum ist die akzeptierte Antwort eine, die Ihnen sagt, wie Sie sie selbst implementieren können - obwohl eine vorhandene Implementierung verpackt wird? =)
Djjeck

Ich stimme Ihrer Beobachtung zu. GCD sollte eine Klasse mit einer Reihe überladener statischer Methoden sein, die zwei Zahlen akzeptiert und die gcd angibt. Und es sollte Teil des Pakets java.math sein.
Anu

Antworten:


78

Für int und long, als Primitive, nicht wirklich. Für Integer ist es möglich, dass jemand eine geschrieben hat.

Da BigInteger eine (mathematisch / funktionale) Obermenge von int, Integer, long und Long ist, konvertieren Sie diese Typen in eine BigInteger, führen Sie die GCD durch und konvertieren Sie das Ergebnis zurück.

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()ist viel besser.
Albert


4
Wenn diese Funktion häufig aufgerufen wird (dh millionenfach), sollten Sie int oder long nicht in BigInteger konvertieren. Eine Funktion, die nur primitive Werte verwendet, ist wahrscheinlich um eine Größenordnung schneller. Überprüfen Sie die anderen Antworten.
jcsahnwaldt Reinstate Monica

@Bhanu Pratap Singh Um Casting oder Kürzung zu vermeiden, ist es besser, getrennte Methoden für int und long zu verwenden. Ich habe die Antwort entsprechend bearbeitet.
jcsahnwaldt Reinstate Monica

1
Dies beantwortet nicht nur nicht die Frage (wo ist gcd für int oder long in Java), sondern die vorgeschlagene Implementierung ist ziemlich ineffizient. Dies sollte nicht die akzeptierte Antwort sein. Soweit ich weiß, hat die Java-Laufzeit es nicht, aber es existiert in Bibliotheken von Drittanbietern.
Florian F

133

Soweit ich weiß, gibt es keine eingebaute Methode für Grundelemente. Aber so etwas Einfaches sollte den Trick machen:

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

Sie können es auch einzeilig machen, wenn Sie sich für so etwas interessieren:

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

Es ist zu beachten, dass es absolut keinen Unterschied zwischen den beiden gibt, da sie mit demselben Bytecode kompiliert werden.


Soweit ich das beurteilen kann, funktioniert es gut. Ich habe gerade 100.000 Zufallszahlen durch beide Methoden gezogen und sie stimmten jedes Mal überein.
Tony Ennis

18
Es ist ein euklidischer Algorithmus ... Es ist sehr alt und hat sich als richtig erwiesen. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Ja, ich kann es irgendwie sehen, aber ich brauche mehr Zeit, um es durchzuarbeiten. Ich mag das.
Tony Ennis

1
@ Albert, nun, Sie könnten es immer mit einem generischen Typ ausprobieren und sehen, ob es funktioniert. Ich weiß nicht nur einen Gedanken, aber der Algorithmus ist für Sie da, mit dem Sie experimentieren können. Was eine Standardbibliothek oder -klasse betrifft, habe ich noch nie eine gesehen. Sie müssen jedoch beim Erstellen des Objekts angeben, dass es sich um ein int, long usw. handelt.
Matt

1
@ Albert, nun, obwohl Matt eine Implementierung bereitgestellt hat, könnten Sie sie selbst auf eine "allgemeinere" Weise zum Laufen bringen, nein? :) :)
Bart Kiers

33

Oder der euklidische Algorithmus zur Berechnung der 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
Nur um zu verdeutlichen: Dies ist absolut nicht das, wonach ich gefragt habe.
Albert

11
In diesem Fall hatten Sie nicht angegeben, dass Sie keine alternativen Implementierungen wünschen, da eine nicht vorhanden war. Erst später haben Sie Ihren Beitrag bearbeitet und nicht nach Implementierungen gesucht. Ich glaube, andere hatten mehr als angemessen mit "Nein" geantwortet.
Xorlev

2
Dies wäre langsam, wenn a sehr groß und b klein ist. Die '%'-Lösungen wären viel schneller.
Bruce Feist

12

Verwenden Sie Guave LongMath.gcd()undIntMath.gcd()


2
Interessanterweise verwendet Guava nicht die euklidische "Modulo" -Methode, sondern den binären GCD-Algorithmus, von dem sie behaupten, er sei 40% schneller. Man kann mit Sicherheit sagen, dass es ziemlich effizient und gut getestet ist.
Florian F

12

Wenn ich keine Guave habe, definiere ich Folgendes:

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


7

Sie können diese Implementierung des binären GCD-Algorithmus verwenden

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

}}

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


Es ist eine Variation von Steins Algorithmus, die ausnutzt, dass das Schalten auf den meisten Maschinen eine relativ billige Operation ist. Es ist ein Standardalgorithmus.
Bastian J

6

Einige Implementierungen funktionieren hier nicht richtig, wenn beide Zahlen negativ sind. gcd (-12, -18) ist 6, nicht -6.

Es sollte also ein absoluter Wert zurückgegeben werden, so etwas wie

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

Ein Randfall hierfür ist, wenn beide aund bsind Integer.MIN_VALUE, erhalten Sie Integer.MIN_VALUEals Ergebnis zurück, was negativ ist. Dies kann akzeptabel sein. Das Problem ist, dass gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31 ist, aber 2 ^ 31 nicht als ganze Zahl ausgedrückt werden kann.
Michael Anderson

Ich würde auch empfehlen, if(a==0 || b==0) return Math.abs(a+b);damit das Verhalten für Nullargumente wirklich symmetrisch ist.
Michael Anderson

3

Wir können die rekursive Funktion verwenden, um gcd zu finden

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

Wenn Sie Java 1.5 oder höher verwenden, handelt es sich um einen iterativen binären GCD-Algorithmus, mit Integer.numberOfTrailingZeros()dem die Anzahl der erforderlichen Überprüfungen und Iterationen verringert wird.

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

Gerätetest:

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

Ähnlich wie bei MutableBigInteger.binaryGcd (int, int) ist letzteres leider nicht zugänglich. Aber trotzdem cool!
Mostowski Zusammenbruch

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

Diese Methode verwendet den Euklid-Algorithmus, um den "größten gemeinsamen Teiler" von zwei ganzen Zahlen zu erhalten. Es empfängt zwei ganze Zahlen und gibt deren gcd zurück. so einfach!


1

Ist es woanders?

Apache!- es hat sowohl gcd als auch lcm, also cool!

Aufgrund der Tiefe ihrer Implementierung ist sie jedoch langsamer als eine einfache handgeschriebene Version (falls es darauf ankommt).


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

Können Sie mit einer Erklärung erläutern, wie dies helfen könnte?
kommradHomer

0

Ich habe diese Methode verwendet, die ich mit 14 Jahren erstellt habe.

    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

Die von Commons-Math und Guava bereitgestellten GCD-Funktionen weisen einige Unterschiede auf.

  • Commons-Math wirft ein ArithematicException.classnur für Integer.MIN_VALUEoder Long.MIN_VALUE.
    • Andernfalls wird der Wert als absoluter Wert behandelt.
  • Guave wirft ein IllegalArgumentException.classfür alle negativen Werte.

-3

Die% geben uns die gcd Zwischen zwei Zahlen bedeutet dies: -% oder mod von big_number / small_number sind = gcd, und wir schreiben es so auf Java big_number % small_number .

EX1: für zwei ganze Zahlen

  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: für drei ganze Zahlen

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
Dies ist falsch, zB gcd(42, 30)sollte es sein, 6aber es ist 12durch Ihr Beispiel. Aber 12 ist kein Teiler von 30 und keiner von 42. Sie sollten gcdrekursiv aufrufen . Sehen Sie sich die Antwort von Matt an oder suchen Sie in Wikipedia nach dem euklidischen Algorithmus.
Albert
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.