逆元

扩展欧几里得

当 a 与 b 互素时有 gcd ( a , b ) = 1 ;

即得: a * x + b * y = 1;

a * x ≡ 1 ( mod b );

由于 a 与 b 互素,同余式两边可以同除 a ,得:

1 * x ≡ 1 / a (mod b);
x就是a%b的逆元

 

 

费马小定理(Fermat's little theorem)是数论中的一个重要定理,在1636年提出,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

 

欧拉定理

a^(φ(m))≡1(mod m) (a与m互质)

φ(n)=n * (1-1/p1) * (1-1/p2) ********(1-1/pn)   p[1~n]为小于n的素数


//求单个数的欧拉函数:

int euler_phi(int n)  
{  
    int res = n;  
    int m = (int)sqrt(n);  
    for(int i = 2; i <= m; i++)  
        if(n % i == 0)  
        {  
            res = res / i * (i-1);  
            while(n % i == 0) n /= i;  
        }  
    if(n > 1) res = res / n * (n-1);  
    return res;  
}

//筛选法求欧拉函数

void euler_phi()  
{  
    for(int i = 1; i < N; i++) phi[i] = i;  
    for(int i = 2; i < N; i++)  
        if(phi[i] == i) //成立说明i是素数
            for(int j = i; j < N; j += i) //j要从i开始,这样可以处理素数的情况  
                phi[j] = phi[j] / i * (i-1);  
}  

void Get_phi()///筛法求欧拉函数
{
    cnt = 0;
    memset(flag, true, sizeof(flag));
    phi[1] = 1;
    for(int i=2; i<MAXN; i++)///线性筛法
    {
        if(flag[i])///素数
        {
            p[cnt++] = i;
            phi[i] = i-1;///素数的欧拉函数值是素数 - 1
        }
        for(int j=0; j<cnt; j++)
        {
            if(i*p[j] > MAXN)
                break;
            flag[i*p[j]] = false;///素数的倍数,所以i*p[j]不是素数
            if(i%p[j] == 0)///性质:i mod p == 0, 那么 phi(i * p) == p * phi(i)
            {
                phi[i*p[j]] = p[j] * phi[i];
                break;
            }
            else
                phi[i*p[j]] = (p[j]-1) * phi[i];///i mod p != 0, 那么 phi(i * p) == phi(i) * (p-1) 
        }
    }
}

 

求幂大法  gcd(a,m)>1&&b>φ(m)     a^b≡a^(b%φ(m)+φ(m))(mod m)

 

 

一种通用的求逆元方法,适合(b|a)。公式如下

 

          

 

现在我们来证明它,已知,证明步骤如下

 

          

逆元打表

有时会遇到这样一种问题,在模质数p下,求1~n逆元 n< p(这里为奇质数)。可以O(n)求出所有逆元,有一个递推式如下

 
inv[i]=(M-M/i)*inv[M%i]%M
                   

 

它的推导过程如下,设t=M/i,k=M%i,那么

 
   ==>t*i+k≡0( mod M)
   ==>-t*i≡k( mod M)    
 

对上式两边同时除i*k,进一步得到
  ==>  -t/k≡1/i(mod M)//逆元
  ==>  -t*inv[k]≡inv[i](mod M)       
 

再把t和k替换掉,最终得到
       
     inv[i]=(M+M/i)*inv[M%i]%M
 

初始化inv[1]=1,这样就可以通过递推法求出1->n模奇素数p的所有逆元了。

 

另外有个结论模1->p的所有逆元值对应1->中所有的数,比如p=7,那么1->6对应的逆元是1 4 5 2 3 6。

typedef  long long ll;  
const int N = 1e5 + 5;  
int inv[N];  
   
void inverse(int n, int p) {  
    inv[1] = 1;  
    for (int i=2; i<=n; ++i) {  
        inv[i] = (ll) (p - p / i) * inv[p%i] % p;  
    }  
}  

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值