题目描述
求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解。
输入输出格式
输入格式:
输入只有一行,包含两个正整数 a, b,用一个空格隔开。
输出格式:
输出只有一行,包含一个正整数 x0,即最小正整数解。输入数据保证一定有解。
输入输出样例
输入样例#1:
3 10
输出样例#1:
7
说明
【数据范围】
对于 40%的数据,2 ≤b≤ 1,000;
对于 60%的数据,2 ≤b≤ 50,000,000;
对于 100%的数据,2 ≤a, b≤ 2,000,000,000。
NOIP 2012 提高组 第二天 第一题
这是一道数论的题目,首先先说下同余的概念同余的形式就是ax≡kmod(b)他的形式就可以转变为–>ax mod b = k这样子的形式。用集合论的语言,严格地来说就是:
对于整数集的任意一个子集Z,对于任意一个属于Z的元素n,n都除以m,得到的余数可以为0,1,2,…m-1,共m种。我们就以余数的大小作为标准,将Z分为m个互不相交的m个子集Z0,Z1,Z2,…Zm-1。
对于Zi的任意两个元素a,b,都关于m同余。记为
a≡b(mod m)。
我们解同余方程可以用到扩展欧几里得
大概的意思就是如下
int E_gcd(int a,int b,int &x,int &y){
int ret,temp;
if(!b){
x=1;y=0;
return a;
}
ret=E_gcd(b,a%b,x,y);
temp=x;
x=y;
y=temp-a/b*y;
return ret;
}
因为这里我们要对x,y本身进行修改来实现算法本身,所以在对函数本身定义的时候要加上取地址符来时限对x,y值的修改。
那么明白了扩展欧几里得算法本身之后代码本身就很好写了
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int a,b,x,y;
inline int gcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int r=gcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return r;
}
int main()
{
scanf("%d%d",&a,&b);
gcd(a,b,x,y);
printf("%d",(x+b)%b);
turn
0;
}
下面我们来说一下扩展欧几里得
概述
欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:
gcd函数就是用来求(a,b)的最大公约数的。
gcd函数的基本性质:
gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)
公式表述
gcd(a,b)=gcd(b,a mod b)
证明:a可以表示成a = kb + r,则r = a mod b
假设d是a,b的一个公约数,则有
d|a, d|b,而r = a – kb,因此d|r
因此d是(b,a mod b)的公约数
假设d 是(b,a mod b)的公约数,则
d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证
语言实现
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
扩展算法
对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然
存在整数对 x,y ,使得 gcd(a,b)=ax+by。
语言实现
int gcd(int a,int b,int &x,int &y){
if (b==0){
x=1,y=0;
return a;
}
int q=gcd(b,a%b,y,x);
y-=a/b*x;
return q;
}
求解 x,y的方法的理解
设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,a>b>0 时
设 ax1+ by1= gcd(a,b);
bx2+ (a mod b)y2= gcd(b,a mod b);
根据朴素的欧几里德原理有 gcd(a,b) = gcd(b,a mod b);
则:ax1+ by1= bx2+ (a mod b)y2;
即:ax1+ by1= bx2+ (a – [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);
根据恒等定理得:x1=y2; y1=x2- [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
扩展欧几里德算法
扩展欧几里德算法是用来在已知a, b求解一组x,y使得ax+by = Gcd(a, b) =d(解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。实现:
int exGcd(int a,int b,int &x,int &y)
把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。
{
if(b==0)
{
x=1;y=0;
return a;
}
int r=exGcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*y;
return r;
}
可以这样思考:
对于a’=b,b’=a%b 而言,我们求得 x, y使得 a’x+b’y=Gcd(a’,b’)
由于b’=a%b=a-a/b*b (注:这里的/是程序设计语言中的除法)
那么可以得到:
a’x+b’y=Gcd(a’,b’) ===>
bx+(a – a / b * b)y = Gcd(a’, b’) = Gcd(a, b) ===>
ay +b(x – a / b*y) = Gcd(a, b)
因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)
使用扩展欧几里德算法解决不定方程的办法
对于不定整数方程pa+qb=c,若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。
有种较为不严谨的方法证明,不过至少弥补了一点空白,望某些数论大师补充修改:
由于我们知道,存在一组x与y使得a*x+b*y=gcd(a,b)。
将等式两边同时乘以整数k,即a*x*k+b*y*k=gcd(a,b)*k。如果c mod gcd(a,b)=f,则0<=f