模运算与基本四则运算有些相似,但是除法例外。其规则如下:
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
(a^b) % p = ((a % p)^b) % p
推论:
若a≡b (% p),则对于任意的c,都有(a + c) ≡ (b + c) (%p);
若a≡b (% p),则对于任意的c,都有(a * c) ≡ (b * c) (%p);
若a≡b (% p),c≡d (% p),则 (a + c) ≡ (b + d) (%p),(a - c) ≡ (b - d) (%p),
(a * c) ≡ (b * d) (%p),(a / c) ≡ (b / d) (%p);
对于(a÷b)%p,如果存在b1,这个b1满足的条件是,b * b1%p=1。。这样,将式子(a÷b)%p乘上b * b1%p,结果不变,式子变为(a * b1)%p。这样就将除法转换为乘法计算。这样的b1称作b模p的逆元。
怎样求逆元呢?
1.费马小定理
如果p为素数,且gcd(b,p)=1,那么就有bp-1 = 1 (modP),即b * bp-2 =1(modP),则bp-2 即为b模P的逆元。用快速幂可以实现。
费马小定理
2.扩展欧几里得求逆元
原理解释:
扩展欧几里得算法:计算乘法逆元
注意,模数P应该取比要求逆元的数大的素数,这样可以保证P与该数互质,只有满足互质前提,扩展欧几里得算法得到的x才是逆元。超全超实用的素数表
扩展欧几里得求逆元计算组合数:
public class Main {
static InputReader sc=new InputReader(System.in);
static int maxn=100010,p=1000000007;
static long f[]=new long[maxn];
public static void main(String args[]) {
PrintWriter out=new PrintWriter(System.out);
f[0]=1;
for(int i=1;i<maxn;i++) {
f[i]=f[i-1]*i%p;
}
out.println(C(b,a)%p);//C(n,m),n>=m.
}
static long C(int a,int b) {
long A=f[b];
long B=f[b-a]*f[a]%p;
long C=mod_reverse(B, p);
return A*C%p;
}
static class LLong{
long v=0;
}
static long extend_gcd(long a,long b,LLong x,LLong y)
{
if(a==0&&b==0) return -1;//无最大公约数
if(b==0){x.v=1;y.v=0;return a;}
long d=extend_gcd(b,a%b,y,x);
y.v-=a/b*x.v;
return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
static long mod_reverse(long a,long n)
{
LLong x=new LLong(),y=new LLong();
long d=extend_gcd(a,n,x,y);
if(d==1) return (x.v%n+n)%n;
else return -1;
}
}
扩展欧几里得算法求逆元
#include <bits/stdc++.h>
using namespace std;
void ExPower( int b, int p, int & a, int & k ) {
if( p == 0 ) {
a = 1; k = 0;
return;
}
ExPower( p, b % p, k, a );
k -= b / p * a;
return;
}
int main() {
int b, p;
cin >> b >> p;
int a, k;
ExPower( b, p, a, k );
if( a < 0 ) a += p;
cout << a << endl;
return 0;
}