扩展欧几里德算法(递归及非递归实现c++版)
今天终于弄懂了扩展欧几里德算法,有了自己的理解,觉得很神奇,就想着写一篇博客。
在介绍扩展欧几里德算法之前,我们先来回顾一下欧几里德算法。
欧几里德算法(辗转相除法):
辗转相除法求最大公约数,高中就学了,但当时知其然不知其所以然,直到大学才真正理解它的精髓。
理解辗转相除,关键在于理解 gcd(a,b)==gcd(b,a%b)
那么怎么去理解呢?下面是我的理解:
首先对于非负整数a,b,一定可以写成 a=k*b+r(r<b) 的形式
令 g=gcd(a,b) ,则有 g|a ,即 g|(k*b+r)
又 g|b ,所以 g|r
当然,只证得 g 同时整除 b 和 r 依然不能得出 g==gcd(b,r),还可能是gcd(b,r)>g 。
于是我们假设存在 g‘>g(故g'不可能是 a,b 的公约数)
使得 g'|b 且 g'|r
则有 g'|(k*b+r) 即 g'|a
→ g' 是 a,b 的公约数,矛盾。
故 gcd(a,b)==gcd(b,r)(其中 r==a%b) 得证。
下面贴出欧几里德算法的递归和非递归代码:
//递归欧几里德算法
int gcd(int a,int b)
{
return b==?a:gcd(b,a%b);
}
//非递归欧几里德算法
int gcd(int a,int b)
{
int t;
while(b){
t=b;
b=a%b;
a=t;
}
return a;
}
值得注意的是:
1、任意正整数和 0 的最大公约数为正整数本身。
2、即使参数 a<b ,经过一次递归或循环之后,a,b 会自动交换,故开头不需要加 if(a<b)swap(a,b); 语句。
扩展欧几里德算法:
扩展欧几里德算法其实就是为了求出 ax+by=gcd(a,b) 的一个整数解 (x0,y0) ,然后就能得出全部整数解为 (x0+k*b,y0-k*a) 。
递归实现
扩展欧几里德算法的递归实现是基于递归求gcd的过程,它会在gcd函数递归完返回的时候逐步解出一个整数解(x,y) 。
让我们来简单模拟一下 gcd(a,b) 递归返回的过程(数学归纳法得出递推公式)
①在gcd递归到最深处(倒是第一层)的时候,即此时 b0==0 ,可得出方程 a0x+b0y=gcd(a,b) (gcd(a,b)==gcd(a0,b0)==gcd(a1,b1)==…==a0)的一组整数解为x0=1,y0=0。
②假设返回到倒数第 k 层时方程 akx+bky=gcd(a,b) 的一组整数解为 xk , yk
又设返回到倒数第 k+1 层时方程 ak+1x+bk+1y=gcd(a,b) 的一组整数解为 xk+1 , yk+1
则有 akxk+bkyk=ak+1xk+1+bk+1yk+1 其中 ak==bk+1 , bk=ak+1%bk+1
不妨令 xk+1=yk yk+1=xk-ak+1/bk+1*yk
下面贴出递归代码:
//递归扩展欧几里得算法
int exgcd(int a,int b,int&x,int&y)
{
if(b==){
x=,y=;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
非递归实现
个人感觉扩展欧几里德算法的非递归方式理解起来更为简单。
我们就从一个具体的例子入手吧。
若 a=30 , b=47 , 则 gcd(a , b)=1,那么就是要求解一次不定方程 30x+47y=1 的整数解。
由已知,我们有
观察上述矩阵,第三列的元素变换过程恰好是辗转相除法求最大公约数的过程。
他们的每一步的变换形式(行变换)可用如下通式表示:
当第 2 行 3 列的元素为 0 时,第 1 行 3 列的元素即为 gcd(a , b)。
故最终能得出一组整数解 (x , y)=(11 , -7) 满足 30x+47y=1 。
于是我们可以在非递归欧几里德算法的基础上写出非递归的扩展欧几里德算法,代码如下:
//非递归扩展欧几里得算法
int exgcd(int a,int b,int&x,int&y)
{
int m=,n=,t;
x=,y=;
while(b){
t=m,m=x-a/b*m,x=t;
t=n,n=y-a/b*n,y=t;
t=b,b=a%b,a=t;
}
return a;
}
写到这里算是终于写完了。
这是我写的第一篇博客,本来旨在用最简洁的语言描述最核心的思想,但貌似……可能……大概……与预期有点不符...
我希望通过写博客记录一下自己的想法,即使日后忘记也能一看便知。当然,如果能帮助别人深入理解,那就再好不过了。
最后,如果各位发现文中的错误、有什么建议或者有一些自己的想法,都欢迎在评论区提出。
扩展欧几里德算法(递归及非递归实现c++版)的更多相关文章
- 汉诺塔算法的递归与非递归的C以及C++源代码
汉诺塔(又称河内塔)问题其实是印度的一个古老的传说. 开天辟地的神勃拉玛(和中国的盘古差不多的神吧)在一个庙里留下了三根金刚石的棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一 个小, ...
- 回溯算法 DFS深度优先搜索 (递归与非递归实现)
回溯法是一种选优搜索法(试探法),被称为通用的解题方法,这种方法适用于解一些组合数相当大的问题.通过剪枝(约束+限界)可以大幅减少解决问题的计算量(搜索量). 基本思想 将n元问题P的状态空间E表示成 ...
- C#实现(递归和非递归)高速排序和简单排序等一系列排序算法
本人由于近期工作用到了一些排序算法.就把几个简单的排序算法.想冒泡排序,选择排序,插入排序.奇偶排序和高速排序等整理了出来,代码用C#代码实现,而且通过了測试.希望能给大家提供參考. ...
- 汉诺塔算法c++源代码(递归与非递归)[转]
算法介绍: 其实算法非常简单,当盘子的个数为n时,移动的次数应等于2^n - 1(有兴趣的可以自己证明试试看).后来一位美国学者发现一种出人意料的简单方法,只要轮流进行两步操作就可以了.首先把三根柱 ...
- 欧几里德与扩展欧几里德算法 Extended Euclidean algorithm
欧几里德算法 欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数. 基本算法:设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd( ...
- ACM_扩展欧几里德算法
<pre name="code" class="cpp">/* 扩展欧几里德算法 基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表 ...
- C语言实现 二分查找数组中的Key值(递归和非递归)
基本问题:使用二分查找的方式,对数组内的值进行匹配,如果成功,返回其下标,否则返回 -1.请使用递归和非递归两种方法说明. 非递归代码如下: #include <stdio.h> int ...
- poj2115-C Looooops(扩展欧几里德算法)
本题和poj1061青蛙问题同属一类,都运用到扩展欧几里德算法,可以参考poj1061,解题思路步骤基本都一样.一,题意: 对于for(i=A ; i!=B ;i+=C)循环语句,问在k位存储系统中循 ...
- poj1061-青蛙的约会(扩展欧几里德算法)
一,题意: 两个青蛙在赤道上跳跃,走环路.起始位置分别为x,y. 每次跳跃距离分别为m,n.赤道长度为L.两青蛙跳跃方向与次数相同的情况下, 问两青蛙是否有方法跳跃到同一点.输出最少跳跃次数.二,思路 ...
随机推荐
- Python 爬虫从入门到进阶之路(十六)
之前的文章我们介绍了几种可以爬取网站信息的模块,并根据这些模块爬取了<糗事百科>的糗百内容,本章我们来看一下用于专门爬取网站信息的框架 Scrapy. Scrapy是用纯Python实现一 ...
- python 中的__name__ == "__main__"(转)
有句话经典的概括了这段代码的意义: “Make a script both importable and executable” 意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可 ...
- 什么是Task
什么是Task Task是.Net4.0新增用来处理异步编程的,叫做基于“任务编程模型”,任务其实是架构在线程之上的,具体操作的时候还是由线程去执行的,任务的管控有点类似于线程池,程序中开10个Tas ...
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- python接口自动化(三十四)-封装与调用--函数和参数化(详解)
简介 前面虽然实现了参数的关联,但是那种只是记流水账的完成功能,不便于维护,也没什么可读性,随着水平和技能的提升,再返回头去看前边写的代码,简直是惨不忍睹那样的代码是初级入门的代码水平都达不到.接下来 ...
- 最新 centos7下zabbix 监控redis状态
准备 先准备两台服务器,已经部署好了zabbix-sever和zabbix-agent zabbix-server 192.168.1.70 centos7.2 zabbix-agent 192.16 ...
- VS2012 BIDS之Reporting Service/SSRS 项目
最近资讯部门需要:网页/sharepoint提供事实表RawData下载功能,以下是项目大概: 工具:VS(BIDS)Report Designer+Reporting Service配置管理器
- springboot-多模块构建
1. 场景描述 先介绍下背景,项目为什么需要用多模块?springmvc难道还不够? (1)设计模式真言:"高内聚.低耦合",springmvc项目,一般会把项目分成多个包:con ...
- Git更改提交
提交记录我们的工作历史记录,提交自身是一成不变的.Git提供了几个工具和命令,抓门用来帮助修改完善版本库中的提交. 实际工作中存在很多情况需要我们去修改或返工某个提交或者整个提交序列: 1,可以在某个 ...
- NameNode和SecondaryNameNode的工作机制
NameNode&Secondary NameNode 工作机制 NameNode: 1.启动时,加载编辑日志和镜像文件到内存 2.当客户端对元数据进行增删改,请求NameNode 3.Nam ...