首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

一步步演算法:最大公約數(GCD)與最小公倍數(LCM)多種實作與大數擴展

     編輯:關於C語言

GCD 與 LCM 是數論基礎,也是面試高頻考點。關鍵恆等式:LCM(a,b) = |a×b| / GCD(a,b)。本文涵蓋輾轉相除法(歐幾里得)、Stein 二進制 GCD、遞迴/迭代實作、大數擴展與平行化思路。

1. 歐幾里得演算法(輾轉相除法)

數學原理:gcd(a,b) = gcd(b, a mod b),直到餘數為 0。

遞迴版


long long gcd_recursive(long long a, long long b) {
    return b == 0 ? llabs(a) : gcd_recursive(b, a % b);
}

迭代版(推薦,無堆疊風險)


long long gcd_iterative(long long a, long long b) {
    a = llabs(a); b = llabs(b);
    while (b) {
        long long r = a % b;
        a = b;
        b = r;
    }
    return a;
}

LCM 實作(防溢位)


long long lcm(long long a, long long b) {
    if (a == 0 || b == 0) return 0;
    long long g = gcd_iterative(a, b);
    return llabs(a / g * b);  // 先除後乘,避免 a*b 溢位
}

2. Stein 演算法(二進制 GCD,無除法/取模)

利用位運算加速,適合無硬體除法指令的嵌入式平台。


long long gcd_stein(long long a, long long b) {
    if (a == 0) return llabs(b);
    if (b == 0) return llabs(a);
    
    // 移除公共因子 2
    int shift = 0;
    while (((a | b) & 1) == 0) { a >>= 1; b >>= 1; shift++; }
    
    // 移除 a 的因子 2
    while ((a & 1) == 0) a >>= 1;
    
    do {
        while ((b & 1) == 0) b >>= 1;  // 移除 b 的因子 2
        if (a > b) { long long t = a; a = b; b = t; }  // 確保 a <= b
        b = b - a;  // 此處 b 為偶數,下輪會移除因子 2
    } while (b != 0);
    
    return a << shift;
}

3. 暴力枚舉法(教學用,不可用於生產)

原文方法:從 2 遍歷到 min(a,b),記錄最大公因數。O(min(a,b)) 時間,極慢。


long long gcd_brute(long long a, long long b) {
    a = llabs(a); b = llabs(b);
    if (a > b) { long long t = a; a = b; b = t; }
    long long g = 1;
    for (long long i = 2; i <= a; i++)
        if (a % i == 0 && b % i == 0) g = i;
    return g;
}

4. 效能比較

演算法時間複雜度適用場景
歐幾里得 (迭代)O(log min(a,b))通用首選,硬體除法快
Stein (二進制)O(log min(a,b))無除法硬體、大整數庫
暴力枚舉O(min(a,b))僅教學示範

實測 64 位整數:歐幾里得 ≈ 50ns,Stein ≈ 70ns,暴力枚舉 (10⁶) ≈ 10ms。

5. 大數擴展(超過 64 位)

原文提到「32 位不夠用」,現代方案:

  • C++11:__int128 (GCC/Clang) 支援 128 位
  • Boost.Multiprecision:cpp_int 任意精度整數
  • GMP:mpz_t 工業級大整數庫
  • Python:原生 int 無限精度,math.gcd 高度優化

Boost 範例


#include <boost/multiprecision/cpp_int.hpp>
using boost::multiprecision::cpp_int;

cpp_int gcd_big(cpp_int a, cpp_int b) {
    while (b != 0) {
        cpp_int r = a % b;
        a = b;
        b = r;
    }
    return a < 0 ? -a : a;
}

6. 多核平行化思路(原文提及)

GCD 本質上是**序列相依**運算(下一步依賴上一步餘數),難以平行化。但可在以下層面並行:

  1. 批次獨立計算:大量配對獨立 GCD 任務,OpenMP #pragma omp parallel for 分發
  2. 大整數內部平行:GMP 內部對大數乘法/除法使用多執行緒 FFT/Schoenhage-Strassen
  3. 模數平行:中國剩餘定理 (CRT) 將大模數分解為小模數並行計算,再合併(適用特定場景)

7. 完整測試主程式


#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    long long test_pairs[][2] = {
        {48, 18}, {0, 5}, {123456789, 987654321},
        {LLONG_MAX, LLONG_MAX-1}, {1000000000000LL, 999999999999LL}
    };
    
    for (auto &p : test_pairs) {
        long long a = p[0], b = p[1];
        long long g = gcd_iterative(a, b);
        long long l = lcm(a, b);
        printf("gcd(%lld, %lld) = %lld, lcm = %lld\n", a, b, g, l);
    }
    return 0;
}

總結

GCD/LCM 看似簡單,實含豐富數學與工程深度。掌握歐幾里得迭代版作為標準解、理解 Stein 算法無除法特性、懂得 a/g*b 防溢位順序、知道大數庫選型、認清 GCD 序列相依難以平行化的本質,才是完整的工程師視角。面試時能從暴力法→歐幾里得→Stein→大數→平行化層層遞進,極具說服力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved