bdfz_2024_summer/day7/inverse/inverse_by_chat.md
2024-08-09 10:03:38 +08:00

11 KiB
Raw Blame History

快速幂算法是一种高效计算大整数幂次的方法,时间复杂度为 $O(\log n)$,远优于直接的幂运算 $O(n)$。其核心思想是将指数的二进制展开,通过平方和乘法快速计算幂次。以下是详细的解释和实现。

核心思想

假设我们要计算 $a^b \mod m$,其基本思想如下:

  1. 如果 b 是偶数: $ a^b = (a^{b/2})^2 $
  2. 如果 b 是奇数: $ a^b = a \cdot a^{b-1} $

通过这种递归分治的方法,可以将指数迅速减半,从而提高计算效率。

算法步骤

  1. 初始化结果 $result = 1$。
  2. b > 0 时,执行以下循环:
    • 如果 b 是奇数,更新结果:$result = (result \cdot a) \mod m$。
    • 更新基数:$a = (a \cdot a) \mod m$。
    • b 右移一位(相当于除以 2
  3. 返回结果。

代码实现

以下是 C++ 代码实现:

#include <iostream>
using namespace std;

// 快速幂算法,计算 (base^exp) % mod
long long modExp(long long base, long long exp, long long mod) {
    long long result = 1;  // 初始化结果
    while (exp > 0) {
        if (exp % 2 == 1) {  // 如果 exp 是奇数
            result = (result * base) % mod;
        }
        base = (base * base) % mod;  // 基数自乘
        exp /= 2;  // 指数右移一位
    }
    return result;
}

int main() {
    long long base = 3, exp = 200, mod = 13;
    cout << base << "^" << exp << " % " << mod << " = " << modExp(base, exp, mod) << endl;
    return 0;
}

逐行解释

  1. 函数声明和初始化

    long long modExp(long long base, long long exp, long long mod) {
        long long result = 1;
    
    • modExp 函数接受三个参数:base (底数)exp (指数),和 mod (模数)。
    • result 初始化为 1将用于存储最终结果。
  2. 循环

        while (exp > 0) {
    
    • exp 大于 0 时,继续循环。
  3. 处理指数为奇数的情况

            if (exp % 2 == 1) {
                result = (result * base) % mod;
            }
    
    • 如果 exp 是奇数(即 exp % 2 == 1),更新 result(result * base) % mod
  4. 更新基数和指数

            base = (base * base) % mod;
            exp /= 2;
        }
    
    • base 更新为 (base * base) % mod,这一步相当于将 base 平方。
    • exp 右移一位(相当于除以 2
  5. 返回结果

        return result;
    }
    
    • exp 为 0 时,循环结束,返回 result
  6. 主函数

    int main() {
        long long base = 3, exp = 200, mod = 13;
        cout << base << "^" << exp << " % " << mod << " = " << modExp(base, exp, mod) << endl;
        return 0;
    }
    
    • 定义底数、指数和模数,调用 modExp 计算结果并输出。

示例计算

假设我们要计算 $3^{200} \mod 13$,步骤如下:

  1. 初始值:result = 1base = 3exp = 200mod = 13
  2. 200 是偶数,计算 base = (3 * 3) % 13 = 9exp = 200 / 2 = 100
  3. 100 是偶数,计算 base = (9 * 9) % 13 = 81 % 13 = 3exp = 100 / 2 = 50
  4. 50 是偶数,计算 base = (3 * 3) % 13 = 9exp = 50 / 2 = 25
  5. 25 是奇数,计算 result = (1 * 9) % 13 = 9base = (9 * 9) % 13 = 81 % 13 = 3exp = 25 / 2 = 12
  6. 重复上述步骤,直到 exp 为 0。

最终结果为 3^{200} % 13 = 9

通过这种方法,可以高效地计算大整数幂次的模运算。如果有其他问题或需要进一步的解释,随时告诉我!

要使用费马小定理和快速幂来计算 $\frac{a}{b} \mod p$,需要先了解费马小定理和快速幂算法。

费马小定理

费马小定理表明,对于一个质数 p 和任意整数 $ a ,如果 a 不被 p $ 整除,则:

a^{p-1} \equiv 1 \pmod{p}

通过这个定理,可以得到 a 的逆元:

a^{p-2} \equiv a^{-1} \pmod{p}

快速幂算法

快速幂算法用于高效地计算大整数的幂模某个数的值。其时间复杂度是 $ O(\log n) $。

实现步骤

  1. 使用费马小定理求模逆元
  2. 使用快速幂算法求出 b^{p-2} \mod p 作为 b 的逆元
  3. 将分数转换为乘法形式

C++ 实现

以下是使用费马小定理和快速幂算法计算 \frac{a}{b} \mod p 的 C++ 实现:

#include <iostream>
using namespace std;

// 快速幂算法,计算 (base^exp) % mod
long long modExp(long long base, long long exp, long long mod) {
    long long result = 1;
    while (exp > 0) {
        if (exp % 2 == 1) {  // 如果 exp 是奇数
            result = (result * base) % mod;
        }
        base = (base * base) % mod;
        exp /= 2;
    }
    return result;
}

// 求 b 在模 p 下的逆元,使用费马小定理
long long modInverse(long long b, long long p) {
    // b^(p-2) ≡ b^(-1) (mod p)
    return modExp(b, p - 2, p);
}

// 计算 (a / b) % p
long long modDivide(long long a, long long b, long long p) {
    long long inv = modInverse(b, p);
    return (a * inv) % p;
}

int main() {
    long long a = 10, b = 3, p = 7;
    try {
        long long result = modDivide(a, b, p);
        cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
    } catch (const invalid_argument &e) {
        cout << e.what() << endl;
    }
    return 0;
}

逐行解释

  1. 快速幂算法

    long long modExp(long long base, long long exp, long long mod) {
        long long result = 1;
        while (exp > 0) {
            if (exp % 2 == 1) {  // 如果 exp 是奇数
                result = (result * base) % mod;
            }
            base = (base * base) % mod;
            exp /= 2;
        }
        return result;
    }
    
    • modExp 函数实现快速幂算法,通过不断将指数减半,并在指数为奇数时乘以当前基数。
    • 循环中,基数平方并取模,指数减半,最终结果为 $(base^{exp}) \mod mod$。
  2. 求模逆元

    long long modInverse(long long b, long long p) {
        return modExp(b, p - 2, p);
    }
    
    • 使用费马小定理计算 b 的逆元,即 $ b^{p-2} \mod p $。
  3. 计算 $\frac{a}{b} \mod p$

    long long modDivide(long long a, long long b, long long p) {
        long long inv = modInverse(b, p);
        return (a * inv) % p;
    }
    
    • 调用 modInverse 求出 b 的模逆元 $ inv $。
    • 计算并返回 $ (a \times inv) \mod p $。
  4. 主函数

    int main() {
        long long a = 10, b = 3, p = 7;
        try {
            long long result = modDivide(a, b, p);
            cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
        } catch (const invalid_argument &e) {
            cout << e.what() << endl;
        }
        return 0;
    }
    
    • 定义 a, b, p 的值。
    • 调用 modDivide 计算结果并输出。

使用扩展欧几里得算法Extended Euclidean Algorithm, Exgcd来求解模逆元也是一种经典且高效的方法特别是在处理大数时更为优越。接下来我将详细解释如何使用扩展欧几里得算法来实现分数 \frac{a}{b} \mod p 的计算,并与快速幂进行比较。

扩展欧几里得算法的原理

扩展欧几里得算法不仅可以用于求两个数的最大公约数GCD还能求出模逆元。对于两个整数 a 和 (b),扩展欧几里得算法可以找到一组整数 x 和 (y),使得:


ax + by = \gcd(a, b)

ab 互质时,(\gcd(a, b) = 1),因此方程变为:


ax + by = 1

在这种情况下,x 就是 ab 的逆元,即:


a^{-1} \equiv x \pmod{b}

C++ 实现

下面是使用扩展欧几里得算法计算分数 \frac{a}{b} \mod p 的 C++ 实现代码:

#include <iostream>
using namespace std;

// 扩展欧几里得算法
long long extendedGCD(long long a, long long b, long long &x, long long &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    long long x1, y1;
    long long gcd = extendedGCD(b, a % b, x1, y1);
    x = y1;
    y = x1 - (a / b) * y1;
    return gcd;
}

// 求 b 的逆元,前提是 b 和 p 互质
long long modInverse(long long b, long long p) {
    long long x, y;
    long long g = extendedGCD(b, p, x, y);
    if (g != 1) {
        throw invalid_argument("Inverse doesn't exist.");
    } else {
        // 确保结果是正数
        return (x % p + p) % p;
    }
}

// 计算 (a / b) % p
long long modDivide(long long a, long long b, long long p) {
    long long inv = modInverse(b, p);
    return (a * inv) % p;
}

int main() {
    long long a = 10, b = 3, p = 7;
    try {
        long long result = modDivide(a, b, p);
        cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
    } catch (const invalid_argument &e) {
        cout << e.what() << endl;
    }
    return 0;
}

逐行解释

  1. 扩展欧几里得算法

    long long extendedGCD(long long a, long long b, long long &x, long long &y) {
        if (b == 0) {
            x = 1;
            y = 0;
            return a;
        }
        long long x1, y1;
        long long gcd = extendedGCD(b, a % b, x1, y1);
        x = y1;
        y = x1 - (a / b) * y1;
        return gcd;
    }
    
    • b = 0 时,直接返回 a 作为 gcdx = 1 和 (y = 0)。
    • 递归调用 extendedGCD(b, a % b, x1, y1),计算 gcd 及其对应的系数。
    • 通过回溯,计算当前的 x 和 (y)。
  2. 求模逆元

    long long modInverse(long long b, long long p) {
        long long x, y;
        long long g = extendedGCD(b, p, x, y);
        if (g != 1) {
            throw invalid_argument("Inverse doesn't exist.");
        } else {
            return (x % p + p) % p;
        }
    }
    
    • 使用扩展欧几里得算法计算 b 的模逆元 (x)。
    • 如果 gcd 不为 1bp 不互质,逆元不存在。
    • 否则,返回 x 的正值作为逆元。
  3. 计算 (\frac{a}{b} \mod p)

    long long modDivide(long long a, long long b, long long p) {
        long long inv = modInverse(b, p);
        return (a * inv) % p;
    }
    
    • 调用 modInverse 求出 b 的逆元 (inv)。
    • 返回 ( (a \times inv) \mod p )。
  4. 主函数

    int main() {
        long long a = 10, b = 3, p = 7;
        try {
            long long result = modDivide(a, b, p);
            cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
        } catch (const invalid_argument &e) {
            cout << e.what() << endl;
        }
        return 0;
    }
    
    • 定义 a, b, p 的值,计算并输出结果。

为什么使用 Exgcd

相比于快速幂算法Exgcd 在某些场景下更加灵活和高效:

  1. 处理更大范围的数Exgcd 可以直接处理整数(包括负数),而快速幂需要在计算中额外处理这些情况。
  2. 无需质数模:快速幂算法基于费马小定理,只能在模数为质数的情况下使用。而 Exgcd 不要求模数为质数。
  3. 更快的计算速度对于某些特定的输入Exgcd 在常数时间上可能更快。

通过这种方式,你可以在 C++ 中使用扩展欧几里得算法来实现高效的分数模运算。如果你还有其他问题或需要进一步的解释,随时告诉我!