这两个算法能够说是OI里数学模块最重要的基础了(若是位运算不算数学的话)。html
一.欧几里得算法(Euclidean Algorithm)算法
模板水题:LOJ P1212 (LOJ真是个好东西啊)ide
在学习一种算法前,我认为咱们首先应该知道,这种算法是要解决什么问题的。函数
小学就已经学过了两个数的最大公约数,而欧几里得算法就是为了求出两个数a、b的最大公约数的,这个最大公约数能够表示为gcd(a,b)。学习
欧几里得算法又称展转相除法,这个名字已经揭示了它的主要思想:展转相除!idea
它的函数代码只有一行,简单便捷,复杂度O(log n):spa
int gcd(int a,int b){ return b?gcd(b,a%b):a; }
不要小看这短短的一行代码,其中蕴含了无尽的人生智慧(/滑稽)code
由于代码很短,因此算法的过程我就不赘述了,主要就是递归,能够从代码中看出来。htm
若是赶时间(好比距离noip就差一天了),能够忽略掉证实只记代码,可是我认为证实仍是必要的,证实在这里:证实blog
二.扩展欧几里得算法
首先咱们要理解一个定理:
贝祖定理:若存在a、b是整数,则必存在整数x、y,知足ax+by=gcd(a,b)。
证实在这里写得比较清楚:证实
须要耐心理解。
贝祖定理
证实:
当 b=0 时,gcd(a,b)=a,此时 x=1 , y=0
当 b!=0 时,
设 ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2
又因 a%b=a-a/b*b
则 ax1+by1=bx2+(a-a/b*b)y2
ax1+by1=bx2+ay2-a/b*by2
=ay2+bx2-b*a/b*y2
=ay2+b(x2-a/b*y2)
解得 x1=y2 , y1=x2-a/b*y2
由于当 b=0 时存在 x , y 为最后一组解
而每一组的解可根据后一组获得
因此第一组的解 x , y 必然存在
证毕。
显然,由于当b=0时,x=1,y=0,这时x和y是已知的,因此咱们很容易想到经过递归来求解。
不断返回下一层的解,来获得这一层的解,最终回溯回来,得解。
由于是借助欧几里得算法进行回溯的,因此复杂度也是O(log n)。
基本理解扩展欧几里得算法后,咱们就能够来看看例题了。
例题:洛谷oj P1082
同余定理:给定一个正整数$m$,若是两个整数$a$和$b$知足$a-b$可以被$m$整除,即$(a-b)/m$获得一个整数,那么就称整数$a$与$b$对模$m$同余,记做$a\ ≡\ b\ (\ mod \ m \ )$。对模$m$同余是整数的一个等价关系。
其实就是$a\ mod\ m\ =\ b\ mod\ m$。
当$b>=1$时,由于$1\ mod\ b\ =\ 1$,因此$ax\ ≡\ 1(mod b)$就是$ax mod b=1$。其实就差很少是方程$ax\ +by\ =\ 1$,y可能为负数,因此咱们在作exgcd后还要加个答案处理。
ac代码:
#include <cstdio> using namespace std; long long a,b; long long x,y; inline void exgcd(long long a,long long b){ if (b==0){ x=1,y=0; return ; } else exgcd(b,a%b); long long x1=x; x=y,y=x1-a/b*y; return ; } int main(){ scanf("%lld%lld",&a,&b); exgcd(a,b); while (x<0) x+=b; x%=b; printf("%lld",x); return 0; }