Mencari Ganda Paling Sedikit di Jawa

1. Gambaran keseluruhan

Berbilang Common Least (LCM) dua bukan sifar integer (a, b) adalah integer terkecil positif yang sempurna dibahagikan dengan kedua-dua a dan b .

Dalam tutorial ini, kita akan belajar mengenai pendekatan yang berbeza untuk mencari LCM dua atau lebih nombor. Kita mesti perhatikan bahawa bilangan bulat negatif dan sifar bukan calon LCM .

2. Mengira LCM Dua Nombor Menggunakan Algoritma Mudah

Kita dapat mencari LCM dua nombor dengan menggunakan fakta mudah bahawa pendaraban adalah penambahan berulang .

2.1. Algoritma

Algoritma mudah untuk mencari LCM adalah pendekatan berulang yang menggunakan beberapa sifat asas LCM dua nombor.

Pertama, kita tahu bahawa LCM sebarang nombor dengan sifar adalah sifar itu sendiri. Jadi, kita dapat membuat keluar awal dari prosedur apabila salah satu bilangan bulat yang diberikan adalah 0.

Kedua, kita juga dapat memanfaatkan fakta bahawa batas bawah LCM dua bilangan bulat bukan sifar adalah lebih besar daripada nilai mutlak kedua nombor tersebut .

Lebih-lebih lagi, seperti yang dijelaskan sebelumnya, LCM tidak boleh menjadi bilangan bulat negatif. Jadi, kita hanya akan menggunakan nilai mutlak bagi bilangan bulat untuk mencari gandaan yang mungkin sehingga kita menemui gandaan yang sama.

Mari lihat prosedur tepat yang perlu kita ikuti untuk menentukan lcm (a, b):

  1. Sekiranya a = 0 atau b = 0, kemudian kembali dengan lcm (a, b) = 0, jika tidak, lanjutkan ke langkah 2.
  2. Hitung nilai mutlak bagi dua nombor tersebut.
  3. Permulaan lcm sebagai yang lebih tinggi dari dua nilai yang dihitung dalam langkah 2.
  4. Sekiranya lcm dibahagi dengan nilai mutlak yang lebih rendah, maka kembalikan.
  5. Peningkatan lcm dengan nilai mutlak yang lebih tinggi di antara keduanya dan pergi ke langkah 4.

Sebelum kita memulakan dengan pelaksanaan pendekatan mudah ini, mari kita lakukan jalan kering untuk mencari lcm (12, 18).

Oleh kerana kedua-duanya 12 dan 18 positif, mari kita melangkah ke langkah 3, memulakan lcm = max (12, 18) = 18, dan teruskan lebih jauh.

Dalam lelaran pertama kami, lcm = 18, yang tidak dapat dibahagikan dengan sempurna oleh 12. Oleh itu, kami menambahnya dengan 18 dan teruskan.

Pada lelaran kedua, kita dapat melihat bahawa lcm = 36 dan kini dapat dibahagikan dengan sempurna oleh 12. Oleh itu, kita dapat kembali dari algoritma dan menyimpulkan bahawa lcm (12, 18) adalah 36.

2.2. Pelaksanaan

Mari kita laksanakan algoritma di Java. Kami lcm () keperluan kaedah untuk menerima dua hujah integer dan memberi LCM mereka sebagai nilai pulangan.

Kita dapat melihat bahawa algoritma di atas melibatkan melakukan beberapa operasi matematik pada nombor seperti mencari nilai mutlak, minimum dan maksimum. Untuk tujuan ini, kita dapat menggunakan kaedah statik yang sesuai untuk kelas Matematik seperti abs () , min (), dan maks () , masing-masing.

Mari kita laksanakan kaedah lcm () kami :

public static int lcm(int number1, int number2) { if (number1 == 0 || number2 == 0) { return 0; } int absNumber1 = Math.abs(number1); int absNumber2 = Math.abs(number2); int absHigherNumber = Math.max(absNumber1, absNumber2); int absLowerNumber = Math.min(absNumber1, absNumber2); int lcm = absHigherNumber; while (lcm % absLowerNumber != 0) { lcm += absHigherNumber; } return lcm; }

Seterusnya, mari kita mengesahkan kaedah ini:

@Test public void testLCM() { Assert.assertEquals(36, lcm(12, 18)); }

Kes ujian di atas mengesahkan kebenaran kaedah lcm () dengan menegaskan bahawa lcm (12, 18) adalah 36.

3. Menggunakan Pendekatan Pemfaktoran Perdana

Teori asas aritmetik menyatakan bahawa adalah mungkin untuk menyatakan secara unik setiap bilangan bulat yang lebih besar daripada satu sebagai produk kekuatan nombor perdana.

Jadi, untuk sebarang bilangan bulat N> 1, kita mempunyai N = (2k1) * (3k2) * (5k3) *…

Dengan menggunakan hasil teorema ini, kita sekarang akan memahami pendekatan pemfaktoran utama untuk mencari LCM dua nombor.

3.1. Algoritma

Pendekatan pemfaktoran perdana mengira LCM dari penguraian perdana bagi dua nombor tersebut. Kita boleh menggunakan faktor utama dan eksponen dari faktorisasi utama untuk mengira LCM dari dua nombor:

Bilakah, | a | = (2p1) * (3p2) * (5p3) *…

dan | b | = (2q1) * (3q2) * (5q3) *…

maka, lcm (a, b) = (2max (p 1 , q 1 )) * (3max (p 2 , q 2 )) * (5max (p 3 , q 3 )) ...

Mari lihat bagaimana mengira LCM 12 dan 18 menggunakan pendekatan ini:

Pertama, kita perlu mewakili nilai mutlak kedua nombor itu sebagai produk faktor utama:

12 = 2 * 2 * 3 = 2² * 3¹

18 = 2 * 3 * 3 = 2¹ * 3²

Kita dapat perhatikan di sini bahawa faktor utama dalam perwakilan di atas adalah 2 dan 3.

Seterusnya, mari kita tentukan eksponen bagi setiap faktor utama bagi LCM. Kami melakukan ini dengan mengambil kekuatannya yang lebih tinggi dari dua perwakilan.

Dengan menggunakan strategi ini, kekuatan 2 di LCM akan menjadi maksimum (2, 1) = 2, dan kekuatan 3 di LCM akan menjadi maksimum (1, 2) = 2.

Akhirnya, kita dapat mengira LCM dengan mengalikan faktor utama dengan daya yang sepadan yang diperoleh pada langkah sebelumnya. Oleh itu, kita mempunyai lcm (12, 18) = 2² * 3² = 36.

3.2. Pelaksanaan

Pelaksanaan Java kami menggunakan perwakilan faktorisasi utama dari dua nombor untuk mencari LCM.

Untuk tujuan ini, kaedah getPrimeFactors () kami perlu menerima hujah integer dan memberikan perwakilan faktorisasi utama kepada kami. Di Java, kita dapat mewakili pemfaktoran utama nombor menggunakan HashMap di mana setiap kunci menunjukkan faktor prima dan nilai yang terkait dengan kunci menandakan eksponen faktor yang sesuai.

Mari lihat pelaksanaan berulang kaedah getPrimeFactors () :

public static Map getPrimeFactors(int number) { int absNumber = Math.abs(number); Map primeFactorsMap = new HashMap(); for (int factor = 2; factor <= absNumber; factor++) { while (absNumber % factor == 0) { Integer power = primeFactorsMap.get(factor); if (power == null) { power = 0; } primeFactorsMap.put(factor, power + 1); absNumber /= factor; } } return primeFactorsMap; }

We know that the prime factorization maps of 12 and 18 are {2 → 2, 3 → 1} and {2 → 1, 3 → 2} respectively. Let's use this to test the above method:

@Test public void testGetPrimeFactors() { Map expectedPrimeFactorsMapForTwelve = new HashMap(); expectedPrimeFactorsMapForTwelve.put(2, 2); expectedPrimeFactorsMapForTwelve.put(3, 1); Assert.assertEquals(expectedPrimeFactorsMapForTwelve, PrimeFactorizationAlgorithm.getPrimeFactors(12)); Map expectedPrimeFactorsMapForEighteen = new HashMap(); expectedPrimeFactorsMapForEighteen.put(2, 1); expectedPrimeFactorsMapForEighteen.put(3, 2); Assert.assertEquals(expectedPrimeFactorsMapForEighteen, PrimeFactorizationAlgorithm.getPrimeFactors(18)); }

Our lcm() method first uses the getPrimeFactors() method to find prime factorization map for each number. Next, it uses the prime factorization map of both the numbers to find their LCM. Let's see an iterative implementation of this method:

public static int lcm(int number1, int number2) { if(number1 == 0 || number2 == 0) { return 0; } Map primeFactorsForNum1 = getPrimeFactors(number1); Map primeFactorsForNum2 = getPrimeFactors(number2); Set primeFactorsUnionSet = new HashSet(primeFactorsForNum1.keySet()); primeFactorsUnionSet.addAll(primeFactorsForNum2.keySet()); int lcm = 1; for (Integer primeFactor : primeFactorsUnionSet) { lcm *= Math.pow(primeFactor, Math.max(primeFactorsForNum1.getOrDefault(primeFactor, 0), primeFactorsForNum2.getOrDefault(primeFactor, 0))); } return lcm; }

As a good practice, we shall now verify the logical correctness of the lcm() method:

@Test public void testLCM() { Assert.assertEquals(36, PrimeFactorizationAlgorithm.lcm(12, 18)); }

4. Using the Euclidean Algorithm

There's an interesting relation between the LCM and GCD (Greatest Common Divisor) of two numbers that says that the absolute value of the product of two numbers is equal to the product of their GCD and LCM.

As stated, gcd(a, b) * lcm(a, b) = |a * b|.

Consequently, lcm(a, b) = |a * b|/gcd(a, b).

Using this formula, our original problem of finding lcm(a,b) has now been reduced to just finding gcd(a,b).

Granted, there are multiple strategies to finding GCD of two numbers. However, the Euclidean algorithm is known to be one of the most efficient of all.

For this reason, let's briefly understand the crux of this algorithm, which can be summed up in two relations:

  • gcd (a, b) = gcd(|a%b|, |a| ); where |a| >= |b|
  • gcd(p, 0) = gcd(0, p) = |p|

Let's see how we can find lcm(12, 18) using the above relations:

We have gcd(12, 18) = gcd(18%12, 12) = gcd(6,12) = gcd(12%6, 6) = gcd(0, 6) = 6

Therefore, lcm(12, 18) = |12 x 18| / gcd(12, 18) = (12 x 18) / 6 = 36

We'll now see a recursive implementation of the Euclidean algorithm:

public static int gcd(int number1, int number2) { if (number1 == 0 || number2 == 0) { return number1 + number2; } else { int absNumber1 = Math.abs(number1); int absNumber2 = Math.abs(number2); int biggerValue = Math.max(absNumber1, absNumber2); int smallerValue = Math.min(absNumber1, absNumber2); return gcd(biggerValue % smallerValue, smallerValue); } }

The above implementation uses the absolute values of numbers — since GCD is the largest positive integer that perfectly divides the two numbers, we're not interested in negative divisors.

We're now ready to verify if the above implementation works as expected:

@Test public void testGCD() { Assert.assertEquals(6, EuclideanAlgorithm.gcd(12, 18)); }

4.1. LCM of Two Numbers

Using the earlier method to find GCD, we can now easily calculate LCM. Again, our lcm() method needs to accept two integers as input to return their LCM. Let's see how we can implement this method in Java:

public static int lcm(int number1, int number2) { if (number1 == 0 || number2 == 0) return 0; else { int gcd = gcd(number1, number2); return Math.abs(number1 * number2) / gcd; } }

We can now verify the functionality of the above method:

@Test public void testLCM() { Assert.assertEquals(36, EuclideanAlgorithm.lcm(12, 18)); }

4.2. LCM of Large Numbers Using the BigInteger Class

To calculate the LCM of large numbers, we can leverage the BigInteger class.

Internally, the gcd() method of the BigInteger class uses a hybrid algorithm to optimize computation performance. Moreover, since the BigInteger objects are immutable, the implementation leverages mutable instances of the MutableBigInteger class to avoid frequent memory reallocations.

To begin with, it uses the conventional Euclidean algorithm to repeatedly replace the higher integer by its modulus with the lower integer.

As a result, the pair not only gets smaller and smaller but also closer to each other after successive divisions. Eventually, the difference in the number of ints required to hold the magnitude of the two MutableBigInteger objects in their respective int[] value arrays reaches either 1 or 0.

At this stage, the strategy is switched to the Binary GCD algorithm to get even faster computation results.

In this case, as well, we'll compute LCM by dividing the absolute value of the product of the numbers by their GCD. Similar to our prior examples, our lcm() method takes two BigInteger values as input and returns the LCM for the two numbers as a BigInteger. Let's see it in action:

public static BigInteger lcm(BigInteger number1, BigInteger number2) { BigInteger gcd = number1.gcd(number2); BigInteger absProduct = number1.multiply(number2).abs(); return absProduct.divide(gcd); }

Finally, we can verify this with a test case:

@Test public void testLCM() { BigInteger number1 = new BigInteger("12"); BigInteger number2 = new BigInteger("18"); BigInteger expectedLCM = new BigInteger("36"); Assert.assertEquals(expectedLCM, BigIntegerLCM.lcm(number1, number2)); }

5. Conclusion

In this tutorial, we discussed various methods to find the least common multiple of two numbers in Java.

Moreover, we also learned about the relation between the product of numbers with their LCM and GCD. Given algorithms that can compute the GCD of two numbers efficiently, we've also reduced the problem of LCM calculation to one of GCD computation.

Seperti biasa, kod sumber lengkap untuk implementasi Java yang digunakan dalam artikel ini tersedia di GitHub.