模运算
(从点点搬运过来)
1. 欧几里德算法——求最大公约数
递归:
// initially a>b>=0, return greatest common divisor
int gcd(int a,int b)
{
return b? gcd(b,a%b) : a;
}
非递归:
#include <algorithm>
int gcd(int a,int b)
{
while(a=a%b) { swap(a,b); }
return b;
}
2. 欧几里德扩展算法
定理:对不全为零的两个非负整数 a,b,其最大公约数为 gcd (a,b),则必存在两个整数 x , y ,使得 a*x+b*y=gcd (a,b)
算法简述:利用欧几里德算法的中间过程,不断将a,b值用b,a%b代替,直到a%b为零,则有x=1,y=0。
证明与实现:
1 . 假设a>b,当 b=0 ,则 x=1,y=0.
2 . a*x1 + b*y1 =gcd (a,b)
同理 b*x2 + (a%b)y2 =gcd (b,a%b)=gcd (a,b) 成立,且因为 (a%b)=a-b(a/b)
所以 x1=y2;
y1=x2-(a/b)*y2;
所以x1,y1可以从x2,y2的值推导出来。并且最后a%b必然可以达到零,可以退出递归。
递归:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(!b) return x = 1, y = 0, a;
int g = exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
lrj版:
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1000000;
ll ext_euclid(ll a, ll b, ll& d, ll& x,ll& y ) {
if(!b) { d = a; x = 1; y = 0;}
else { ext_euclid(b, a%b, d, y ,x); y -= x*(a/b);}
} // d=gcd(a,b);
扩展欧几里德的应用: 1> 求解不定方程。2> 求解同模方程。3> 求乘法逆元
1> a*x+b*y=m,有解的充要条件为m % gcd(a,b)==0
2> a*x = b (mod p)
3> a模b 的乘法逆元 。x*(1/a) mod b=x*(a’) mod b 等同于 a*a’=1(mod b) 当且仅当 gcd(a,b)=1。可转化为,求a*a’+b*b’=1,其中,a’是a模b的乘法逆元,b‘是b模a的乘法逆元。
求乘法的逆元:1>欧几里德 2>快速幂(p为质数) a^(p-2) * a = 1 (mod p)。故a^(p-2)为a的逆。
3. 欧拉函数
在数论中,对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。
φ函数的值
通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。
与欧拉定理、费马小定理的关系
对任何两个互质的正整数a, m, m>=2有
a^φ(m)≡1(mod m)
即欧拉定理
当m是质数p时,此式则为:
a^(p-1)≡1(mod m)
即费马小定理。
求phi[2]~phi[n]的值:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define maxn 100
int main()
{
int prime[maxn+1],phi[maxn+1],tot=0,pri[maxn+1];
int i,j;
prime[0]=prime[1]=0;
for(i = 2; i <= maxn; i ++) prime[i] = 1;
for(i = 2; i * i <= maxn; i ++)
if(prime[i])
for(j = i + i; j <= maxn; j += i) //if i is not so big.you can start from j = i * i
prime[j]=0;
//find all prime numbers
for(i = 1; i <=maxn; i ++)
phi[i]=i;
for(i = 2; i <= maxn; i ++)
if(prime[i])
for(j = i; j <= maxn; j += i)
phi[j] = phi[j] / i *(i - 1);
// /i firstly,or the number will overflow
for(i = 2; i <= maxn; i++) cout<<i<<" "<<phi[i]<<endl;
return 0;
}
中国剩余定理
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1000000;
ll ext_euclid(ll a, ll b, ll& d, ll& x,ll& y ) {
if(!b) { d = a; x = 1; y = 0;}
else { ext_euclid(b, a%b, d, y ,x); y -= x*(a/b);}
} // d=gcd(a,b);
//n , x=a[i](mod m[i]), 0<=i<n
ll china(int n,int *a,int *m)
{
ll M = 1, d, y, x = 0;
for( int i = 0; i < n; i++) {
M *= m[i];
}
//printf("M=%lld\n",M);
for( int i = 0; i < n; i++) {
ll w = M / m[i];
ext_euclid(m[i],w,d,d,y); //d,y.because relatively prime,gcd(w,m)=1 = d = original x;
cout<<"w = "<<w<<" y = " << y << endl;
x = (x + y*w*a[i]) % M;
}
return (x+M) % M;
}
int main() {
int n,a[maxn+1],m[maxn+1];
while(scanf("%d",&n),n) {
for(int i = 0; i < n; i ++) {
scanf("%d%d",&m[i],&a[i]);
}
;
cout<<"--> "<<china(n,a,m)<<endl;
}
return 0;
}