初级数论1:(扩展)欧几里得算法

初级数论第一节:欧几里得算法,扩展欧几里得算法,例题。

欧几里得算法

先来讲一下欧几里得算法

欧几里得算法是可以在 O(logn) 时间内求解两数最大公约数的算法,简称 gcd

代码如下:

int gcd (int a, int b)
{
    if (b == 0) return a;
    return gcd (b, a % b);
}

证明一下这个算法的正确性。

b=0 的时候 a,b 的最大公约数就是 a,这个是对的。

现在需要证明 gcd(a,b)=gcd(b,a%b),可以再简化一点,证明 gcd(a,b)=gcd(b,ab)

因为a 一直减 b 直到 a<b 最后得到的结果就是 a%b

证明

gcd(a,b)=x,那么 a|x,b|x

a=cx,b=dxa>b

那么 ab=(cd)×xab 也是 x 的倍数。

所以 gcd(a,b)=gcd(b,ab)

时间复杂度证明

a>b

如果 a>2b,那么迭代一次之后 a 会缩小到 b 以内,减半。

如果 b<a<2b,那么第一次迭代之后变成 b,a%b,此时有三种情况:

a%b<b2,那么下一轮 b%(a%b) 小于 a%b,即小于 b2

a%b>b2,那么下一轮 b%(a%b) 就会等于 b(a%b),因为 a%b>b2所以 b(a%b)<b2

a%b=b2,直接成 0 了。

每一轮都减半, logn 次就成 0 了。

拓展知识:

1.gcd(a,b) 一般跑不满 logn,只有相邻的斐波那契数列会跑满。

证明:

假设斐波那契数列的相邻两项为 a,b,那么a<b<2a,取余相当于减。

而相减就会变成斐波那契数列的上一项,

所以用欧几里得算法求斐波那契数列的第 x 项和第 x+1 项的最大公约数。

时间复杂度就是 O(x),而斐波那契数列的增长是 2n 不到一点,基本跑满 O(logn)

2.gcd(F[n],F[m])=F[gcd(n,m)],这个在算法竞赛中很有用。

其中,F[n] 指的是斐波那契数列的第 n

证明过程太长,此处省略 500 字。

例题:LuoguP1306 斐波那契公约数

扩展欧几里得算法

扩展欧几里得算法是可以在 O(logn) 的时间内求方程 ax+by=gcd(a,b) 的整数解。

简称扩欧,别名 exgcd

其中,a,b 已知,gcd(a,b) 可以在算的时候求。

扩展欧几里得算法是用小的解算出大的解然后回退。

终止答案

欧几里得算法的终结条件是 b=0b 最后一定会等于 0

所以我们从 b=0 的情况入手。

ax+0×y=gcd(a,0)x1y 按理来说取任何数都行。

但是取太大最后会爆 longlong,所以取 0

回退

先来看一下欧几里得算法,gcd(a,b)可以怎么求?

我们只要知道了 gcd(b,a%b) 就可以求得 gcd(a,b),因为 gcd(a,b)=gcd(b,a%b)

扩展欧几里得算法也是这样,要想求 ax+by=gcd(a,b) 的整数解,

可以先求 bx+(a%b)×y=gcd(b,a%b) 的整数解,然后回退得到 ax+by=gcd(a,b) 的解。

我们先假设已经求得了 bx+(a%b)×y=gcd(b,a%b) 的解,这是必然的。

因为最后 b 会等于 0,就会得到一组解,然后慢慢回退。

解的变形

首先可以发现 gcd(b,a%b)=gcd(a,b)

我们现在就知道了 bx+(a%b)×y=gcd(a,b)。尝试拆开拼出 ax+by

其次,a%b 可以写成 aab×b

代入进去变成 bx+(aab×b)×y=gcd(a,b)

拆开变成 bx+a×yab×b×y=gcd(a,b)

然后把和 a 有关的,还有和 b 有关的提出来。

得到 ay+b×(x+ab×y)=gcd(a,b)

然后我们发现这个方程不就是 ax+by 的形式吗?

x 就等于 yy 稍麻烦一点,等于 x+ab×y

然后就可以回退到上一层求解了。

时间复杂度证明

不难发现,exgcd 的时间复杂度只和 a,b 有关,

并且 a,b 每一次还是变成 b,a%b,所以时间复杂度和 gcd 一样。

代码:

复制代码
void exgcd (int a, int b)
{
    if (b == 0)//b=0 的情况答案可以直接得到
    {
        x = 1;
        y = 0;
        return;
    }
    exgcd (b, a%b)//否则先算 exgcd(b, a % b) 的解。
    int t = x;//现在的 x, y 是 bx + (a % b)y = gcd(a, b) 的解。
    x = y;//刚刚证明了 ax + by = gcd(a, b) 的解 x 就是上一个方程的解 y
    y = t - a / b * y;//x 已被更改,所以用临时变量 t 存储上一个方程的解 x
}
复制代码

拓展:一般形式

题目里很少有要求 ax+by=gcd(a,b) 的整数解,一般都是求 ax+by=c 的整数解。

这时该怎么办呢?

如果 gcd(a,b)|c,那么我们在等式两边同乘以 cgcd(a,b) 即可得到整数解。

否则,可以证明,该方程无整数解。

例题:

1.iai 六星题 两个闹钟

2.iai 五星级挑战 无尽的循环

两个闹钟

题意已经很明显了,就是一个扩欧。

设两个整数 x,y,使得 ax+n=by+m

整理得 axby=mn

y=y,代入得 ax+by=mn

其中,mna,b 都是已知的,这不就是扩欧吗?

解出来之后,ax+n 或者 by+m 都是题目要求的答案。

但是这个解有可能是负的,所以得尝试把他变成正的。

可以发现两个闹钟第一次同时响如果在 t1 时刻,那么下一次响肯定是在 t1+lcm(a,b) 时刻啦!

那么就一直加 lcm(a,b) 直到都变成正的就好了。

但是会超时,用一个除法就可以了。

无尽的循环

这一道题是需要变形一下的,大家可以自己先尝试一下。

另外,注意开个 longlong

简化题意

给定 a,b,c,k,求一个整数 d,使得 a+dcb (mod 2k)

然后把同余去了,变成 a+dc=b+e×2k

e 移到左边,b 移到右边,得 dce×2k=ba

其中, c,2k 已知,右边的 ba 也是已知的。

a=c,x=d,b=2k,y=e,c=ba,这就是一个典型的扩欧式子。ax+by=c

最后 x 就是要求的答案,然后还需要化简一下,和两个闹钟差不多,大家可以自己去研究研究。

 

下节预告:中国剩余定理 CRT 和扩展中国剩余定理 EXCRT。什么时候有空就更。

posted @   Xy_top  阅读(150)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示