逆元求法

记录自己学的逆元求法

自己粗略的对逆元理解:
逆元 :关于 a/b对于mod的取模,因为 (a%mod) / (b%mod) != (a/b)%mod; 就是说在除法当中执行取模操作对最后答案的贡献是不正确的,所以我们要引用逆元知识点 ,将除法转换为乘法进行取模 (a%mod * b%mod == (a*b)%mod ) (取模操作 加减乘 都是可以,唯独除法不行 (除法太卑微了) - - )
所以,对于 (a/b) %mod 转换为 a * b^(-1) 次方 ,而 b^(-1) 则可以用逆元求出

逆元的三种求法

  1. 扩展欧几里得求逆元
  2. 费马小定理求逆元
  3. 线性递推求逆元

扩展欧几里得

//扩展欧几里得求逆元 模数为质数
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){
		x = 1;
		y=0;
		return a;
	}
	ll d = exgcd(b,a%b,y,x);
	y -= a/b*x;
	return d;
}
ll inv(ll a,ll mod){
	ll x,y;
	ll d =exgcd(a,mod,x,y);
	if(d == 1){
		return (x%mod+mod)%mod;
	}
	return -1;
}
int main(void){
	/* ----- 初始内容省略 */
	/* 调用方式 */
	ll ans = inv(b,mod); // 此时的b就是 a/b 中的 b,然后利用扩展欧几里得 求出 关于b的逆元 
	return 0;
}

费马小定理
相对而言,费马小定理就简单多了 (费马小定理跟快速幂更配噢)

//快速幂 
ll pow2(ll a,ll n){
	ll res = 1;
	while(n){
		if(n&1) res = (res*a)%mod;
		a = (a*a)%mod;
		n >>= 1;
	}
	return res%mod;
}
int main(void){
	/* 利用快速幂求关于b的逆元 */
	/* a^(p-1) = 1 (mod p) p 为模数且是质数*/
	/* a^(p-2) = a^-1 (mod p) a^(mod-2) 即为 a 的逆元*/ 
	ll ans = (pow2(b,mod-2))%mod;
	return 0;
}

线性递推

其实就类似一个打表,通常情况下 mod 模数不是很大没有超过1e7左右这样子
	ll f2[mod+5];
void init(){
	f2[1] = 1; //初始化边界 为1
	for(int i = 2;i<mod+5;++i){
		f2[i] = (mod-mod/i)*f2[mod%i] %mod; //线性递推
		/* 证明: */
		/*
			M 即为模数(mod)
			设t=M/i,k=M%i,那么
	    	t*i+k≡0(Mod M)
    		-t*i≡k(Mod M)
			对上式两边同时除 i×k,进一步得到
    		-t*inv[k]≡inv[i](Mod M)
			再把和替换掉,最终得到
    		inv[i]=(M-M/i)*inv[M%i]%M
    		
			(直接搬过来了)
		*/
	}
}

int main(void){
	ll ans = f2[b]; // 返回的是关于b的逆元
	return 0;
}

你可能感兴趣的:(逆元求法(模板),算法)