Java: ottieni il massimo comune divisore


91

Ho visto che una tale funzione esiste per BigInteger, ie BigInteger#gcd. Esistono altre funzioni in Java che funzionano anche per altri tipi ( int, longo Integer)? Sembra che questo avrebbe senso come java.lang.Math.gcd(con tutti i tipi di sovraccarichi) ma non è lì. È da qualche altra parte?


(Non confondere questa domanda con "come posso implementarlo da solo", per favore!)


7
Perché la risposta accettata è quella che ti dice come implementarla da solo, sebbene includa un'implementazione esistente? =)
djjeck

Condivido la tua osservazione. GCD dovrebbe una classe con un mucchio di metodi statici sovraccarichi che accetta due numeri e dà il suo gcd. E dovrebbe far parte del pacchetto java.math.
anu

Risposte:


79

Per int e long, come primitivi, non proprio. Per Integer, è possibile che qualcuno ne abbia scritto uno.

Dato che BigInteger è un superset (matematico / funzionale) di int, Integer, long e Long, se è necessario utilizzare questi tipi, convertirli in BigInteger, eseguire il GCD e riconvertire il risultato.

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()è molto meglio.
Albert


4
Se questa funzione viene chiamata spesso (cioè milioni di volte) non dovresti convertire int o long in BigInteger. Una funzione che utilizza solo valori primitivi sarà probabilmente un ordine di grandezza più veloce. Controlla le altre risposte.
jcsahnwaldt Ripristina Monica il

@Bhanu Pratap Singh Per evitare il casting o il troncamento, è meglio usare metodi separati per int e long. Ho modificato la risposta di conseguenza.
jcsahnwaldt Ripristina Monica il

1
Questo non solo non risponde alla domanda (dove è gcd per int o long in Java) ma l'implementazione proposta è piuttosto inefficiente. Questa non dovrebbe essere la risposta accettata. Per quanto ne so, il runtime Java non ce l'ha, ma esiste in librerie di terze parti.
Florian F

134

Per quanto ne so, non esiste alcun metodo integrato per le primitive. Ma qualcosa di semplice come questo dovrebbe fare il trucco:

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

Puoi anche scrivere una riga se ti piace questo genere di cose:

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

Va notato che non c'è assolutamente alcuna differenza tra i due poiché vengono compilati con lo stesso codice byte.


Per quanto ne so, funziona bene. Ho appena eseguito 100.000 numeri casuali con entrambi i metodi e si sono accordati ogni volta.
Tony Ennis

19
È un algoritmo euclideo ... È molto vecchio e ha ragione. en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

Sì, posso vederlo ma ho bisogno di più tempo per lavorarci sopra. Mi piace.
Tony Ennis

1
@ Albert, beh, potresti sempre provarlo con un tipo generico e vedere se funziona. Non so solo un pensiero, ma l'algoritmo è lì per te con cui sperimentare. Per quanto riguarda qualche libreria o classe standard, non ne ho mai vista una. Dovrai comunque specificare quando crei l'oggetto che è un int, long, ecc. Però.
Matt

1
@ Albert, beh, sebbene Matt abbia fornito un'implementazione, tu stesso potresti farla funzionare in un modo, come hai detto, "più generico", no? :)
Bart Kiers

33

Oppure l'algoritmo euclideo per il calcolo del 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
Giusto per chiarire: questo non è assolutamente quello che stavo chiedendo.
Albert

11
In questo caso, non avevi specificato di non volere implementazioni alternative poiché una non esisteva. Solo in seguito hai modificato il tuo post senza cercare implementazioni. Credo che altri abbiano risposto "no" più che adeguatamente.
Xorlev

2
Sarebbe lento se a è molto grande eb è piccolo. Le soluzioni "%" sarebbero molto più veloci.
Bruce Feist

12

2
È interessante notare che Guava non utilizza il metodo "modulo" euclideo ma l'algoritmo binario GCD che affermano di essere il 40% più veloce. È sicuro dire che è abbastanza efficiente e ben testato.
Florian F

12

A meno che non abbia Guava, definisco così:

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


7

È possibile utilizzare questa implementazione dell'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));
}

}

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


È una variazione dell'algoritmo di Stein che sfrutta il fatto che sulla maggior parte delle macchine il cambio di marcia è un'operazione relativamente economica. È un algoritmo standard.
Bastian J

6

Alcune implementazioni qui non funzionano correttamente se entrambi i numeri sono negativi. mcd (-12, -18) è 6, non -6.

Quindi dovrebbe essere restituito un valore assoluto, qualcosa di simile

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

Un caso limite per questo è che se entrambi ae lo bsono Integer.MIN_VALUE, Integer.MIN_VALUEtornerai come risultato, il che è negativo. Questo può essere accettabile. Il problema è che mcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, ma 2 ^ 31 non può essere espresso come numero intero.
Michael Anderson

Consiglierei anche di usarlo in if(a==0 || b==0) return Math.abs(a+b);modo che il comportamento sia veramente simmetrico per zero argomenti.
Michael Anderson

3

possiamo usare la funzione ricorsiva per trovare 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 stai utilizzando Java 1.5 o versioni successive, questo è un algoritmo GCD binario iterativo che utilizza Integer.numberOfTrailingZeros()per ridurre il numero di controlli e iterazioni richiesti.

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 unitario:

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

Simile a MutableBigInteger.binaryGcd (int, int), purtroppo quest'ultimo non è accessibile. Ma fico comunque!
Mostowski Crollo

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

Questo metodo utilizza l'algoritmo di Euclide per ottenere il "Massimo comune divisore" di due numeri interi. Riceve due numeri interi e ne restituisce il mcd. proprio così facile!


1

È da qualche altra parte?

Apache!- ha sia mcd che mcm, fantastico!

Tuttavia, a causa della profondità della loro implementazione, è più lento rispetto alla semplice versione scritta a mano (se è 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
  }
}

puoi elaborare con una spiegazione su come questo potrebbe aiutare?
kommradHomer

0

Ho usato questo metodo che ho creato quando avevo 14 anni.

    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

Le funzioni di GCD fornite da Commons-Math e Guava presentano alcune differenze.

  • Commons-Math genera un ArithematicException.classsolo per Integer.MIN_VALUEo Long.MIN_VALUE.
    • In caso contrario, gestisce il valore come un valore assoluto.
  • Guava genera un IllegalArgumentException.classper qualsiasi valore negativo.

-3

La% ci darà il gcd Tra due numeri, significa: -% o mod di big_number / small_number are = gcd, e lo scriviamo su java in questo modo big_number % small_number .

EX1: per due numeri interi

  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: per tre numeri interi

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
Questo è sbagliato, ad esempio gcd(42, 30)dovrebbe esserlo, 6ma è in 12base al tuo esempio. Ma 12 non è un divisore di 30 e nessuno di 42. Dovresti chiamare gcdricorsivamente. Vedi la risposta di Matt o cerca su Wikipedia l'algoritmo euclideo.
Albert
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.