zoukankan      html  css  js  c++  java
  • [数论]逆元

    模板题:Luogu P3811 【模板】乘法逆元 

    逆元类似于倒数,

    在($mod$ $p$)意义下,如果$a$*$inv$[$a$]≡ 1 ,那么我们就说 $inv$[$a$]是 $a$ 的逆元,$a$也是$inv$[$a$]的逆元。

    前提必须保证$a,p$互质。

    当题目要求在$mod$ $p$意义下求$a*b$时,为了避免数据过大引起的麻烦,通常会把它拆成$a$ $mod$ $p*b$ $mod$ $p$。

    但是$a/b$却不能这样拆分。这时用逆元把它转化为$a*inv$[$b$],变成乘法,就可以拆分了。

    逆元的性质

    1.存在唯一性

    2.完全积性函数

    $inv$[$a$]*$inv$[$b$] ≡ $inv$[$a*b$]

    证明:

    $a$*$inv$[$a$] ≡ $b$*$inv$[$b$] 1 ($mod$ $p$)

    $a$*$inv$[$a$] * $b$*$inv$[$b$] ≡ 1*1 ($mod$ $p$)

    ($ a $*$b$* ($inv$[$a$]*$inv$[$b$]) ≡ 1 ($mod$ $p$)

    3.

    $a$*$inv$[$b$] ≡ $a/b$ ($mod$ $p$)

    证明:

    $b$*$inv$[$b$] ≡ 1 ($mod$ $p$)

    同乘$a/b$,得

    $a$*$inv$[$b$] ≡ $a/b$ ($mod$ $p$)

    其他的:

    在$(mod$ $n)$意义下,集合{[0,n-1]中存在逆元的数}==集合{[0,n-1]中存在逆元的数的逆元}

    因为i < n,inv[i] < n,

    a的逆元是b,b∈[0,n-1] ;b的逆元是a,a∈[0,n-1]

    所以这些数应该互为逆元。

    接下来介绍几种逆元的求法。

    费马小定理

    定理:$x$p-1 ≡ 1 ($mod$ $p$)

    证明:(源自数论妙趣——数学女王的盛情款待)

    任意取一个质数,比如13。考虑从1到12的一系列整数1,2,3,4,5,6,7,8,9,10,11,12,给这些数都乘上一个与13互质的数,比如3,得到3,6,9,12,15,18,21,24,27,30,33,36。对于模13来说,这些数同余于3,6,9,12,2,5,8,11,1,4,7,10。这些余数实际上就是原来的1,2,3,4,5,6,7,8,9,10,11,12,只是顺序不同而已。

    把1,2,3...12统统乘起来,乘积就是12的阶乘12!。把3,6,9,..36也统统乘起来,并且提出公因子3,乘积就是312×12!。对于模13来说,这两个乘积都同余于1,2,3...12系列,尽管顺序不是一一对应,即312×12!≡12!mod 13。两边同时除以12!得312≡1 mod 13。如果用p代替13,用x代替3,就得到费马小定理

    $x$p-1≡1 ($mod$ $p$)

    由$x$p-1≡1 ($mod$ $p$)

    得$x$*$x$p-2≡1 ($mod$ $p$)

    $x$p-2是$x$在$mod$ $p$意义下的逆元

    注意,这个方法要求$a,p$互质。

    int quickpow(int a,int b,int p){
        int ans = 1;
        int base = a;
        while(b){
            if(b&1)ans = ans*base % p;
            base = base*base % p;
            b >>= 1;
        }
        return ans%p;
    }
    
    int main(){
        scanf("%d%d",&n,&p);
        for(int i = 1;i <= n;i++)
            printf("%d
    ",quickpow(i,p-2,p));
        return 0;
    }

    扩展欧几里得

    求$a*x≡1$ ($mod$ $p$),等同于

    $a*x+p*y≡1$

    我们知道,$exgcd$可求$a*x + b*y≡gcd(a,b)$

    而这里的$a,p$互质,刚好有$gcd(a,p)=1$

    所以求出$exgcd(a,p,x,y)$,$inv[a] = x$

    这个方法要比费马小定理快一点。

    void exgcd(int a,int b,int &x,int &y){
        if(b == 0){
            x = 1,y = 0;
            return;
        }
        exgcd(b,a%b,x,y);
        int tx = x;
        x = y;
        y = tx-(a/b)*y;
    }
    
    int main(){
        scanf("%d%d",&n,&p);
        for(int i = 1;i <= n;i++){
            exgcd(i,p,x,y);
            printf("%d
    ",(x%p+p)%p);
        }
        return 0;
    }

    线性推逆元

    公式:inv[i]=inv[p%i]*(p-p/i)%p

    证明:

    令$s=p/i,t=p$ $mod$ $i$,

    则$s*i+t=p$

    $s*i+t≡0$ ($mod$ $p$)

    $t≡-s*i$ ($mod$ $p$)

    两边同除$t*i$,得

    $t/(t*i)≡-s*i/(t*i)$ ($mod$ $p$)

    $inv[i]≡-s*inv[t]$ ($mod$ $p$)

    将$s,t$的值带入,则有

    $inv[i]≡(-p/i)*inv[p$ $mod$ $i]$ ($mod$ $p$)

    将$-p/i$处理成正数,即$-p/i=p-p/i$

    整理,得$inv[i]≡inv[p$ $mod$ $i]*(p-p/i)$ ($mod$ $p$)

    这个方法很好,适用于要求很多逆元的情况。

    inv[0] = inv[1] = 1;
    for(int i = 2; i <= n; i++)
        inv[i] = inv[p%i]*(p-p/i)%p;
  • 相关阅读:
    ansible命令应用示例
    ansible运维自动化工具
    grep 命令使用指南
    Nginx httpS server配置
    find 使用指南
    fastcgi_param 详解
    nginx虚拟主机的配置
    shell中字体变色
    nginx.conf 配置文件详解
    php-fpm.conf 配置文件详解
  • 原文地址:https://www.cnblogs.com/mogeko/p/10524868.html
Copyright © 2011-2022 走看看