求逆元算法

费马小定理:若p是素数,a是正整数且不能被p整除,则ap-1 == 1(mod p)

费马小定理的拓展:ap == a(mod p)

欧拉定理:对任意互素的a和n. 设Φ(n) 为小于n且与n互素的正整数的个数,有aΦ(n) == 1(mod n)

欧拉定理的拓展:aΦ(n)+1 == a(mod n)

求乘法逆元的作用:除以一个数 再取模时,可以将这个数乘以这个数的逆元 再取模(将除法转化成乘法运算)

为什么要这样等价:对于 (a/b)% mod 这个式子,是不可以等价为 ((a%mod) / (b%mod))%mod 的 (例如:a=3,b=2,mod=3),但是可以写为(a*b-1)%mod,其中b-1表示b的逆元。这就是逆元的作用

引用计蒜客某题面:

求逆元算法_第1张图片

什么是乘法逆元:a*x = 1(mod C),那么称 x 为 a 对 C 的乘法逆元

e.g. a = 4,C = 7 则逆元 x = 2;

  4*2=1(mod 7)          12/4%7 = (12*2)%7 (除法换乘法)

用一道入门题来来学习三中模板:https://www.luogu.org/problem/P3811

 

模板一:

线性打表递推法:(递推公式:inv[i] = (p-p/i) * inv[p%i] % p )

这种方法最快,但是耗费的空间多

 1 #include 
 2 #include 
 3 #include 
 4 #include <string>
 5 #include 
 6 #include 
 7 #include 
 8 #include 
 9 #include 
10 #include 
11 #include 
12 #include 
13 using namespace std;
14 typedef long long ll;
15 
16 ll inv[3000006];
17 int main()
18 {
19     ll a, p;
20     while(~scanf("%lld %lld", &a, &p) )
21     {
22         memset(inv, 0, sizeof(inv));
23         inv[0] = 1;
24         inv[1] = 1;
25         for(ll i=2; i<=a; i++ )
26             inv[i] = (p-p/i)*inv[p%i]%p;
27         for(ll i=1; i<=a; i++ )
28             printf("%lld\n", inv[i]);
29     }
30     return 0;
31 }

模板二:

拓展欧几里得算法

这道题我用这个方法TLE了,这个次慢

 1 #include 
 2 #include 
 3 #include 
 4 #include <string>
 5 #include 
 6 #include 
 7 #include 
 8 #include 
 9 #include 
10 #include 
11 #include 
12 #include 
13 using namespace std;
14 typedef long long ll;
15 
16 void exgcd(ll a, ll b, ll &x, ll &y)
17 {
18     if( b==0 )
19     {
20         x=1;
21         y=0;
22         return ;
23     }
24     exgcd(b, a%b, y, x);
25     y -= (a/b)*x;
26     return ;
27 }
28 
29 int main()
30 {
31     ll a, p;
32     ll x, y;
33     while( ~ scanf("%lld %lld", &a, &p) )
34     {
35         for(ll i=1; i<=a; i++ )
36         {
37             exgcd(i, p, x, y);
38             x = (x%p+p)%p;
39             printf("%lld\n", x);
40         }
41     }
42     return 0;
43 }

模板三:

费马小定理算法:(快速幂(a,p-2,p)),前提是a p互质,这个也TLE了,这个最慢

 1 #include 
 2 #include 
 3 #include 
 4 #include <string>
 5 #include 
 6 #include 
 7 #include 
 8 #include 
 9 #include 
10 #include 
11 #include 
12 #include 
13 using namespace std;
14 typedef long long ll;
15 
16 ll fpm(ll x, ll power, ll mod)
17 {
18     ll ans = 1;
19     for( ; power; power>>=1,(x*=x)%=mod )
20     if( power&1 )
21         (ans*=x)%=mod;
22     return ans;
23 }
24 
25 int main()
26 {
27     ll a, p;
28     ll x, y;
29     while( ~ scanf("%lld %lld", &a, &p) )
30     {
31         for(ll i=1; i<=a; i++ )
32         {
33             printf("%lld\n", fpm(i,p-2,p));
34         }
35     }
36     return 0;
37 }

 

转载于:https://www.cnblogs.com/wsy107316/p/11520602.html

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