目錄

廣告 AD

C/C++ 語言:如何找最大公因數和最小公倍數

最大公因數和最小公倍數不常遇到

但還是紀錄一下,順便複習一下

廣告 AD

GCD 是最大公因數,為 Greatest Common Divisor 的縮寫,也稱作 Highest Common Factor (HCF),下面就展示兩種計算 GCD 的方法,其中以輾轉相除法的計算最為快速。


我們直接使用窮舉的方法,從兩數中最小的數字開始檢查是否為兩數的公因數,如果不是答案的話,就將數字減一繼續檢查,到最後時都找不到的話,就直接回傳 1,因為 1 是任何整數的因數。

C

int gcd(int a, int b) {
  int start = (a > b ? b : a);
  for (int i = start; i > 1; --i) {
    if ((a % i == 0) && (b % i == 0)) return i;
  }
  return 1;
}

輾轉相除法又稱歐幾里得算法 (Euclidean algorithm),是快速計算最大公因數的算法,詳細證明可以去 Wiki 觀看。

在計算過程中: 0. 假設我們有兩個數字 A 和 B,其中 A 較大。

  1. 我們會將較大的數 A 除以較小的數 B,並得到餘數 C。
  2. 之後我們用 B 取代 A,用 C 取代 B。
  3. 回到步驟 1,持續到 B 數值為 0,則答案為 A。

C

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

如果今天數字超過兩個,要計算共同的 GCD 的話,我們可以採用以下性質:

$$ GCD(a,b,c) = GCD(GCD(a,b),c) $$

範例中的 gcd 上方介紹的 gcd。

C

#include <stdarg.h>

int GCD(int total, ...) {
  va_list args;
  va_start(args, total);
  int val = va_arg(args, int);
  for (int i = 1; i < total; i++) {
    int number = va_arg(args, int);
    val = gcd(val, number);
  }
  va_end(args);
  return val;
}

使用前要先傳入總共有幾個數字,下面的範例為傳入 3 個數字,分別為 12 18 63。

C

printf("GCD of 12, 18, 63 = %d\n", GCD(3, 12, 18, 63));

LCM 為最小公倍數,全名為 Least Common Multiple,一樣提供兩種方法。


與 GCD 的列舉法類似,從兩數中最大的數字開始檢查是否為兩數的公倍數,如果不是答案的話,就將數字加一繼續檢查。

C

int lcm(int a, int b) {
  int start = (a > b ? a : b);
  for (int i = start;; ++i) {
    if ((i % a == 0) && (i % b == 0)) return i;
  }
}

由於 LCM 和 GCD 有著以下的關係,因此我們可以透過 GCD 來計算 LCM,由於 LCM 一定為正整數,因此我們要加上絕對值,範例中的 gcd 為前段部分介紹的 gcd 的函式。

$$LCM(a,b) = \frac{|a \times b|}{GCD(a, b)}$$

C

#include <stdlib.h>

int lcm(int a, int b) {
  return abs(a * b) / gcd(a, b);
}
Overflow
要注意 a * b 的時候會不會 Overflow,並在必要時換成更大的 long long 或是使用無限精度的 GMP Library。Overflow 檢查方法和 GMP Library 可以參考下方連結。
C/C++ 語言中的數值運算陷阱:Integer Overflow and Underflow

C/C++ 語言中的數值運算陷阱:Integer Overflow and Underflow

有時候在做簡單加減乘除的時候,計算結果超出由於當前型態的範圍,而導致計算結果錯誤的問題,這邊簡單紀錄了幾個如何偵測這種問題的方法

閱讀全文
GMP Library: C/C++ 任意精度的整數和浮點數

GMP Library: C/C++ 任意精度的整數和浮點數

在算排列的時候發現 64 位元的整數不夠用,於是換成了 128 位元的整數,但發現還是不夠用…

閱讀全文

如果今天數字超過兩個,要計算共同的 LCM 的話,我們可以採用以下性質:

$$ LCM(a,b,c) = LCM(LCM(a,b),c) $$

也就是說,先計算其中兩者的 LCM,再計算其結果和最後一者的 LCM,證明有興趣的可以到 Wiki 查看。

C

#include <stdarg.h>

int LCM(int total, ...) {
  va_list args;
  va_start(args, total);
  int val = 1;
  for (int i = 0; i < total; i++) {
    int number = va_arg(args, int);
    val = val * number / gcd(val, number);
  }
  va_end(args);
  return val;
}

一樣要記得確認是否有 Overflow。

C

printf("LCM of 7, 14, 10 = %d\n", LCM(3, 7, 14, 10));

廣告 AD