求逆元的方法汇总

求逆元的方法汇总

  • 源于PO姐在UOJ群里面的讲课%%%%%

逆元

它是一个可以取消另一给定元素运算的元素。
amax1(mod  m)xam

前提

a(modm),am,

定理

欧拉定理(费马小定理)

  • 欧拉定理: amaφ(m)1(mod  m)

方法

1.欧拉定理

根据欧拉定理

aϕ(m)aaϕ(m)1a111aϕ(m)1(modm)(modm)(modm)

所以 aϕ(m)1a
时间复杂度 O(n) 即求出单个欧拉函数的值

2.exgcd

应用exgcd
我们假设a的逆元是x

axax+my=11(modm)

用exgcd求出x即为a的逆元
时间复杂度 O(loga)

3.需要线性处理1~n的逆元

更正:本方法需在模数为素数情况下才能使用!!!
我们假设 y=ax+b, b<i, 1<x<y
在将这个式子放在 mody  意义下

ax+b0(mody)

两边同时乘上 x1b1 得到
ab1+x1x10ab1(mody)(mody)

再将 y=ax+b 带入得到
x1yx(ymodx)1(mody)

所以我们定义 a[i]xmody 意义下的逆元,根据上式得到
a[i]=(y  div  x)a[ymodx]
1 在模任何意义下逆元都是 1
同时也就得到了一种 O(logN) 级别的递归求单个逆元的方法
由于每次取模,所以每次都相当于折半,复杂度就降到了 O(logN)

最后贴上代码

1.欧拉定理

const
    maxn=1000;
var
    prime:array[0..maxn]of longint;
    check:array[0..maxn]of boolean;
    i,j,k:longint;
    n,m,tt,len,ans:longint;
procedure prepare;
begin
    len:=0;
    for i:=2 to trunc(sqrt(maxn)) do
        begin
            if check[i]=false
            then begin inc(len); prime[len]:=i; end;
            for j:=1 to len do
                begin
                    if prime[j]*i>trunc(sqrt(maxn)) then break;
                    check[i*prime[j]]:=true;
                    if i mod prime[j]=0 then break;
                end;
        end;
end;

function f(a,b,m:longint):longint; {a^b mod m}
var t,y:int64;
begin
    t:=1; y:=a;
    while b<>0 do
        begin
            if b and 1<>0 then t:=(t*y)mod m;
            y:=(y*y)mod m;
            b:=b>>1;
        end;
    exit(t);
end;

begin
    prepare;
    readln(n,m); tt:=m;
    ans:=m;
    for i:=1 to len do
        begin
            if m=1 then break;
            if m mod prime[i]=0 then ans:=(ans div prime[i])*(prime[i]-1);
            while m mod prime[i]=0 do
                m:=m div prime[i];
        end;
    if m<>1 then ans:=(ans div m)*(m-1);
    writeln(f(n,ans-1,tt));
end.

2.exgcd

var
    n,m,a,b,c,x,y:longint;
procedure exgcd(a,b:longint; var x,y:longint);
var c:longint;
begin
    if b=0
    then begin x:=1; y:=0; exit; end
    else exgcd(b,a mod b,x,y);
    c:=x; x:=y; y:=c-(a div b)*x;
end;

begin
    readln(n,m);
    exgcd(n,m,x,y);
    x:=(x+((x div m)+1)*m+m)mod m;
    writeln(x);
end.

线性处理1~n

const
    maxn=1000;
var
    x:array[0..maxn]of longint;
    i,j,k:longint;
    n,m:longint;
begin
    readln(n,m);
    x[1]:=1;
    for i:=2 to n do
        x[i]:=(-(m div i)*x[m mod i])mod m;
    writeln(x[i]);
end.    

线性处理转求单个

var
    n,m:longint;
function f(a:longint):longint;
begin
    if a=1
    then exit(1)
    else exit((-(m div a)*f(m mod a))mod m);
end;

begin
    readln(n,m);
    writeln(f(n));
end.

你可能感兴趣的:(求逆元的方法汇总)