欧几里得(Euclid)与拓展的欧几里得算法
欧几里得(Euclid)与拓展的欧几里得算法
欧几里得算法
原理
欧几里得算法是一种快速计算最大公约数的算法,对于任意的两个数\((a,b)\),其最大公约数表示为\(gcd(a,b)\),根据欧几里得算法,\(gcd(a,b)=gcd(b,a\%b)\)。证明如下:
如果\(b>a\),显然成立;因此只需考虑\(b<a\)的情况。根据初等数学知识,可知\(a,b\)的关系可表示为\(a=qb+r\),其中\(q\)为商,\(r\)为余数。
对于\((a,b)\)的最大公约数\(g1=gcd(a,b)\),当然\(g1|a,g1|b\)(\(g1|a\)表示g1整除a),所以易知对于\(r=a-qb\),同样满足\(g1|r\);
又因为\(a\%b=r\),所以对于\(a,b\)的最大公约数g1,同样满足\(g1|a\%b,g1|b\),即\((b,a\%b)\)的最大公约数至少为\(g1\),即\(gcd(b,a\%b)>g1=gcd(a,b)\)。
反过来,对于\((b,a\%b)\)的最大公约数\(g2=gcd(b,a\%b)\),同样满足\(g2|a, g2|b\),即\(gcd(a,b)>g2=gcd(b,a\%b)\)。
因此\(gcd(a,b)=gcd(b,a\%b)\)证明成立。下面对该算法进行实现。
实现
#include <iostream>
using namespace std;
int euclid(int a, int b)
{
if (b!=0)
{
return euclid(b, a%b);
}
else
{
return a;
}
}
int main()
{
int a(0),b(0);
cin >> a >> b;
cout << euclid(a,b);
return 0;
}
拓展的欧几里得算法
原理
拓展的欧几里得算法在密码学中有着重要的应用,现给出定理:
对正整数a,b;总是存在一组整数X,Y,使得\(Xa+Yb=gcd(a,b)\)成立,且\(gcd(a,b)\)为满足这种条件的最小整数。
这里不对该定理进行证明,欧几里得算法给出了在已知a,b的情况下求\(gcd(a,b)\)的方法,但是如果想要求得X,Y的值,就要求助于拓展的欧几里得算法。怎么才能从欧几里得算法的计算过程当中得到我们想要求解的值呢?我们再次详细回顾欧几里得算法的求解过程。
对于已知整数\(a,b\),我们的算法求解过程如下:
\(a\) | \(b\) | 余数\(r\) | 商\(q\) |
---|---|---|---|
a | b | \(r1=a\%b\) | \(q1=a/b\) |
b | r1 | \(r2=b\%r1\) | \(q2=b/r1\) |
r1 | r2 | \(r3=r1\%r2\) | \(q3=r1/r2\) |
... | ... | ... | ... |
\(r_{n-1}\) | \(r_n\) | \(r_{n+1}=r_{n-1}\%r_n\) | \(q_{n+1}=r_{n-1}/r_n\) |
逐步计算,直到某一步出现\(r_{n-1}\%r_{n}=0\)的情况,这时候就找到了最大公约数,最大公约数即为\(r_n\),以上就是欧几里得算法的全过程。通过这个过程当中多产生的一些中间结果我们能不能求得\(X,Y\)的值呢?下面进行两种求解方法的推导。
递归求解
根据上面的表格我知道,\(Xa+Yb=gcd(a,b)\),并且对于中间所求解的每一步我们所得到的\(r_i\)都满足\(X_i\cdot r_i+Y_i\cdot r_{i+1}=gcd(a,b)\),因为很明显每一对\(r_i,r_{i+1}\)都满足最大公约数为\(gcd(a,b)\),这也是欧几里得算法的原理。
我们试着寻找\((X_i,Y_i),(X_{i+1},Y_{i+1})\)之间的递推关系,由以上阐述可知:
X_i\cdot r_i+Y_i\cdot r_{i+1}=gcd(a,b), &\text{(1)}\\
X_{i+1}\cdot r_{i+1}+Y_{i+1}\cdot r_{i+2}=gcd(a,b),&\text{(2)}
\end{cases}
\]
为了将上式转换成\(r_i\)的方程组,我们使用\(r_{i+1},r_{i+2}来表示r_i\),通过以上可知\(r_i=r_i/r_{i+1}\cdot r_{i+1}+r_{i+2}\),将该式带入上式(1),并将两式合并可得:
\]
进一步化简可得:
\]
根据系数相等的原则可得:
X_i\cdot r_i/r_{i+1}+Y_i=X_{i+1}, &\text{(1)}\\
X_i=Y_{i+1},&\text{(2)}
\end{cases}
\]
以上就得到了\((X_i,Y_i),(X_{i+1},Y_{i+1})\)之间的递推关系,那么我们接下来的工作就是找到一对可以求出其值的\((X_i,Y_i)\),通过以上可知当出现某一次计算使得\(r_{i}\%r_{i+1}=0\)时,我们可知对于\(X_i\cdot r_i+Y_i\cdot r_{i+1}=gcd(a,b)\),满足\(gcd(a,b)=r_{i+1}\),那么很显然\(X_i=0,Y_i=1\)。于是我们就得到了一对\((X_i,Y_i)\)的值,我们已经知道了最后一对\(r_{i},r_{i+1}\)所对应的\((X_i,Y_i)\)才能够推知前面的值,所以我们的推导时从后往前推的,因此我们将上面的递推关系稍微变换一下形式:
X_i=Y_{i+1},&\text{(1)}\\
Y_i=X_{i+1}-Y_{i+1}\cdot r_i/r_{i+1}, &\text{(2)}
\end{cases}
\]
此时我们就得到了推导关系和初值,通过计算我们就可以求得满足\(Xa+Yb=gcd(a,b)\)的\(X,Y\)值。下面通过代码对其进行实现:
#include <iostream>
using namespace std;
void extEuc(int a, int b, int&x, int&y)
{
int rx,ry;
int r(0);
if (a%b==0)
{
x=0;y=1;
return;
}
else
{
r = a%b;
extEuc(b, r, rx, ry);
x = ry;
y = rx - ry*a/b;
return;
}
}
int main()
{
int a,b;
int x,y;
cin >> a >> b;
extEuc(a, b, x, y);
cout << x <<' '<< y << endl;
return 0;
}
输入42 2017可求得输出为-48,1。
迭代求解
较多的递归调用可能会影响计算速度,所以我们接下来推一下迭代的计算方式,已知上面表格中所列欧几里得算法的计算步骤。已知\(gcd(a,b)\)是满足该集合的最小值\(\lbrace Xa+Yb | X,Y\in Z \rbrace\),已知对于每一步所产生的余数均能被\(gcd(a,b)\)整除,现在考虑每一步迭代所产生的余数满足的等式:
r_i=X_i\cdot a+Y_i\cdot b \\
r_{i+1}=X_{i+1}\cdot a + Y_{i+1}\cdot b
\end{cases}
\]
且已知\(r_i, r_{i+1}\)满足\(r_{i-1}=r_{i-1}/r_i\cdot r_i+r_{i+1}\),将上面两式代入到该式,可得:
\]
值得注意的是此处的\(X_i,Y_i\)与递归方法中的值含义不同。根据上式可推知以下递推关系:
X_{i+1}=X_{i-1}-r_{i-1}/r_i\cdot X_i \\
Y_{i+1}=Y_{i-1}-r_{i-1}/r_i\cdot Y_i
\end{cases}\]
已知中间的递推关系,关键是考虑如何判断循环的起始值和结束条件,对于\(a,b\)也可看做是余数\(r_i\),那么对于\(a,b\)来说,其满足的值为:
a = a\cdot 1 + b\cdot 0\\
b = a\cdot 0 + b\cdot 1
\end{cases}\]
所以就得到了两对\((X,Y)\)的值,分别为\((1,0),(0,1)\),并且已知\(r_i\)之间的递推关系为\(r_{i+1}=r_{i-1}\%r_i\)。我们也知道循环结束的条件为\(r_i=gcd(a,b)\),其最后的形式为\(Xa+Yb=gcd(a,b)\),其直接判断方式为\(r_{i-1}\%r_i=0\),然后我们就得到了最终的\(X,Y\)值,根据以上递推形式,我们有以下实现:
#include <iostream>
using namespace std;
int main()
{
int a,b;
int x1(1),y1(0),x2(0),y2(1);
int temp;
cin >> a >> b;
while (a%b!=0)
{
temp = x2;
x2 = x1 - a/b*x2;
x1 = temp;
temp = y2;
y2 = y1 - a/b*y2;
y1 = temp;
temp = a%b;
a = b;
b = temp;
}
cout << x2 <<' '<<y2<<endl;
return 0;
}
输入42 2017可求得输出为-48,1。
这里有一个用尽可能多的程序语言实现求逆元的网站,大家也可以参考这里的不同实现。
参考文献
[1] Katz J,Lindel Y.Introduction to Modern Cryptography—Principle and Protocol现代密码学——原理与协议【M】任伟.北京:国防工业出版社.2010:10-15.
欧几里得(Euclid)与拓展的欧几里得算法的更多相关文章
- 欧几里得 & 拓展欧几里得算法 解说 (Euclid & Extend- Euclid Algorithm)
欧几里得& 拓展欧几里得(Euclid & Extend-Euclid) 欧几里得算法(Euclid) 背景: 欧几里德算法又称辗转相除法.用于计算两个正整数a.b的最大公约数. -- ...
- 最大公约数与欧几里得(Euclid)算法
---恢复内容开始--- 记a, b的最大公约数为gcd(a, b).显然, gcd(a,b)=gcd(|a|,|b|). 计算最大公约数的Euclid算法基于下面定理: [GCD递归定理]对于任意非 ...
- Gym100812 L 扩展欧几里得
L. Knights without Fear and Reproach time limit per test 2.0 s memory limit per test 256 MB input st ...
- NOIP2012拓展欧几里得
拉板题,,,不说话 我之前是不是说过数据结构很烦,,,我想收回,,,今天开始的数论还要恶心,一早上听得头都晕了 先来一发欧几里得拓展裸 #include <cstdio> void gcd ...
- poj 1061 青蛙的约会 拓展欧几里得模板
// poj 1061 青蛙的约会 拓展欧几里得模板 // 注意进行exgcd时,保证a,b是正数,最后的答案如果是负数,要加上一个膜 #include <cstdio> #include ...
- bzoj4517: [Sdoi2016]排列计数--数学+拓展欧几里得
这道题是数学题,由题目可知,m个稳定数的取法是Cnm 然后剩下n-m本书,由于编号为i的书不能放在i位置,因此其方法数应由错排公式决定,即D(n-m) 错排公式:D[i]=(i-1)*(D[i-1]+ ...
- POJ 2891 Strange Way to Express Integers(拓展欧几里得)
Description Elina is reading a book written by Rujia Liu, which introduces a strange way to express ...
- POJ1061 青蛙的约会-拓展欧几里得
Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事 ...
- BZOJ-2242 计算器 快速幂+拓展欧几里得+BSGS(数论三合一)
污污污污 2242: [SDOI2011]计算器 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 2312 Solved: 917 [Submit][S ...
随机推荐
- RobotFramework_4.SeleniumLibrary操作(二)
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- vuex的实现
源代码可以查看我的github: https://github.com/Jasonwang911/TryHardEveryDay/tree/master/Vuex/vuex-resouce 欢迎s ...
- 再记一次经典Net程序的逆向过程
1.前言 上次发完,有网友问了一个问题:如果不绕过编译,而是直接编译怎么办? 记一次Net软件逆向的过程:https://www.cnblogs.com/dotnetcrazy/p/10142315. ...
- dubbo文档笔记
配置覆盖关系 以 timeout 为例,显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似: 方法级优先,接口级次之,全局配置再次之. 如果级别一样,则消费 ...
- c#小灶——注释和代码规范
为什么要写注释? 早上我写完了代码,我和我的朋友们都能看懂,到了晚上,我还能看懂,一周后,就只有上帝能看懂了…… 将来我们写的代码量是很大的,代码又不像我们自然语言这么好理解,可能过段时间我们就不认识 ...
- python_0基础开始_day05
第五节 一.字典 python的数据结构之一 字典 —— dict 定义:dic = {"key":"dajjlad"} 作用:存储数据,大量,将数据和数据起到 ...
- 【译】尝试使用Nullable Reference Types
随着.NET Core 3.0 Preview 7的发布,C#8.0已被认为是“功能完整”的.这意味着它们的最大亮点Nullable Reference Types,在行为方面也被锁定在.NET Co ...
- 深扒JVM,对它进行“开膛破肚”式解析!
1. 打怪升级,你绕不开JVM JVM,对Java程序员进阶而言,是一个绝对绕不开,也不能绕开的话题. 在你打怪升级.进阶蜕变的路上,势必会遇到项目上线中各种OOM.GC等问题,此时JVM的功底就至关 ...
- 冬天苹果笔记macbookpro消除静电的方法
冬天mac除静电的方法 1.mac虽然声称不需要关机,但和apple技术人员沟通后,还是需要1周进行一次关机操作 2.知乎上的回答:https://www.zhihu.com/question/195 ...
- intellIJ IDEA学习笔记3
intellij idea 的快捷鍵 https://blog.csdn.net/wei83523408/article/details/60472168 https://www.cnblogs.co ...