乘法逆元

乘法逆元
我们都知道
(a+b)%p=(a%p+b%p)%p
(a-b)%p=(a%p-b%p)%p
(ab)%p=(a%pb%p)%p
(a/b)%p…不满足

可以这样展开有什么好处呢?
那就是当a和b很大时,可以先对其进行求模运算再运算。但是除法不能用上述公式,那么我们可以考虑一种新的方法解决这个问题。

定义
若在 mod p意义下,对于一个整数a,有a*x ≡ 1(mod p),(这里面面等号不代表恒等于,而是说同余,即(a*x)%p的值与 1(mod p)相等),那么这个整数x即为a乘法逆元,同时a也为x乘法逆元

充要条件
a存在模p的乘法逆元的充要条件是 gcd(a,p) = 1,即a与p互质。

那么求取 (a/b)%p就等同与求 a*(b的逆元)%p。

证明
假设b的逆元为x,那么 (b*x)(mod p) = 1(mod p) ,那么x=b在数论上的倒数,即x=1/b,这样我们可以吧 (a\b)%p ==> (a*x)%p
证明如下:

(a / b) % p= m …0 ----------------①
①*b(并满足modp情况)得 a%p=m*(b%p)
也就是说 a ≡ m*b (%p)---------- ②
②*x得 a*x≡ m*b*x (%p)
将 b*x 看做一个整体 因为 b*x ≡1 (mod p)
可以将 b*x换为1 ,那么 a*x ≡ m (%p)
因为 (a/b)%p =m 那么 a*x ≡ ((a/b)%p) %p
即 (a*x)%p = (a/b)%p

那么新的问题来了,如何求逆元?
有很多种方法:费马小定理(p为质数)、扩展欧几里得、线性递推…

在信息学竞赛中 p一般都为 质数

那么我们用费马小定理来求一下:

费马小定理
假如 a 是一个整数,p是一个质数,那么

1、如果a是p的倍数,a^p ≡ a (mod p)

2、如果a不是p的倍数,a^(p-1) ≡ 1 (mod p)

注:≡ 表示的不是恒等于,而是上面提到的 模p同余

我们求逆元根据逆元存在的充要条件,a与p肯定是互质的,那么我们就用第二条。
先把第二个式子拿过来:

a^(p-1) ≡ 1 (mod p)

根据上面式子,我们转化成 a a^(p-2) ≡ 1 (mod p)*
那么! a ^ ( p - 2 ) 不就正是 a的逆元了吗。
别高兴太早,真正是a的逆元的是这个 a^(p-2)%p
因为我们的式子本质是 (a%p) * [a^(p-2)%p] = 1%p
Example:

a=5 ,p=3
a^(-1) 代表a的逆元
那么 a^(-1) =2
检验一下 上面这个数是不是 a的逆元:
( 5 * 2 )% 3 =1,1%3=1,所以,上面没有错。

在计算 a^(p-2)我们通常采用快速幂来求解
下面放一个 java快速幂的模板

//mod 为全局变量
public static long quickPow(long base,long power) {
         long res=1;
         while(power>0) {
              if((power&1)==1) {
              //如果当前阶数二进制最后一位为1即power为奇数,则令结果将上次偶数次阶算得的base乘到结果中
                  res=res*base%mod;
              }
              power>>=1;
              
              base=base*base%mod;
         }
         return res%mod;
}

Emmm…
刚说完,用java在洛谷交了一道这样的模板题,结果直接整了个这玩意(可能因为我用的是java吧)!!!
在这里插入图片描述
可以看出,费马小定理求逆元有点小慢。那么我们看另一种方法

exgec求逆元(O(logn))

这个是利用扩展欧几里得来求线性同余方程 a * x ≡ c (mod p) 的c =1 的情况。
我们就可以转换成 :a*x + p*y =1
直接上代码:

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class Main{
    static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static long mod;
    static long x,y;

    public static void main(String[] args) throws IOException{
        long a=100;
        long mod=100000007;
        exgcd(a,mod);
        x=(x%mod+mod)%mod;
        out.println(x);//57000004
        out.flush();
    }
    public static void exgcd(long a,long p)
    {
        if(p==0){
            x=1;y=0;
            return ;
        }
        exgcd(p,a%p);
        long tmp=x;
        x=y;
        y=tmp-a/p*y;
    }

    static int nextInt(){
        try {
            in.nextToken();
        }catch(IOException e){
            e.printStackTrace();
        }
        return (int)in.nval;
    }
    static double nextDouble(){
        try{
            in.nextToken();
        }catch(IOException e){
            e.printStackTrace();
        }
        return (double)in.nval;
    }
    static long nextLong(){
        try{
            in.nextToken();
        }catch(IOException e){
            e.printStackTrace();
        }
        return (long)in.nval;
    }
    static String next(){
        try{
            in.nextToken();
        }catch(IOException e){
            e.printStackTrace();
        }
        return in.sval;
    }
}

exgcd和费马小定理只适合用来求单个逆元,求大量数的逆元肯定会超时。
再看另一种方法
线性递推求逆元

看一下递推过程:

设 t = p / i,k = p % i,有:p = i * t + k // i为当前要求逆元的主角
即 i * t + k Ξ 0 (mod p)
即 k Ξ - i * t (mod p)
两边同时除以 i * k
有 1 / i Ξ - t / k (mod p)
将k,t带入
有 inv[ i ] Ξ - p / i * inv[ p % i ] (mod p)
为防止有负数,有inv[ i ] = ( p - p / i * inv[ p % i ] % p ) % p
//此处为小技巧,多加一个p
inv[i]=(p-p/i*inv[p%i]%p)%p;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值