数论
欧几里德算法:
用于计算两个整数a,b的最大公约数。
基本算法:设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd(b,a%b)。算法的实现(递归算法):
- int gcd(int a,int b)
- {
- if(b==0)
- return a;
- else
- return gcd(b,a%b);
- }
扩展欧几里德算法:
证明:
设 a>b
1)显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;2)ab!=0 时
设 a*x1+b*y1=gcd(a,b);
b*x2+(a mod b)*y2=gcd(b,a mod b);
根据欧几里德原理:gcd(a,b)=gcd(b,a mod b);
则:a*x1+b*y1=b*x2+(a mod b)*y2即:a*x1+b*y1=b*x2+(a-(a/b)*b)*y2
=a*y2+b*x2-(a/b)*b*y2
=a*y2+b*(x2-(a/b)*y2);
根据恒等定理得:x1=y2;
y1=x2-(a/b)*y2;
算法实现(递归代码):
- int exgcd(int a,int b,int &x,int &y)
- {
- int d,t;
- if(b==0)
- {
- x=1;
- y=0;
- return a;
- }
- r=exgcd(b,a%b,x,y);
- t=x;
- x=y;
- y=t-a/b*y;
- return d;
- }
#include<stdio.h>
int exgcd(int a, int b, int &x, int &y)
{
int d,tmp;
if (b==0)
{
x = 1;
y = 0;
return a;
}
d = exgcd(b,a%b,x,y);
tmp = x;
x = y;
y = tmp - a/b * y;
return d;
}
int main()
{
int a,b,x,y,k;
scanf("%d %d",&a,&b);
k=exgcd(a,b,x,y);
printf("%d %d %d\n",k,x,y);
return 0;
}
求解不定方程(扩展欧几里德算法):
x = x0 + b/Gcd(a,b) * t
y = y0 - a/Gcd(a,b) * t (其中t为任意整数)
找到a*x+b*y = Gcd(a,b)的一组解(x0,y0)后,
找到a*x+b*y=c的一组解(x1,y1)后,
a*x+b*y=c的其他整数解满足:
x = x1 + b/Gcd(a,b) * k
y = y1 - a/Gcd(a,b) * k (其中k为任意整数)
x 、y就是a*x+b*y=c的所有整数解。
用扩展欧几里得算法解不定方程ax+by=c;
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 bool linear_equation(int a,int b,int &x,int &y) 2 { 3 int d=exgcd(a,b,x,y); // 方程a*x+b*y=c 4 if(c%d) 5 return false; 6 int k=c/d; 7 x*=k; y*=k; //求得的只是其中一组解 8 return true; 9 }
同余
定义:用给定的正整数m分别除整数a、b,如果所得的余数相等,则称a、b对模m同余,记作a≡b(mod m)。
定理1:整数a,b对模m同余的充要条件是 a-b能被m整除(即m|a-b)。
推论:a≡b(mod m)的充要条件是a=mt+b(t为整数)。
定理2:同余关系具有反身性、对称性与传递性,即
1)a≡a (mod m);
2)若a≡b (mod m), 则b≡a (mod m);
3)若a≡b (mod m), b≡c (mod m),则a≡c (mod m).
定理3 若a≡b(mod m), c≡d (mod m),则
1)a+c≡b+d (mod m);
2)a-c≡b-d (mod m);
3)ac≡bd (mod m).
推论:若a≡b(mod m),n为自然数,则an≡bn (mod m)。
定理4:若ca≡cb(mod m), (c,m)=d (c,d最大公约数为d),且a,b为整数,则a≡b(mod m/d).
推论:若ca=cb(mod m), (c,m)=1 (c,d最大公约数为1,即互质),且a,b为整数,则a≡b(mod m).
定理5:若a≡b (mod m),a≡b (mod n),则a≡b(mod [m,n]).
推论若a≡b(mod mi), i=1,2,…,n,则a≡b (mod [m1,m2,..,mn]).
求解模线性方程(扩展欧几里德算法):
求解同余方程 ax≡b (mod n) 相当于求解方程 ax + ny = b, (x, y为整数)
当且仅当 gcd(a,n)|b时,同余方程ax≡b(mod n)对于未知数x有解。且方程有解时,方程有gcd(a,n)个解。
证明:
设d=gcd(a,n),假如整数x和y,满足ax+ny=d( 由扩展欧几里德得出)。
如果d|b时(即:b%d==0),则方程a*x0+ n*y0= d,方程两边乘以 b/d,得到:a*x0*b/d+ n*y0*b/d= b。
所以 x=x0*b/d,y=y0*b/d 为 ax+ ny= b 的一个解。(x0,y0为方程ax+ny=d=gcd(a,n)的一组解)
所以 x=x0*b/d 为 ax=b(mod n) 的解。
则同余方程ax≡b(mod n)的一个解(x0`)为:x0`=x0*(b/d)mod n。
例如:5x=4(mod3),解得x = 2,5,8,11,14.......由此可以发现一个规律,就是解的间隔是3.
我们设解之间的间隔为dx.
那么有:
a*x = b(mod n);
a*(x+dx) = b(mod n);
两式相减,得到:a*dx(mod n)= 0;
也就是说a*dx就是a的倍数,同时也是n的倍数,即a*dx是a 和 n的公倍数.为了求出dx,我们应该求出a 和 n的最小公倍数,此时对应的dx是最小的.
设a和n的最大公约数为d,那么a和n的最小公倍数为(a*n)/d.
即a*dx = a*n/d;
所以dx = n/d.
因此解之间的间隔就求出来了.
所以:方程的d个解分别为:xi=(x0`+i*(n/d))mod n {i=0,1,2 ... d-1}。
1 bool modular_linear_equation(int a,int b,int n)
2 {
3 int x,y,x1,i;
4 int d=exgcd(a,n,x,y);
5 if(b%d)
6 return false;
7 x1=x*(b/d)%n; //特解
8 for(i=1;i<d;i++)
9 printf("%d\n",(x1+i*(n/d))%n);
10 return true;
11 }
1 bool modular_linear_equation(int a,int b,int n) 2 { 3 int x,y,x1,i; 4 int d=exgcd(a,n,x,y); 5 if(b%d) 6 return false; 7 x1=x*(b/d)%n; //特解 8 for(i=1;i<d;i++) 9 printf("%d\n",(x1+i*(n/d))%n); 10 return true; 11 }
求模的逆元(欧几里德算法):
同余方程ax≡b (mod n),如果 gcd(a,n)== 1,则方程只有唯一解。
如果 b== 1,同余方程就是 ax=1 (mod n ),gcd(a,n)= 1。这时称求出的 x 为 a 的对模 n 乘法的逆元。
同余方程 ax= 1(mod n ), gcd(a,n)= 1 的求解就是求解方程ax+ ny= 1(x, y 为整数).
中国剩余定理(求解同与方程组)
问题:一个数被3除余1,被4除余2,被5除余4,这个数最小是几?
解法:x%3=1, x%4=2, x%5=4;
也就是求同余式组x≡1(mod3),x≡2(mod4),x≡4(mod5)的正整数解.
题中3、4、5三个数两两互质。则〔4,5〕=20;〔3,5〕=15;〔3,4〕=12;〔3,4,5〕=60。
使20被3除余1(1)(即(20*x1)%3=1),x1=2。用20×2=40;
使15被4除余1(2)(即(15*x2)%4=1),x2=3。用15×3=45;
使12被5除余1(4)(即(12*x3)%5=1),x3=3。用12×3=36;
//让余数为1,是为了要求余数4的话,只要乘以4就可以;要求余数为2的话,只要乘以2就可以……
然后,40×1+45×2+36×4=274,因为,274>60,所以,274-60×4=34,就是所求的数。
问题:
给定两两互质的正整数n1,n2,...,nk,要求找到最小的正整数a,满足a≡ai (mod ni) //ai 表示余数
算法步骤:
令n=n1n2...nk,mi=n/ni // n=60, m1=20, m2=15, m3=15
显然gcd(mi,ni)=1,解模线性方程,计算出xi满足mi*xi≡1 (mod ni) //(20*x1)%3=1 --> x1=2
a≡a1*x1*m1+a2*x2*m2+...+ak*xk*mk (mod n) // a1*x1*m1 (1*2*20)
算法实现:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int gcd(int a,int b,int &x,int &y)
{
int d,temp;
if(b==0) // 不定方程 a*x+b*y=gcd(a,b)=d;(x,y)为其一组整数解
{
x=1;
y=0;
return a;
}
d = gcd(b,a%b,x,y);
temp = x;
x = y;
y = temp - a/b * y;
return d;
}
int main()
{
int m,m1,r1,m2,r2,flag=0;
int a[11],b[11],T;
cin>>T;
while(T--)
{
int i,j;
int x,y,d,c,t;
cin>>m;
for(i=0;i<m;i++)
cin>>a[i];
for(i=0;i<m;i++)
cin>>b[i];
// x%m1=r1,x%m2=r2 ...
// x=m1*k1+r1,x=m2*k2+r2 ... (k1,k2 ... 为任意整数)
// m1*k1-m2*k2=r2-r1 ...
flag=0;
m1=a[0];r1=b[0];
for(i=1;i<m;i++)
{
m2=a[i];r2=b[i];
if(flag)
continue;
d=gcd(m1,m2,x,y); // 方程 x*m1+y*m2=d=gcd(m1,m2);
c=r2-r1;
if(c%d) //对于方程m1*x+m2*y=c=r2-r1,如果c不是d的倍数就无整数解
{
flag=1;
continue;
}
//对于方程m1*x+m2*y=c=r2-r1,若(x0,y0)是一组整数解,那么(x0+k*(m2/d),y0-k*(m1/d))也是一组整数解(k为任意整数)
//其中x0=x*(c/d),y0=y*(c/d); (x,y为方程m1*x+m2*y=d=gcd(a,b)的一组整数解)
t=m2/d;
x=(c/d*x+t)%t;
r1=m1*x+r1;
m1=m1*m2/d;
}
if(flag)
cout<<0<<endl;
else
cout<<r1<<endl;
}
return 0;
}