zoukankan      html  css  js  c++  java
  • 数论—模运算的逆元

    目录

    有关模运算

    定义

    运算规则

    逆元

    定义

    使用方法

    求逆元的方法

    枚举法

    拓展欧几里得(Extend - Eculid)

    费马小定理(Fermat's little theorem)

    注意


    有关模运算

    • 在信息学竞赛中,当答案过于庞大的时候,我们经常会使用到模运算(Modulo Operation)来缩小答案的范围,以便输出计算得出的答案。
    • 定义

    给定一个正整数 p,任意一个整数 n,那么一定存在等式:

    n = k * p + r;

    其中k、r 是整数,且0 ≤ r < p,则称 k 为 n 除以 p 的商,r 为 n 除以 p 的余数。

    对于正整数 p 和正整数 a、b,定义如下运算:

    • 取模运算 : a % p (或 a mod p),表示 a 除以 p 的余数。
    • 模 p 加法:(a + b) % p,其结果是 a + b 算术和除以 p 的余数。
    • 模 p 减法:(a - b) % p,其结果是 a - b 算术差除以 p 的余数。
    • 模 p 乘法:(a * b) % p,其结果是 a * b 算数积除以 p 的余数。
    • 同余式:正整数 a、b 对 p 取模,他们的余数相同,记做 a ≡ b (mod p)。

    说明:

    n % p 得到结果的正负由被除数 n 决定,与 p 无关。

    例如:7 % 4 = 3, -7 % 4 = -3, -7 % -4 = -3.

    • 运算规则

      • 模运算与基本四则运算有些相似,但是除法除外。其规则如下:
        • (a + b) % p = (a % p + b % p) % p
        • (a - b) % p = (a % p - b % p) % p
        • (a * b) % p = (a % p * b % p) % p
        • a ^ b % p = ((a % p) ^ b) % p
        • 结合律
          • ((a + b) % p + c) = (a + (b + c) % p) % p
          • ((a * b) % p * c) = (a * (b * c) % p) % p
        • 交换律
          • (a + b) % p = (b + a) % p
          • (a * b) % p = (b * a) % p
        • 分配律
          • (a + b) % p = (a % p + b % p) % p
          • ((a + b) % p * c) % p = ((a * c) % p + (b * c) % p
      • 重要定理
        • 若 a ≡ b (mod p),则对于任意的 c,都有(a + c) ≡ (b + c) (mod p)
        • 若 a ≡ b (mod p),则对于任意的 c,都有(a * c) ≡ (b * c) (mod p)
        • 若 a ≡ b (mod p),c ≡ d (mod p),则
          • (a + c) ≡ (b + d) (mod p)
          • (a - c) ≡ (b - d) (mod p)
          • (a * c) ≡ (b * d) (mod p)
          • (a / c) ≡ (b / d) (mod p)

    逆元

    前文已经说过,模运算的除法运算规则与普通四则运算不同,那么当(a / b)的中间运算结果过大怎么办?

    使用逆元可以很好地解决这个问题。

    定义

    逆元是指在数学领域群G中任意一个元 a,都在G中有唯一的逆元a',具有性质 a · a' = a' · a = e ( · 为该群中定义的运算)。其中,e为该群的单位元。

    逆元其实是加法中的相反数以及乘法中的倒数的拓展思想。

    在模运算中,单位元便是1。

    a mod p的逆元便是可以使 a * a' mod p = 1 的最小a'。

    使用方法

    因为 b' 为 b 的逆元,b * b' mod p = 1;

    所以 (a / b) mod p = (a * b') mod p ,但要求a | b;

    这样我们便可以应用 (a * b) % p = (a % p * b % p) % p 这一条性质缩小中间运算结果了。

    求逆元的方法

    1. 枚举法;
    2. 利用拓展欧几里得算法求解同余方程;
    3. 费马小定理。

    枚举法

    枚举1到p - 1的整数bi,若b * bi % p = 1,则bi即为b mod p的乘法逆元。

    为什么只枚举到p - 1呢?

    1. 如果枚举到 p,那么显然 b * p % p = 0;;

    2. 如果枚举到 p + k ( 0 < k < p),那么有 b * (p + k) % p = b * p % p + b * k % p = b * k % p,这样就返回了枚举1到p - 1的情况;

    3. 如果枚举到 p + k ( k > p),同第二种情况。

    拓展欧几里得(Extend - Eculid)

    求最小整数x、y,使 x * a + y * b = gcd(a , b);

    类似这样的问题便可以使用拓展欧几里得来求解。

    由欧几里得定理可知gcd(a , b) = gcd(b , a % b) (假设 a > b),

    所以有x' * b + y' * (a % b) = gcd(a , b),假设已经求得 x' 和 y',那么有 :

    x' * b + y' * ( a % b) = gcd(a , b) and a % b = a - [a / b] * b

    ∴ x' * b + y' * ( a - [a / b] * b) = gcd(a , b)

    ∴ y' * a + (x' - y' * [a / b]) * b = gcd(a , b)

    如此这个问题便可以递归的求解了。

    (显然如果b = 0的话,那么x = 1,y = 0)

    那么求解 b' 使得 b * b‘ mod p = 1 这个问题便可以转化为:

    求最小整数 b'、k,使得 b' * b + k * p = 1;

    代码实现:

    #include<iostream>
    using namespace std;
    void ext_gcd(int a,int b,int &d,int &x,int &y) {
        if(b == 0) {
    	d = a; x = 1; y = 0;
    	return;
        }
        ext_gcd(b,a % b,d,y,x);
        y -= a / b * x;
        return;
    }
    int main() {
        int x,y,d;
        int a,b; 
        while(1) {
    	cin >> a >> b;
    	ext_gcd(a,b,d,x,y);
    	if(d != 1) cout << "There is no ans for this input." << endl;
    	else cout << "The ans for this input : " << (x + b) % b << endl;
        }
        return 0;
    } 

    费马小定理(Fermat's little theorem)

    假如 p 是质数,那么 a ^ (p-1) ≡ 1 (mod p) 。

    推论: b ^ (p - 2) % p 即为 b mod p 的乘法逆元。

    注意

    需要b、p互质才可以使用逆元法,如果b、p不互质的话只能用(a / b) % p = (a % (b * p)) / b 来尝试解决问题了。

    但是在信息竞赛中一般给出的模数均为质数,例如10 ^ 9 + 7 。

    NOIP 2018 RP++
  • 相关阅读:
    ASCII码
    cron表达式学习
    mysql学习二、SQL常用数据类型
    mysql学习一 常用语句
    python学习
    搬砖
    新接触Linux 命令
    搬砖
    python encode decode
    201521123071 《JAVA程序设计》第十二周学习总结
  • 原文地址:https://www.cnblogs.com/Black-S/p/9819265.html
Copyright © 2011-2022 走看看