Java: obtenir le plus grand diviseur commun


91

J'ai vu qu'une telle fonction existe pour BigInteger, ie BigInteger#gcd. Existe-t-il d'autres fonctions en Java qui fonctionnent également pour d'autres types ( int, longou Integer)? Il semble que cela aurait du sens java.lang.Math.gcd(avec toutes sortes de surcharges), mais ce n'est pas là. Est-ce ailleurs?


(Ne confondez pas cette question avec "comment puis-je implémenter cela moi-même", s'il vous plaît!)


7
Pourquoi la réponse acceptée vous indique-t-elle comment l'implémenter vous-même - tout en englobant une implémentation existante? =)
djjeck

Je suis d'accord avec votre observation. GCD devrait avoir une classe avec un tas de méthodes statiques surchargées qui prend en deux nombres et donne son pgcd. Et cela devrait faire partie du package java.math.
anu

Réponses:


79

Pour int et long, comme primitifs, pas vraiment. Pour Integer, il est possible que quelqu'un en ait écrit un.

Étant donné que BigInteger est un sur-ensemble (mathématique / fonctionnel) de int, Integer, long et Long, si vous avez besoin d'utiliser ces types, convertissez-les en BigInteger, effectuez le GCD et reconvertissez le résultat.

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()c'est beaucoup mieux.
Albert


4
Si cette fonction est appelée souvent (c'est-à-dire des millions de fois), vous ne devez pas convertir int ou long en BigInteger. Une fonction utilisant uniquement des valeurs primitives sera probablement un ordre de grandeur plus rapide. Vérifiez les autres réponses.
jcsahnwaldt Reinstate Monica

@Bhanu Pratap Singh Pour éviter le cast ou la troncature, il est préférable d'utiliser des méthodes séparées pour int et long. J'ai édité la réponse en conséquence.
jcsahnwaldt Réintègre Monica

1
Non seulement cela ne répond pas à la question (où est gcd pour int ou long en Java), mais l'implémentation proposée est assez inefficace. Cela ne devrait pas être la réponse acceptée. Autant que je sache, le runtime Java ne l'a pas, mais il existe dans des bibliothèques tierces.
Florian F

134

Autant que je sache, il n'y a pas de méthode intégrée pour les primitives. Mais quelque chose d'aussi simple que cela devrait faire l'affaire:

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

Vous pouvez également le mettre en ligne si vous aimez ce genre de choses:

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

Il convient de noter qu'il n'y a absolument aucune différence entre les deux car ils se compilent avec le même code d'octet.


Autant que je sache, cela fonctionne très bien. Je viens de lancer 100 000 numéros aléatoires selon les deux méthodes et ils ont accepté à chaque fois.
Tony Ennis

19
C'est un algorithme euclidien ... Il est très ancien et a fait ses preuves. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Oui, je peux en quelque sorte le voir, mais j'ai besoin de plus de temps pour y travailler. Je l'aime.
Tony Ennis

1
@Albert, eh bien, vous pouvez toujours l'essayer avec un type générique et voir si cela fonctionne. Je ne sais pas juste une pensée, mais l'algorithme est là pour que vous puissiez expérimenter. En ce qui concerne une bibliothèque ou une classe standard, je n'en ai jamais vu. Vous devrez tout de même spécifier lors de la création de l'objet qu'il s'agit d'un int, long, etc.
Matt

1
@Albert, eh bien, bien que Matt ait fourni une implémentation, vous pourriez vous-même la faire fonctionner d'une manière, comme vous le dites, "plus générique", non? :)
Bart Kiers

33

Ou l'algorithme euclidien de calcul du 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
Juste pour clarifier: ce n'est absolument pas ce que je demandais.
Albert

11
Dans ce cas, vous n'aviez pas spécifié que vous ne vouliez pas d'implémentations alternatives car il n'en existait pas. Ce n'est que plus tard que vous avez modifié votre message sans rechercher d'implémentations. Je crois que d'autres avaient répondu «non» plus que convenablement.
Xorlev

2
Ce serait lent si a est très grand et b est petit. Les solutions «%» seraient beaucoup plus rapides.
Bruce Feist

12

Utilisez de la goyave LongMath.gcd()etIntMath.gcd()


2
Fait intéressant, Guava n'utilise pas la méthode euclidienne "modulo" mais l'algorithme binaire GCD qu'ils prétendent être 40% plus rapide. Il est sûr de dire qu'il est assez efficace et bien testé.
Florian F

12

Sauf si j'ai de la goyave, je définis comme ceci:

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


7

Vous pouvez utiliser cette implémentation de l' algorithme Binary 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));
}

}

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


C'est une variante de l'algorithme de Stein qui exploite le fait que sur la plupart des machines, le décalage est une opération relativement bon marché. C'est un algorithme standard.
Bastian J

6

Certaines implémentations ici ne fonctionnent pas correctement si les deux nombres sont négatifs. pgcd (-12, -18) vaut 6 et non -6.

Une valeur absolue doit donc être renvoyée, quelque chose comme

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

Un cas particulier pour cela est que si les deux aet bsont Integer.MIN_VALUE, vous obtiendrez Integer.MIN_VALUEle résultat, ce qui est négatif. Cela peut être acceptable. Le problème étant que pgcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, mais 2 ^ 31 ne peut pas être exprimé sous forme d'entier.
Michael Anderson

Je recommande également d'utiliser if(a==0 || b==0) return Math.abs(a+b);pour que le comportement soit vraiment symétrique pour zéro argument.
Michael Anderson

3

nous pouvons utiliser la fonction récursive pour trouver 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

Si vous utilisez Java 1.5 ou version ultérieure, il s'agit d'un algorithme GCD binaire itératif qui permet Integer.numberOfTrailingZeros()de réduire le nombre de vérifications et d'itérations requises.

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

Test de l'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 );
        }
    }
}

Similaire à MutableBigInteger.binaryGcd (int, int), ce dernier n'est malheureusement pas accessible. Mais cool quand même!
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;
}

Cette méthode utilise l'algorithme d'Euclid pour obtenir le "plus grand diviseur commun" de deux entiers. Il reçoit deux entiers et en renvoie le pgcd. aussi simple que cela!


1

Est-ce ailleurs?

Apache!- il a à la fois pgcd et lcm, tellement cool!

Cependant, en raison de la profondeur de leur mise en œuvre, elle est plus lente par rapport à une simple version manuscrite (si cela compte).


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

pouvez-vous expliquer comment cela pourrait aider?
kommradHomer

0

J'ai utilisé cette méthode que j'ai créée quand j'avais 14 ans.

    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

Ces fonctions GCD fournies par Commons-Math et Guava présentent quelques différences.

  • Commons-Math lance un ArithematicException.classseul pour Integer.MIN_VALUEou Long.MIN_VALUE.
    • Sinon, gère la valeur comme une valeur absolue.
  • Guava lance un IllegalArgumentException.classpour toutes les valeurs négatives.

-3

Le% va nous donner le pgcd Entre deux nombres, ça veut dire: -% ou mod de big_number / small_number sont = gcd, et on l'écrit sur java comme ça big_number % small_number .

EX1: pour deux entiers

  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: pour trois entiers

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
C'est faux, par exemple gcd(42, 30)devrait être 6mais c'est 12par votre exemple. Mais 12 n'est pas un diviseur de 30 ni de 42. Vous devez appeler gcdrécursivement. Voir la réponse de Matt ou chercher sur Wikipedia l'algorithme euclidien.
Albert
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.