Java: obtener el máximo común divisor


91

He visto que existe tal función para BigInteger, es decir BigInteger#gcd. ¿Hay otras funciones en Java que también funcionen para otros tipos ( int, longo Integer)? Parece que esto tendría sentido como java.lang.Math.gcd(con todo tipo de sobrecargas) pero no está ahí. ¿Está en otro lugar?


(¡No confunda esta pregunta con "cómo implemento esto yo mismo", por favor!)


7
¿Por qué la respuesta aceptada es una que le dice cómo implementarla usted mismo, aunque envuelve una implementación existente? =)
djjeck

Estoy de acuerdo con tu observación. GCD debería ser una clase con un montón de métodos estáticos sobrecargados que toma dos números y le da su gcd. Y debería ser parte del paquete java.math.
anu

Respuestas:


79

Por int y long, como primitivos, no realmente. Para Integer, es posible que alguien haya escrito uno.

Dado que BigInteger es un superconjunto (matemático / funcional) de int, Integer, long y Long, si necesita usar estos tipos, conviértalos a BigInteger, haga el GCD y vuelva a convertir el resultado.

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()es mucho mejor.
Albert


4
Si esta función se llama con frecuencia (es decir, millones de veces), no debería convertir int o long a BigInteger. Una función que usa solo valores primitivos probablemente será un orden de magnitud más rápida. Comprueba las otras respuestas.
jcsahnwaldt Reincorpora a Monica

@Bhanu Pratap Singh Para evitar la conversión o el truncamiento, es mejor usar métodos separados para int y long. Edité la respuesta en consecuencia.
jcsahnwaldt Reincorpora a Monica

1
Esto no solo no responde a la pregunta (dónde está gcd para int o long en Java) sino que la implementación propuesta es bastante ineficiente. Esta no debería ser la respuesta aceptada. Hasta donde yo sé, el tiempo de ejecución de Java no lo tiene, pero existe en bibliotecas de terceros.
Florian F

134

Hasta donde yo sé, no hay ningún método incorporado para primitivas. Pero algo tan simple como esto debería funcionar:

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

También puede agregar una línea si le gustan ese tipo de cosas:

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

Cabe señalar que no hay absolutamente ninguna diferencia entre los dos, ya que se compilan en el mismo código de bytes.


Por lo que puedo decir, funciona bien. Acabo de ejecutar 100.000 números aleatorios a través de ambos métodos y estuvieron de acuerdo cada vez.
Tony Ennis

19
Es un algoritmo euclidiano ... Es muy antiguo y está probado. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Sí, puedo verlo, pero necesito más tiempo para trabajar en ello. Me gusta.
Tony Ennis

1
@Albert, bueno, siempre puedes probarlo con un tipo genérico y ver si funciona. No sé solo una idea, pero el algoritmo está ahí para que experimentes. En cuanto a alguna biblioteca o clase estándar, nunca he visto una. Sin embargo, aún necesitará especificar cuando cree el objeto que es un int, long, etc.
Matt

1
@Albert, bueno, aunque Matt proporcionó una implementación, tú mismo podrías hacer que funcione de una manera, como dices, "más genérica", ¿no? :)
Bart Kiers

33

O el algoritmo euclidiano para calcular el MCD ...

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
Solo para aclarar: esto no es absolutamente lo que estaba pidiendo.
Albert

11
En este caso, no especificó que no deseaba implementaciones alternativas ya que no existía una. Solo más tarde editó su publicación sin buscar implementaciones. Creo que otros habían respondido "no" más que adecuadamente.
Xorlev

2
Esto sería lento si a es muy grande y b es pequeño. Las soluciones '%' serían mucho más rápidas.
Bruce Feist

12

2
Curiosamente, Guava no utiliza el método de "módulo" euclidiano, sino el algoritmo binario GCD que, según ellos, es un 40% más rápido. Es seguro decir que es bastante eficiente y está bien probado.
Florian F

12

A menos que tenga Guayaba, defino así:

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


7

Puede utilizar esta implementación del algoritmo 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));
}

}

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


Es una variación del algoritmo de Stein que aprovecha que en la mayoría de las máquinas, el cambio es una operación relativamente barata. Es un algoritmo estándar.
Bastian J

6

Algunas implementaciones aquí no funcionan correctamente si ambos números son negativos. gcd (-12, -18) es 6, no -6.

Entonces se debe devolver un valor absoluto, algo como

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

Un caso de ventaja para esto es si ambos ay lo bson Integer.MIN_VALUE, obtendrás Integer.MIN_VALUEcomo resultado, que es negativo. Esto puede ser aceptable. El problema es que mcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, pero 2 ^ 31 no se puede expresar como un número entero.
Michael Anderson

También recomendaría usarlo if(a==0 || b==0) return Math.abs(a+b);para que el comportamiento sea verdaderamente simétrico para cero argumentos.
Michael Anderson

3

podemos usar la función recursiva para encontrar 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 está utilizando Java 1.5 o posterior, este es un algoritmo GCD binario iterativo que se utiliza Integer.numberOfTrailingZeros()para reducir el número de comprobaciones e iteraciones necesarias.

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

Prueba de unidad:

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

Similar a MutableBigInteger.binaryGcd (int, int), desafortunadamente este último no es accesible. ¡Pero genial de todos modos!
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;
}

Este método usa el algoritmo de Euclides para obtener el "Divisor común más grande" de dos números enteros. Recibe dos enteros y devuelve el mcd de ellos. ¡así de fácil!


1

¿Está en otro lugar?

¡Apache!- tiene gcd y lcm, ¡genial!

Sin embargo, debido a la profundidad de su implementación, es más lento en comparación con la versión simple escrita a mano (si es que importa).


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

¿Puede explicar con más detalle cómo podría ayudar esto?
kommradHomer

0

Usé este método que creé cuando tenía 14 años.

    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

Esas funciones GCD proporcionadas por Commons-Math y Guava tienen algunas diferencias.

  • Commons-Math arroja un ArithematicException.classsolo para Integer.MIN_VALUEo Long.MIN_VALUE.
    • De lo contrario, maneja el valor como un valor absoluto.
  • Guava arroja un IllegalArgumentException.classvalor negativo.

-3

El% que nos va a dar el gcd Entre dos números, significa: -% o mod de big_number / small_number are = gcd, y lo escribimos en java así big_number % small_number .

EX1: para dos enteros

  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: para tres enteros

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
Esto es incorrecto, por ejemplo, gcd(42, 30)debería serlo, 6pero es 12por su ejemplo. Pero 12 no es divisor de 30 ni tampoco de 42. Debería llamar de forma gcdrecursiva. Vea la respuesta de Matt o busque en Wikipedia el algoritmo euclidiano.
Albert
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.