算法 递归 迭代 动态规划 斐波那契数列 MD
| Markdown版本笔记 | 我的GitHub首页 | 我的博客 | 我的微信 | 我的邮箱 |
|---|---|---|---|---|
| MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
算法 递归 迭代 动态规划 斐波那契数列 MD
目录
递归和迭代
什么是递归
递归的基本概念:程序调用自身的编程技巧称为递归
一个函数在其定义中直接或间接调用自身的一种方法,它通常把一个大型的复杂的问题转化为一个与原问题相似的规模较小的问题来解决,可以极大的减少代码量。
递归的能力在于用有限的语句来定义对象的无限集合.
由于递归引起一系列的函数调用,并且有可能会有一系列的重复计算,递归算法的执行效率相对较低。
递归的优劣
优点
- 大问题化为小问题,可以极大的减少代码量
- 用有限的语句来定义对象的无限集合
- 代码更简洁清晰,可读性更好
缺点
- 递归容易产生"栈溢出"错误(stack overflow)。因为需要同时保存成百上千个调用记录,所以递归非常耗费内存。
- 递归可能存在冗余计算。比如最典型斐波那契数列,计算第6个需要计算第4个和第5个;而计算第5个还需要计算第4个,所处会重复。迭代在这方面有绝对优势。
什么是迭代法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法,即一次性解决问题。
迭代法是一类利用递推公式或循环算法通过构造序列来求问题近似解的方法。
迭代算法是用计算机解决问题的一种基本方法,它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值,迭代法又分为精确迭代和近似迭代。比较典型的迭代法如二分法和牛顿迭代法属于近似迭代法。
递归和迭代的区别
知乎 上有很多人举了非常生动的例子来说明他们的区别,下面摘录一些比较经典的描述。
递归就是自己调用自己,自己包含自己。
迭代是将输出做为输入,再次进行处理。
递归过程中, 问题的规模在缩小,这样最终得到问题的解;
迭代是一种由远变近的逼近,问题的规模不见得缩小了,但是慢慢在调整接近答案。
递归——《盗梦空间》,不断下潜至底层并最终解决
迭代——《明日边缘》,不断回到同一个场景并优化解决
递归是一个树结构,每个分支都探究到最远,发现无法继续的时候往回走;
迭代是一个环结构,每次迭代都是一个圈,不会拉掉其中的某一步,然后不断循环;
迭代是更新变量的旧值
递归是在内部调用自身
迭代是循环结构,例如 for while 循环
递归是选择结构,例如 if else 调用自己
在数学上,递归强调的是,新的值与前面计算的好几个值有关系 F{n} =F{n-1} +F{n-2}
而迭代一般是只是 x{n+1} 与 x{n} 之间进行计算
迭代是逐渐逼近,用新值覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。
递归是将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。
动态规划
动态规划 Dynamic Programming,简称DP。通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不像搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。
动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。因此读者在学习时,除了要对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。我们也可以通过对若干有代表性的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计方法。
基本思想
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。
具体的动态规划算法多种多样,但它们具有相同的填表格式。
关键字:分解成若干个子问题,保存子问题的解,从子问题的解得到原问题的解
问题特征
动态规划常常适用于有最优子结构和重叠子问题性质的问题:
- 最优子结构:当
问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。 - 重叠子问题:在用递归算法自顶向下解问题时,
每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能通过查表以利用这些子问题的解。
适用条件
适用动态规划的问题必须满足最优化原理和无后效性。
最优化原理(最优子结构性质)
最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
个人理解:例如求最短路径问题,从起点到终点的最短路径,一定也是这条路径上任意一点到终点的最短路径。
无后效性
将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
个人理解:例如求最短路径问题,前 N 步的路径(所谓的以前阶段的状态)并不会对后续最优路径的选择产生影响,唯一对后续最优路径的选择产生影响的是当前所处的位置(所谓的状态)
子问题的重叠性
动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度(包括O(lgn)、O(n)、O(n^2)、O(n^3)等)的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。
斐波那契数列
1,1,2,3,5,8,13,21,34,55
耗时时间比较:
| n | 递归法 | 迭代法 | 动态规划 |
|---|---|---|---|
| 20 | 0-2 | 全部不超过 1 | 全部不超过 1 |
| 25 | 1-4 | 0 | 0 |
| 30 | 5-8 | 0 | 0 |
| 35 | 50 | 0 | 0 |
| 40 | 489 | 0 | 0 |
| 41 | 803 | 0 | 0 |
| 42 | 1323 | 0 | 0 |
| 43 | 2025 | 0 | 0 |
| 44 | 3293 | 0 | 0 |
| 45 | 5309 | 0 | 0 |
递归法实现
public static int recurFib(int n) {
if (n < 2) {
return n;
} else {
return recurFib(n - 1) + recurFib(n - 2);
}
}
迭代法实现
方式1:
public static int iterFib(int n) {
if (n < 2) {
return n;
} else {
int result = 0, a = 0, b = 1;
for (int i = 2; i <= n; i++) {
result = a + b; //每次都是最近的两个值的和
a = b;// 把最旧的值替换为第二旧的值
b = result; //把第二旧的值替换为最新的值
}
return result;
}
}
方式2:
public static int iterFib(int n) {
if (n < 2) {
return n;
} else {
int result = 0, a = 0, b = 1;
for (int i = 2; i <= n; i++) {
result = a + b; //每次都是最近的两个值的和
if (a < b) a = result; //较小的值是更旧的值,我们把最新的值替换为更旧的值,另一个值保持不变
else b = result;
}
return result;
}
}
动态规划实现
public static int dynFib(int n) {
int[] val = new int[n + 1];
if (n < 2) {
return n;
} else {
val[1] = 1;
for (int i = 2; i <= n; i++) {
val[i] = val[i - 1] + val[i - 2];
}
return val[n];
}
}
在使用动态规划实现时,我们用数组保留中间值,这些中间值即是动态规划定义中的小问题。
但需要注意的是,用动态规划解决斐波那契数列时可以不用数组,因为在计算某个位置上的数时只需要用到前两位的值,所以我们只需要动态的保留前两位的值即可。这样子的动态规划的实现就和迭代是一样的了,但在其他问题上可能是不一样的。
2018-12-9
算法 递归 迭代 动态规划 斐波那契数列 MD的更多相关文章
- Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列)
Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列) 一丶带参数的装饰器 def wrapper_out(pt): def wrapper(func): ...
- Count and Say,统计并输出,利用递归,和斐波那契数列原理一样。
问题描述:n=1,返回“1”:n=2,返回“11”:n=3,返回“21”:n=4,返回1211,.... 算法分析:和斐波那契数列道理差不多,都是后一个要依赖前一个元素.因此可以使用递归,也可以使用迭 ...
- Python 实现 动态规划 /斐波那契数列
1.斐波那契数列 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数 ...
- Python3基础 函数 递归 阶乘与斐波那契数列
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Java迭代实现斐波那契数列
剑指offer第九题Java实现 题目: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项. public class Test9 { public static void ...
- 算法导论-求(Fibonacci)斐波那契数列算法对比
目录 1.斐波那契数列(Fibonacci)介绍 2.朴素递归算法(Naive recursive algorithm) 3.朴素递归平方算法(Naive recursive squaring) 4 ...
- 【算法】Fibonacci(斐波那契数列)相关问题
一.列出Fibonacci数列的前N个数 using System; using System.Collections.Generic; using System.Linq; using System ...
- 《BI那点儿事》Microsoft 时序算法——验证神奇的斐波那契数列
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10 ...
- Python算法_三种斐波那契数列算法
斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为"兔子数列&qu ...
随机推荐
- R基础学习(一)-- 连接mysql数据库
测试环境:win10+RStudio (1)在Console加载两个插件 >install.packages('DBI') Installing package into ‘C:/Users/l ...
- 事件触发器-----dispatchEvent
不要被标题蒙蔽了,今天的重点不是论述事件触发器,而是说一下dispatchEvent这个东西.好了,先简单做个铺垫,dispatchEvent是作为高级浏览器(如chrome.Firfox等)的事件触 ...
- 面向企业级的开源WebGIS解决方案--MapGuide(对比分析)
在技术特点.功能.架构等方面,MapGuide与其他WebGIS产品有什么区别?本文主要从此角度来介绍MapGuide的特性,以供参考. 本人选择了比较熟悉的几款WebGIS产品:MapServ ...
- DELPHI - How to use opendialog1 for choosing a folder? TOpenDialog, TFileOpenDialog
DELPHI - How to use opendialog1 for choosing a folder? On Vista and up you can show a more modern lo ...
- js文件改变之后浏览器缓存问题怎么解决?
升级了js文件,很多页面都引用了这个文件,需要主动清除浏览器缓存才会生效,有没有什么办法可以不主动清除就可以? 修改文件名,加上版本号,或 xxx.js?v=0.101
- 机房收费系统——UML用例图
用例图(Use Case Diagram)是由软件需求分析到终于实现的第一步,说明的是谁要使用系统,以及他们使用该系统能够做些什么,是九种图里面最为基础且很重要的一张图. 用例图包含3方面内容 ...
- delphi下实现ribbon界面的方法(一)
http://www.cnblogs.com/shanmx/archive/2011/12/04/2275213.html
- 读取 android sys/下的信息
读取 android sys/下的信息 https://github.com/ruw/Internet-Services-projects/tree/master/OffloadPredictor/l ...
- Linux学习10-CentOS搭建nginx负载均衡环境
前言 当自己的web网站访问的人越来越多,一台服务器无法满足现有的业务时,此时会想到多加几台服务器来实现负载均衡. 网站的访问量越来越大,服务器的服务模式也得进行相应的升级,怎样将同一个域名的访问分散 ...
- 基于VHDL利用PS2键盘控制的电子密码锁设计
基于VHDL利用PS2键盘控制的密码锁设计 附件:下载地址 中文摘要 摘 要:现代社会,人们的安全意识正在不断提升.按键密码锁由于其具有方便性.低成本等特征,还是大有用武之地的.但是通常的按键密码锁开 ...