Java: отримати найбільший спільний дільник


91

Я бачив, що така функція існує для BigInteger, тобто BigInteger#gcd. Чи існують інші функції в Java, які також працюють для інших типів ( int, longабо Integer)? Здається, це мало б сенс java.lang.Math.gcd( як і при всіх видах перевантажень), але цього немає. Це десь ще?


(Не плутайте це питання з "як я це сам реалізую", будь ласка!)


7
Чому прийнятою відповіддю є та, яка говорить про те, як це реалізувати самостійно - хоча обгортання існуючої реалізації? =)
djjeck

Я згоден з вашим спостереженням. GCD повинен мати клас із купою перевантажених статичних методів, який приймає два числа і дає йому gcd. І це повинно бути частиною пакету java.math.
ану

Відповіді:


79

Для int і long, як примітивів, не дуже. Для Integer цілком можливо, що хтось написав його.

Враховуючи, що BigInteger є (математичним / функціональним) набором int, Integer, long і Long, якщо вам потрібно використовувати ці типи, перетворіть їх у BigInteger, зробіть GCD і перетворіть результат назад.

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()набагато краще.
Альберт,


4
Якщо цю функцію часто викликають (тобто мільйони разів), ви не повинні перетворювати int або long у BigInteger. Функція, яка використовує лише примітивні значення, швидше за все, буде на порядок швидше. Перевірте інші відповіді.
jcsahnwaldt Reinstate Monica

@Bhanu Pratap Singh Щоб уникнути кастингу чи усічення, краще використовувати окремі методи int та long. Відповідь я відредагував.
jcsahnwaldt Reinstate Monica

1
Це не тільки не відповідає на запитання (де gcd для int або long у Java), але запропонована реалізація є досить неефективною. Це не повинно бути прийнятою відповіддю. Наскільки я знаю, середовище виконання Java його не має, але воно існує у сторонніх бібліотеках.
Флоріан F

134

Наскільки я знаю, не існує жодного вбудованого методу для примітивів. Але щось таке просте, як це, повинно зробити трюк:

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

Ви також можете зробити це в один рядок, якщо вам подобається таке:

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

Слід зазначити, що між ними немає абсолютно ніякої різниці, оскільки вони компілюються в один і той же байт-код.


Наскільки я можу сказати, це працює нормально. Я просто провів 100 000 випадкових чисел, хоча обидва методи, і вони кожного разу погоджувались.
Тоні Енніс,

19
Це алгоритм Евкліда ... Він дуже старий і перевірений правильно. en.wikipedia.org/wiki/Euclidean_algorithm
Рекін

Так, я начебто це бачу, але мені потрібно більше часу, щоб це пропрацювати. Мені це подобається.
Тоні Енніс,

1
@Albert, ну ти завжди можеш спробувати його із загальним типом і перевірити, чи він працює. Я не знаю лише думки, але алгоритм є для вас, щоб експериментувати. Що стосується якоїсь стандартної бібліотеки чи класу, я їх ніколи не бачив. Вам все одно потрібно буде вказати, коли ви створюєте об'єкт, що це int, long тощо.
Matt

1
@Albert, ну, хоч Метт запропонував реалізацію, ти сам міг би змусити це працювати так, як ти кажеш, "більш загальним" способом, ні? :)
Барт Кірс,

33

Або алгоритм Евкліда для обчислення 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
Тільки для уточнення: Це абсолютно не те, про що я просив.
Альберт,

11
У цьому випадку ви не вказали, що не хочете альтернативних реалізацій, оскільки такої не існує. Лише згодом ви відредагували свою публікацію, не шукаючи реалізації. Я вважаю, що інші відповіли "ні" більш ніж адекватно.
Xorlev

2
Це було б повільно, якщо а дуже велике, а b мало. Рішення "%" було б набагато швидшим.
Bruce Feist

12

Використовуйте гуаву LongMath.gcd()таIntMath.gcd()


2
Цікаво, що Гуава використовує не метод Евкліда за модулем, а двійковий алгоритм GCD, який, на їх думку, швидший на 40%. Можна з упевненістю сказати, що він досить ефективний і добре перевірений.
Флоріан Ф

12

Якщо у мене немає гуави, я визначаю так:

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


7

Ви можете використовувати цю реалізацію двійкового алгоритму 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));
}

}

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


Це різновид алгоритму Штейна, який використовує, що на більшості машин перемикання є відносно дешевою операцією. Це стандартний алгоритм.
Бастіан Дж.

6

Деякі реалізації тут працюють некоректно, якщо обидва числа від’ємні. gcd (-12, -18) дорівнює 6, а не -6.

Тож слід повернути абсолютне значення, щось на зразок

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

Один крайній випадок для цього - якщо обидва aі bє Integer.MIN_VALUE, ви отримаєте Integer.MIN_VALUEрезультат як результат, який є негативним. Це може бути прийнятним. Проблема полягає в тому, що gcd (-2 ^ 31, -2 ^ 31) = 2 ^ 31, але 2 ^ 31 не можна виразити як ціле число.
Michael Anderson

Я також рекомендую використовувати if(a==0 || b==0) return Math.abs(a+b);так, щоб поведінка була справді симетричною для нульових аргументів.
Michael Anderson

3

ми можемо використовувати рекурсивну функцію для пошуку 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

Якщо ви використовуєте Java 1.5 або пізнішої версії, то це ітеративний двійковий алгоритм GCD, який використовує Integer.numberOfTrailingZeros()для зменшення кількості перевірок та необхідних ітерацій.

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

Одиничний тест:

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

Подібно до MutableBigInteger.binaryGcd (int, int), на жаль, останній недоступний. Але круто в будь-якому разі!
Колапс Мостовського

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

Цей метод використовує алгоритм Евкліда, щоб отримати "Найбільший спільний дільник" двох цілих чисел. Він отримує два цілих числа і повертає gcd з них. просто так просто!


1

Це десь ще?

Apache! - він має і gcd, і lcm, так класно!

Однак через глибину їх реалізації вона повільніша порівняно з простою рукописною версією (якщо це важливо).


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

Ви можете пояснити з поясненням, як це може допомогти?
kommradHomer

0

Я використав цей метод, який створив у 14 років.

    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

Ці функції GCD, надані Commons-Math та Guava, мають деякі відмінності.

  • Commons-Math кидає ArithematicException.classлише для Integer.MIN_VALUEабо Long.MIN_VALUE.
    • В іншому випадку обробляє значення як абсолютне значення.
  • Гуава кидає a IllegalArgumentException.classдля будь-яких негативних значень.

-3

% Збирається дати нам gcd Між двома числами це означає: -% або mod з big_number / small_number = gcd, і ми пишемо це на Java так big_number % small_number.

EX1: для двох цілих чисел

  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: для трьох цілих чисел

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
Це неправильно, наприклад, gcd(42, 30)має бути, 6але це 12на вашому прикладі. Але 12 - це не дільник 30 і не 42. Вам слід дзвонити gcdрекурсивно. Дивіться відповідь Метта або шукайте в Вікіпедії алгоритм Евкліда.
Альберт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.