数论入门基础
最大公约数
问题1
给定平面上的两个格点
P1=(x1,y1)P2=(x2,y2)求线段P1P2上有几个格点
−109<=x1,x2,y1,y2<=109
解法1
枚举所有的合法的
判断 (x,y) 是否在线段上
数据范围大,我们需要更快的算法
解法2
假设我们从(x1,y1)出发已经找到了离它最近的一个格点P0,那么从P0出发走相同长度肯定也是一个整数格点,就这样一路找下去,最终恰好能找到(x2, y2),中间的每一段都是等长的,你可以假设最终到的点不是(x2,y2),而是延伸出去的某个点,那么显然P0就不是离(x1, y1)最近的一个格点了。
所以我们证明了一个性质
线段上的所有的整数格点恰好都在这条线段的K等分点上
要满足等分点都是整数,所以K必须为X距离和Y距离的公约数,我们需要求最大的K,所以就是两个距离的最大公约数了
求最大公约数的欧几里德算法
- 更相减损术
- 辗转相除法
这两种方法是一样的,下面是上面的优化
int gcd(int a, int b) {
while(a != b) {
if (a > b) {
a -= b;
} else {
b -= a;
}
}
return a;
}
int gcd(int a, int b) {
if(b == 0) return a;
return gcd(b, a % b);
}
为什么这样子做是对的呢?
假设 g 为a b的某个公约数,那么对于任意一个g都有如下两个式子
a b更相减损,变的只是前面的k,无论怎么减g还是他们的约数,一直到最后两个数变成相同的,g还是他们的约数
在更相减损的过程当中,所有的公约数信息都没有丢,也不会增加,跟原来一样,但是数据变小了哦,那么一直变小会发生什么呢?
对于任意一个公约数g都满足最后变成的两个相等的数为g的倍数。所以辗转相减之后的数也是最大公约数 gmx 的倍数,假设最后得到的两个数是
既然所有的公约数信息都没丢,这两个数的公约数也是原来a b的公约数,那么 k1,k2 的值必定为1,不然就存在某个比g大的公约数了,与g是最大公约数矛盾。
其实上面的辗转相减法,每次就是最大公约数的两个系数k1, k2在辗转相减,最终变成1(他们的最大公约数是1),这个辗转相减的过程可以描述为
所以这个方程其实一定有解,因为刚才已经证明k1 k2辗转相减一定能得到他们的最大公约数1。
扩展欧几里得
问题
一个游戏
有向前向后无线延续的格子, 每个格子都写有整数。其中0号格子是起点, 1号格子是终点。 而骰子上只有a,b,-a,-b四个整数,所以根据a和b的值得不同,有可能无法到达终点。掷出四个整数各多少次可以到达终点?
解法
上述问题归结起来就是
如果
实际上一定存在整数对(x, y)使得
假设已经求得了
1: b∗x′+(a%b)y′=gcd(a,b)
的整数解
(x′,y′)
将
代入可得
化简一下
2: a∗y′+b∗(x′−(a/b)∗y′)=gcd(a,b)
所以,解出了1式,就可以由2式得到原方程的解
特殊的,当b = 0时,
下面的代码解决 a∗x+b∗y=gcd(a,b)
int extgcd(int a, int b, int &x, int &y) {
int d = a;
if(b != 0) {
d = extgcd(b, a % b, x, y);
x -= (a / b) * y;
std::swap(x, y);
} else {
x = 1; y = 0; // g x+ 0 * y = g, y can be any number
}
return d;
}
递归算到最后y其实可以取任意整数。但是取的太大容易导致使得最后算出来的解爆int之类的事情
下面我们来看一下最终算出来的解
x0
y0
的绝对值大小情况
由x y的计算方法x -= (a / b) * y可以知道,x y始终是在max(|a|, |b|)的绝对值范围内的,因此a*x + b*y = gcd(a, b)最后算出的x y的绝对值大小跟a b是同一个级别的。
扩展欧几里德其实就是欧几里德,欧几里德其实就是辗转相减,全是一样的!
通解: