乘法逆元 学习笔记

定义

如果 \(ax\equiv1\pmod{b}\),则称 \(x\)\(a\)\(b\) 的乘法逆元。

乘法逆元可以处理模意义的除法,可以理解成模意义下的倒数。比如在乘法中 \(a\times \frac{1}{a}=1\),也就是说倒数抵消了乘法,因此有 \(\frac{x}{a}=x\times\frac{1}{a}\)。同理在模意义下 \(\frac{x}{a}=x\times a^{-1}\)

当且仅当 \(\gcd(a,b)=1\) 时逆元存在。

费马小定理求逆元

费马小定理:当 \(b\) 为质数时 \(a^{b-1}\equiv 1\pmod{b}\)

\(b\) 为质数,\(ax\equiv1\equiv a^{b-1}\pmod{b},x\equiv a^{b-2}\pmod{b}\)。快速幂时间复杂度 \(O(\log n)\),仅适用于 \(b\) 为质数的情况。

template<typename T>T inv(T a,T b)
{
  return qpow(a,b-2,b);
}

扩展欧几里得算法求逆元

\(ax\equiv1\pmod{b}\)\(ax+by=1\)。扩欧求解 \(x\) 的最小整数解即可。

template<typename T>void exgcd(T a,T b,T &x,T &y){
  b?(exgcd(b,a%b,y,x),y-=a/b*x):(x=1,y=0);
}
template<typename T>T inv(T a,T b){
  T x,y;
  exgcd(a,b,x,y);
  return (x%b+b)%b;
}

补充:

一个另类的求逆元方法

大意是已知 \(b\)\(a\) 的逆元 \(y\),则 \(ax+by=1\),此时 \(x=\frac{1-by}{a}\) 也是 \(a\) 的逆元。而求 \(b\)\(a\) 的逆元可以先对 \(b\)\(a\),就辗转相除了。本质和 exgcd 相同。

template<typename T>T inv(T a,T b){
  return a>1?b-inv(b%a,a)*b/a:1;//此处没有一是因为一会被取整,没有影响
}

线性求前 n 个数的逆元

显然 \(1^{-1}\equiv1\pmod{p}\)

\(x^{-1}\) 时,设 \(a=\lfloor\frac{p}{x}\rfloor,b=p\bmod x,ax+b=p\),有:

\[\begin{aligned}ax+b\equiv0\pmod{p}\\ab^{-1}+x^{-1}\equiv0\pmod{p}\\x^{-1}\equiv-ab^{-1}\pmod{p}\end{aligned} \]

递推求解即可。

template<typename T>void inv(int n,T p,T ans[]){
  ans[1]=1;
  for(int i=2;i<=n;i++)ans[i]=(p-p/i)*ans[p%i]%p;
}

线性求任意 n 个数的逆元

首先求前缀积,记为 \(fac_i\)。计算出前缀积的逆元 \(vac_n=fac_i^{-1}\)

由于 \(vac_i\) 是前缀积的逆元,乘上最后一个数就会抵消,因此有 \(vac_i=vac_{i+1}\times a_{i+1}\)。可以递推 \(vac\)

此时 \(inv_i=fac_i\times vac_{i-1}\),原理同上。

template<typename T>void inv_fac(int n,T p,T a[],T ans[]){
  T fac[n+5],vac;
  fac[0]=1;
  for(int i=1;i<=n;i++)fac[i]=fac[i-1]*a[i]%p;
  vac=inv(fac[n],p),ans[n]=fac[n-1]*vac%p;
  for(int i=n-1;i>=1;i--)vac=vac*a[i+1]%p,ans[i]=fac[i-1]*vac%p;
}

同理可以预处理阶乘的逆元,常用于组合数。

科技:在线 O(1) 逆元

定义 Farey 序列 \(\mathcal{F}_n\) 为分母不超过 \(n\) 的最简真分数的升序排列。特殊地,认为 \(\frac 0 1,\frac 1 1\) 也是最简真分数。比如 \(\mathcal{F}_5=\left\{\frac 0 1,\frac 1 5,\frac 1 4,\frac 1 3,\frac 2 5,\frac 1 2,\frac 3 5,\frac 2 3,\frac 3 4,\frac 4 5,\frac 1 1\right\}\)

利用 Farey 序列和一个定理可以做到 \(O(p^{\frac 2 3})-O(1)\) 求逆元。\(p\) 是模数。

狄利克雷逼近定理:对于任意实数 \(x\) 和任意正整数 \(n\),均存在整数 \(h\) 和整数 \(0<k\leq n\) 使 \(|kx-h|<\frac 1 n\)

证明:考虑 \(n+1\) 个数 \(0,\{x\},\{2x\},\{3x\},\cdots,\{nx\}\)。将 \([0,1)\) 均分成 \(n\) 个区间,根据抽屉原理,必有两个数落在同一个区间,设 \(|\{bx\}-\{ax\}|<\frac 1 n\),即 \(|(b-a)x-(\lfloor bx\rfloor-\lfloor ax\rfloor)|<\frac 1 n\)。那么 \(k=(b-a),h=\lfloor bx\rfloor-\lfloor ax\rfloor\)

两边除以 \(k\),得到定理的一个变体:对于任意实数 \(x\in[0,1]\) 和任意正整数 \(n\),均存在整数 \(0<h<k\leq N\) 使 \(|x-\frac h k|<\frac 1{nk}\),也就是说 \(\frac h k\)\(\mathcal{F}_n\) 里。

对于 \(\frac a p\),在 \(\mathcal{F}_n\) 中找到这样的一个逼近 \(\frac x y\),有 \(|\frac a p-\frac x y|<\frac 1{yn},|ay-px|<\frac p n\)。这说明 \(ay\equiv u\pmod{p}\),且 \(u<\frac p n\)。所以预处理 \(1\sim\frac p n\) 的逆元,查询时返回 \(u^{-1}y\) 即可。

显然 \(\mathcal{F}_n\) 相邻两项之差大于 \(\frac 1{n^2}\)。开一个长 \(n^2\) 的序列,对于序列中的 \(\frac x y\),可以将其映射到 \(\lfloor\frac{xn^2}y\rfloor\) 上,这也顺便进行了桶排序。查询相当于找 \(\lfloor\frac{an^2}p\rfloor\) 的前驱后继,这可以扫一遍预处理。

预处理复杂度 \(O(n^2+\frac p n)\)\(n=p^{\frac 1 3}\) 最优。

int bn,fac[bn*bn+5],x[bn*bn+5],y[bn*bn+5],pre[bn*bn+5],nxt[bn*bn+5];
bool vis[bn*bn+5];
void init(int p){
  bn=pow(p,1.0/3)+1,fac[1]=1;
  for(int i=2;i<=bn*bn;i++)fac[i]=1ll*(p-p/i)*fac[p%i]%p;
  for(int i=1;i<=bn;i++)for(int j=0;j<=i;j++)if(!vis[j*bn*bn/i])vis[j*bn*bn/i]=1,x[j*bn*bn/i]=j,y[j*bn*bn/i]=i;
  for(int i=0;i<=bn*bn;i++)pre[i]=vis[i]?i:pre[i-1];
  for(int i=bn*bn;i>=0;i--)nxt[i]=vis[i]?i:nxt[i+1];
}
int inv(int a,int p){
  int pos=1ll*a*bn*bn/p,u1=1ll*a*y[pre[pos]]-1ll*p*x[pre[pos]],u2=1ll*a*y[nxt[pos]]-1ll*p*x[nxt[pos]];
  return abs(u1)<=p/bn?((u1>=0?1ll:-1ll)*y[pre[pos]]*fac[abs(u1)]%p+p)%p:((u2>=0?1ll:-1ll)*y[nxt[pos]]*fac[abs(u2)]%p+p)%p;
}

[[数学]]

posted @ 2024-03-01 09:35  lgh_2009  阅读(17)  评论(0)    收藏  举报