Java: obtenha o maior divisor comum


91

Eu vi que essa função existe para BigInteger, ou seja BigInteger#gcd. Existem outras funções em Java que também trabalho para outros tipos ( int, longou Integer)? Parece que faria sentido java.lang.Math.gcd(com todos os tipos de sobrecargas), mas não está lá. Está em outro lugar?


(Não confunda esta pergunta com "como faço para implementar isso sozinho", por favor!)


7
Por que a resposta aceita diz a você como implementá-lo sozinho - embora envolva uma implementação existente? =)
djjeck

Eu concordo com sua observação. GCD deve ser uma classe com um monte de métodos estáticos sobrecarregados que pega em dois números e dá seu gcd. E deve fazer parte do pacote java.math.
anu

Respostas:


79

Por muito tempo, como primitivos, não realmente. Para Integer, é possível que alguém tenha escrito um.

Dado que BigInteger é um superconjunto (matemático / funcional) de int, Integer, long e Long, se você precisar usar esses tipos, converta-os em BigInteger, faça o GCD e converta o resultado de volta.

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()é muito melhor.
Albert,


4
Se esta função for chamada frequentemente (ou seja, milhões de vezes), você não deve converter int ou long para BigInteger. Uma função que usa apenas valores primitivos provavelmente será uma ordem de magnitude mais rápida. Verifique as outras respostas.
jcsahnwaldt Reintegrar Monica em

@Bhanu Pratap Singh Para evitar conversão ou truncamento, é melhor usar métodos separados para int e long. Eu editei a resposta de acordo.
jcsahnwaldt Reintegrar Monica

1
Isso não apenas não responde à pergunta (onde é gcd para int ou long em Java), mas a implementação proposta é bastante ineficiente. Esta não deve ser a resposta aceita. Pelo que eu sei, o Java runtime não tem, mas existe em bibliotecas de terceiros.
Florian F

134

Pelo que eu sei, não existe nenhum método integrado para primitivos. Mas algo tão simples como isso deve resolver o problema:

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

Você também pode usar uma linha se gostar desse tipo de coisa:

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

Deve-se observar que não há absolutamente nenhuma diferença entre os dois, pois eles são compilados para o mesmo código de byte.


Pelo que eu posso dizer, funciona bem. Acabei de executar 100.000 números aleatórios por meio de ambos os métodos e eles concordaram todas as vezes.
Tony Ennis

19
É um algoritmo euclidiano ... É muito antigo e comprovadamente correto. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Sim, eu posso ver, mas preciso de mais tempo para trabalhar nisso. Eu gosto disso.
Tony Ennis

1
@Albert, bem, você pode sempre experimentar com um tipo genérico e ver se funciona. Não sei, apenas uma ideia, mas o algoritmo está lá para você experimentar. No que diz respeito a alguma biblioteca ou classe padrão, nunca vi uma. Você ainda precisará especificar quando criar o objeto que é um int, longo, etc.
Matt

1
@Albert, bem, embora Matt tenha fornecido uma implementação, você mesmo poderia fazê-la funcionar de uma forma, como você diz, "mais genérica", não? :)
Bart Kiers,

33

Ou o algoritmo euclidiano para calcular o 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
Só para esclarecer: isso não é absolutamente o que eu estava pedindo.
Albert,

11
Nesse caso, você não especificou que não deseja implementações alternativas, pois não existe uma. Só mais tarde você editou sua postagem sem procurar implementações. Acredito que outros responderam "não" mais do que adequadamente.
Xorlev

2
Isso seria lento se a fosse muito grande e b fosse pequeno. As soluções '%' seriam muito mais rápidas.
Bruce Feist,

12

2
Curiosamente, o Guava não usa o método do "módulo" euclidiano, mas o algoritmo binário GCD que afirma ser 40% mais rápido. É seguro dizer que é bastante eficiente e bem testado.
Florian F

12

A menos que eu tenha goiaba, eu defino assim:

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


7

Você pode usar esta implementação do 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


É uma variação do algoritmo de Stein que explora que, na maioria das máquinas, a mudança é uma operação relativamente barata. É um algoritmo padrão.
Bastian J

6

Algumas implementações aqui não funcionam corretamente se ambos os números forem negativos. gcd (-12, -18) é 6, não -6.

Portanto, um valor absoluto deve ser retornado, algo como

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

Um caso extremo para isso é se ambos ae bforem Integer.MIN_VALUE, você receberá Integer.MIN_VALUEo resultado, que é negativo. Isso pode ser aceitável. O problema é que gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, mas 2 ^ 31 não pode ser expresso como um número inteiro.
Michael Anderson

Eu também recomendo usar o if(a==0 || b==0) return Math.abs(a+b);para que o comportamento seja realmente simétrico para zero argumentos.
Michael Anderson

3

podemos usar a função 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

Se você estiver usando Java 1.5 ou posterior, este é um algoritmo GCD binário iterativo que usa Integer.numberOfTrailingZeros()para reduzir o número de verificações e iterações necessárias.

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

Teste de unidade:

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

Semelhante a MutableBigInteger.binaryGcd (int, int), infelizmente o último não está acessível. Mas legal de qualquer maneira!
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 o algoritmo de Euclides para obter o "maior divisor comum" de dois inteiros. Ele recebe dois inteiros e retorna o mdc deles. tão fácil!


1

Está em outro lugar?

Apache!- tem gcd e lcm, tão legal!

No entanto, devido à profundidade de sua implementação, é mais lento em comparação com a versão simples escrita à mão (se for importante).


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

você pode explicar como isso pode ajudar?
kommradHomer

0

Usei esse método que criei quando tinha 14 anos.

    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

Essas funções GCD fornecidas por Commons-Math e Guava têm algumas diferenças.

  • Commons-Math lança um ArithematicException.classapenas para Integer.MIN_VALUEou Long.MIN_VALUE.
    • Caso contrário, trata o valor como um valor absoluto.
  • Goiaba lança um IllegalArgumentException.classpara quaisquer valores negativos.

-3

O% vai nos dar o mdc. Entre dois números, isso significa: -% ou mod de big_number / small_number são = gcd, e escrevemos em java assim big_number % small_number .

EX1: para dois inteiros

  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 três inteiros

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
Isso está errado, por exemplo, gcd(42, 30)deveria estar, 6mas é 12pelo seu exemplo. Mas 12 não é um divisor de 30 e nem de 42. Você deve chamar gcdrecursivamente. Veja a resposta de Matt ou procure na Wikipedia o algoritmo euclidiano.
Albert
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.