Java: รับตัวหารร่วมที่ยิ่งใหญ่ที่สุด


91

ผมได้เห็นว่าฟังก์ชั่นดังกล่าวอยู่สำหรับเช่นBigInteger BigInteger#gcdมีฟังก์ชั่นอื่น ๆ ในเกาะชวาซึ่งยังทำงานประเภทอื่น ๆ ( int, longหรือInteger)? ดูเหมือนว่าจะสมเหตุสมผลjava.lang.Math.gcd(กับการโอเวอร์โหลดทุกประเภท) แต่มันไม่ได้อยู่ที่นั่น ที่อื่นหรือเปล่า


(อย่าสับสนกับคำถามนี้กับ "ฉันจะใช้สิ่งนี้ด้วยตัวเองได้อย่างไร"!)


7
เหตุใดคำตอบที่ได้รับการยอมรับจึงบอกคุณถึงวิธีการใช้งานด้วยตัวเองแม้ว่าจะรวมการใช้งานที่มีอยู่ =)
djjeck

ฉันเห็นด้วยกับการสังเกตของคุณ GCD ควรเป็นคลาสที่มีเมธอดคงที่มากเกินไปซึ่งใช้ตัวเลขสองตัวและให้เป็น gcd และควรเป็นส่วนหนึ่งของแพ็คเกจ java.math
anu

คำตอบ:


81

สำหรับ int และ long เป็นแบบดั้งเดิมไม่จริง สำหรับจำนวนเต็มอาจมีคนเขียนขึ้นมา

ระบุว่า 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();
}

65
BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).intValue()จะดีกว่ามาก
Albert

1
เกณฑ์มาตรฐานบางส่วน: stackoverflow.com/questions/21570890/…
jcsahnwaldt Reinstate Monica

5
หากมีการเรียกใช้ฟังก์ชันนี้บ่อยครั้ง (เช่นหลายล้านครั้ง) คุณไม่ควรแปลง int หรือ long เป็น BigInteger ฟังก์ชันที่ใช้เฉพาะค่าดั้งเดิมจะมีลำดับความสำคัญเร็วกว่า ตรวจสอบคำตอบอื่น ๆ
jcsahnwaldt Reinstate Monica

@Bhanu Pratap Singh เพื่อหลีกเลี่ยงการแคสต์หรือการตัดทอนควรใช้วิธีแยก int และ long ฉันแก้ไขคำตอบตามนั้น
jcsahnwaldt Reinstate Monica

1
สิ่งนี้ไม่เพียง แต่ไม่ตอบคำถาม (gcd สำหรับ int หรือ long ใน Java อยู่ที่ไหน) แต่การนำเสนอไปใช้นั้นค่อนข้างไม่มีประสิทธิภาพ นี่ไม่ควรเป็นคำตอบที่ยอมรับได้ เท่าที่ฉันรู้ว่ารันไทม์ Java ไม่มี แต่มีอยู่ในไลบรารีของบุคคลที่สาม
Florian F

136

เท่าที่ฉันรู้ไม่มีวิธีการในตัวสำหรับดั้งเดิม แต่สิ่งที่ง่ายเช่นนี้ควรใช้เคล็ดลับ:

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 หมายเลขโดยใช้ทั้งสองวิธีและพวกเขาตกลงกันทุกครั้ง
Tony Ennis

20
มันเป็นอัลกอริทึมแบบยุคลิด ... มันเก่ามากและได้รับการพิสูจน์แล้วว่าถูกต้อง en.wikipedia.org/wiki/Euclidean_algorithm
Rekin

ใช่ฉันสามารถมองเห็นได้ แต่ฉันต้องการเวลามากกว่านี้เพื่อทำงานผ่านมัน ฉันชอบมัน.
Tony Ennis

1
@ อัลเบิร์ตคุณสามารถลองใช้กับประเภททั่วไปได้ตลอดเวลาและดูว่าใช้งานได้หรือไม่ ฉันไม่รู้แค่ความคิด แต่มีอัลกอริทึมให้คุณทดลองใช้ เท่าที่ห้องสมุดมาตรฐานหรือชั้นเรียนบางแห่งฉันไม่เคยเห็นเลย คุณยังคงต้องระบุเมื่อคุณสร้างออบเจ็กต์ว่าเป็น int, long ฯลฯ แม้ว่า
แมตต์

1
@ อัลเบิร์ตแม้ว่า Matt จะให้การใช้งานคุณเองก็สามารถทำให้มันใช้งานได้อย่างที่คุณวางไว้คือวิธีที่ "ทั่วไปกว่า" ไม่ใช่เหรอ? :)
Bart Kiers

34

หรืออัลกอริทึมแบบยุคลิดในการคำนวณ 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
เพียงเพื่อชี้แจง: นี่ไม่ใช่สิ่งที่ฉันขออย่างแน่นอน
Albert

12
ในกรณีนี้คุณไม่ได้ระบุว่าคุณไม่ต้องการใช้งานทางเลือกอื่นเนื่องจากไม่มีอยู่ หลังจากนั้นคุณได้แก้ไขโพสต์ของคุณโดยไม่ได้มองหาการนำไปใช้งาน ฉันเชื่อว่าคนอื่นตอบว่า "ไม่" มากกว่าอย่างเพียงพอ
Xorlev

2
สิ่งนี้จะช้าถ้า a มีขนาดใหญ่มากและ b มีขนาดเล็ก การแก้ปัญหา '%' จะเร็วกว่ามาก
Bruce Feist

12

ใช้ฝรั่งLongMath.gcd()และIntMath.gcd()


2
ที่น่าสนใจคือฝรั่งไม่ได้ใช้วิธี "โมดูโล" แบบยุคลิด แต่อัลกอริทึม GCD ไบนารีที่พวกเขาอ้างว่าเร็วกว่า 40% ปลอดภัยที่จะกล่าวได้ว่ามีประสิทธิภาพและผ่านการทดสอบมาเป็นอย่างดี
Florian F

12

ถ้าฉันไม่มีฝรั่งฉันจะกำหนดแบบนี้:

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


7

คุณสามารถใช้อัลกอริทึม 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));
}

}

จากhttp://introcs.cs.princeton.edu/java/23recursion/BinaryGCD.java.html


เป็นรูปแบบหนึ่งของอัลกอริทึมของ Stein ซึ่งใช้ประโยชน์จากเครื่องจักรส่วนใหญ่การเปลี่ยนเกียร์เป็นการดำเนินการที่ค่อนข้างถูก มันเป็นอัลกอริทึมมาตรฐาน
Bastian J

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) น่าเสียดายที่หลังไม่สามารถเข้าถึงได้ แต่ยังไงก็เจ๋ง!
Mostowski ยุบ

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

ที่อื่นหรือเปล่า

อาปาเช่! - มีทั้ง 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_VALUELong.MIN_VALUE
    • มิฉะนั้นจะจัดการกับค่านี้เป็นค่าสัมบูรณ์
  • ฝรั่งโยน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ซ้ำ ดูคำตอบของ Matt หรือดูใน Wikipedia สำหรับอัลกอริทึมแบบยุคลิด
Albert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.