掌握三種遞迴實作最大公約數的方法:經典歐幾里得演算法 (模數)、減法遞迴、以及二進制 GCD 演算法,包含完整程式碼、複雜度分析與優化技巧。
兩個整數 a、b (不全為 0) 的最大公約數,記為 gcd(a, b),是能同時整除 a 和 b 的最大正整數。
gcd(a, b) = gcd(b, a mod b) (b ≠ 0)
gcd(a, 0) = |a|
b = 0,回傳 |a|gcd(b, a % b)#include <stdio.h>
#include <stdlib.h> // for abs()
// 遞迴實作:歐幾里得演算法
int gcd_euclidean(int a, int b) {
// 處理負數:取絕對值
a = abs(a);
b = abs(b);
if (b == 0) {
return a; // 基礎案例
}
return gcd_euclidean(b, a % b); // 遞迴呼叫
}
// 主程式測試
int main() {
int m, n;
printf("請輸入兩個整數 (以空格分隔):");
if (scanf("%d %d", &m, &n) != 2) {
printf("輸入格式錯誤\n");
return 1;
}
int result = gcd_euclidean(m, n);
printf("gcd(%d, %d) = %d\n", m, n, result);
return 0;
}
請輸入兩個整數 (以空格分隔):48 18
gcd(48, 18) = 6
請輸入兩個整數 (以空格分隔):-24 36
gcd(-24, 36) = 12
請輸入兩個整數 (以空格分隔):17 5
gcd(17, 5) = 1
gcd(48, 18)
→ gcd(18, 48%18=12)
→ gcd(12, 18%12=6)
→ gcd(6, 12%6=0)
→ return 6
→ return 6
→ return 6
→ return 6
a = b,回傳 aa > b,回傳 gcd(a-b, b)a < b,回傳 gcd(a, b-a)#include <stdio.h>
#include <stdlib.h>
int gcd_subtraction(int a, int b) {
a = abs(a);
b = abs(b);
if (a == b) {
return a; // 基礎案例:相等時即為 GCD
} else if (a > b) {
return gcd_subtraction(a - b, b); // 較大減較小
} else {
return gcd_subtraction(a, b - a); // 較大減較小
}
}
| 輸入 | 歐幾里得 (模數) 遞迴深度 | 減法遞迴 遞迴深度 |
|---|---|---|
| gcd(48, 18) | 3 | 5 |
| gcd(1000000, 1) | 2 | 999999 |
| gcd(1000001, 1000000) | 2 | 1000000 |
int gcd_binary(int a, int b) {
a = abs(a);
b = abs(b);
if (a == 0) return b;
if (b == 0) return a;
if (a == b) return a;
// 都是偶數
if ((a & 1) == 0 && (b & 1) == 0) {
return gcd_binary(a >> 1, b >> 1) << 1;
}
// a 偶數,b 奇數
else if ((a & 1) == 0) {
return gcd_binary(a >> 1, b);
}
// a 奇數,b 偶數
else if ((b & 1) == 0) {
return gcd_binary(a, b >> 1);
}
// 都是奇數,減去較小者
else if (a > b) {
return gcd_binary((a - b) >> 1, b);
}
else {
return gcd_binary((b - a) >> 1, a);
}
}
int gcd_binary_iterative(int a, int b) {
a = abs(a);
b = abs(b);
if (a == 0) return b;
if (b == 0) return a;
// 找出共同的 2 的因子
int shift = 0;
while (((a | b) & 1) == 0) {
a >>= 1;
b >>= 1;
shift++;
}
// 移除 a 中的 2 因子
while ((a & 1) == 0) {
a >>= 1;
}
do {
// 移除 b 中的 2 因子
while ((b & 1) == 0) {
b >>= 1;
}
// 現在 a 和 b 都是奇數
if (a > b) {
int temp = a;
a = b;
b = temp;
}
b = b - a; // b >= a,所以 b-a >= 0
} while (b != 0);
return a << shift;
}
int gcd_iterative(int a, int b) {
a = abs(a);
b = abs(b);
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
| 特性 | 遞迴版 | 迭代版 |
|---|---|---|
| 程式碼簡潔度 | 高 | 中 |
| 記憶體使用 | O(log n) 堆疊空間 | O(1) 常數空間 |
| 堆疊溢位風險 | 極大數值可能溢位 | 無風險 |
| 效能 | 函式呼叫開銷 | 無函式呼叫,較快 |
| 除錯難度 | 較難追蹤 | 容易追蹤 |
int gcd_array(int arr[], int n) {
if (n <= 0) return 0;
int result = abs(arr[0]);
for (int i = 1; i < n; i++) {
result = gcd_iterative(result, arr[i]);
if (result == 1) break; // 提前結束:互質
}
return result;
}
// 使用範例
int numbers[] = {48, 18, 24, 36};
int gcd_all = gcd_array(numbers, 4); // 結果:6
// LCM(a, b) = |a * b| / GCD(a, b)
// 注意溢位:使用 long long 或先除後乘
long long lcm(long long a, long long b) {
if (a == 0 || b == 0) return 0;
return llabs(a) / gcd_iterative(a, b) * llabs(b);
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int gcd_euclidean(int a, int b);
int gcd_subtraction(int a, int b);
int gcd_binary(int a, int b);
int gcd_iterative(int a, int b);
void test_performance() {
struct timespec start, end;
long long iterations = 1000000;
int a = 12345678, b = 87654321;
printf("效能測試 (%lld 次迭代):\n", iterations);
clock_gettime(CLOCK_MONOTONIC, &start);
for (long long i = 0; i < iterations; i++) gcd_euclidean(a, b);
clock_gettime(CLOCK_MONOTONIC, &end);
printf("歐幾里得遞迴:%.3f ms\n",
(end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1e6);
clock_gettime(CLOCK_MONOTONIC, &start);
for (long long i = 0; i < iterations; i++) gcd_iterative(a, b);
clock_gettime(CLOCK_MONOTONIC, &end);
printf("歐幾里得迭代:%.3f ms\n",
(end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1e6);
clock_gettime(CLOCK_MONOTONIC, &start);
for (long long i = 0; i < iterations; i++) gcd_binary(a, b);
clock_gettime(CLOCK_MONOTONIC, &end);
printf("二進制遞迴:%.3f ms\n",
(end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1e6);
}
int main() {
// 功能測試
int test_cases[][2] = {
{48, 18}, {100, 25}, {17, 5}, {0, 5}, {-24, 36}, {1000000, 1}
};
printf("功能驗證:\n");
for (int i = 0; i < 6; i++) {
int a = test_cases[i][0], b = test_cases[i][1];
printf("gcd(%d, %d) = %d (迭代), %d (二進制)\n",
a, b, gcd_iterative(a, b), gcd_binary(a, b));
}
printf("\n");
test_performance();
return 0;
}
abs()gcd(a, 0) = |a|,gcd(0, 0) 通常定義為 0