当前课程知识点:数据结构(上) > 第一章 绪论(下) > (xc)动态规划 > 01XC-1: 动态规划
欢迎回到数据结构的课堂
接下来的这一节
将重点讨论动态与规划
DSA设计与优化的
一种重要形式和手段
DSA的设计优化
既是一个复杂而艰辛的过程
同时也不乏规律可循
Kent Beck曾经就此
做过精辟的概括
他说我们整个的过程
也许应该分为三步
首先是让你的DSA能够运转起来
比如说 对小型的问题
至少Make it work
它能够工作
其次我们要使得它的
正确性得到保证
也就是make it right
那么联系上堂课所学的内容
这两步可以通过
递归的构思 以算法的形式
很好的解决
正如我们很快会看到的
递归 从效率上讲
并不能总是令我们满意
有的时候 会令我们非常不满意
所以在最后
如果我们要讲究它的效率
也就是要make it fast
我们也许应该用到上堂课
所讲的第二个方面
也就是迭代
从某种意义上讲
所谓的动态规划
也可以理解为 通过递归
找出了算法的本质
并且给出一个初步的解之后
再将其等效地转化为迭代的形式
我们的第一个例子是
大家都非常熟悉的Fibonacci数
具体来说 它是一个数列
在我们这里的习惯
从第0项开始 也就是认为
第0项和第1项 分别是0和1
此后的各项呢 按照这里的定义
都等于它的前一项
和再前一项 这两项的总和
所以这就是为什么0+1得1
1+1得2 1+2得3
2+3得5 以及3+5得8 诸如此类
那么这种形式本身
就是一个递归的形式
因此我们很自然地
将它转化为这样一个算法
来计算Fibonacci数的第n项
具体来说 也就是
只需判断一下这个n的大小
如果是0或1
我们就直接返回n
否则的话 按照刚才的定义
取出此前紧邻的两项
并且做一次加法
这个程序再直观再简明不过了
它确实是可以work
而且确实也正确 right
我们现在要来实际的
把它编译并且运行一下
看看它是否足够的快
这是我们这个课程
所提供的示例代码包
它是Visual Studio的一个Solution的形式
根据我们讨论和学习的主题不同
提供了50多个project
大家可以从课程网站上
自行下载并且解开
打开以后 是这样的一个形式
就目前我们这个问题而言
可以找到其中名为Fibonacci的project
我们可以看到其中
比如说刚说的这个算法
就可以描述为这样一段源程序
现在将它运行一次 执行它
效果还不错
这里还做了一些其它算法的对比
我们看到它们都非常棒
都很好地算出了
Fibonacci数的各项
但问题是 我们只计算到了
Fibonacci数的从0到第23项
这个结果 固然还可以利用
我们在一般的意义上的满意
但是我们希望能够计算到更大的项
那多大的项呢?
我们可以对这段代码
进行一个设置
大家可以打开这个project的属性页
在调试这个页面里
找到命令行参数
也有的叫命令参数
我们看到这里已经指定了
它最多计算前24项
也就是从第0到第23项
我们不妨把这个数调大一点
调到多大呢?难道是240?
待会我们看到 这个非常夸张
我们现在这个算法会吃不消的
诸位也会等不起的
我们不妨弄一个
稍微大但又不是很大的
比如说64
好 确定
然后重新运行一下
刚才那个已经编译过的程序
也就是 要求这个程序
按照不同的算法
计算出Fibonacci数的前64项
好 运行起来了
可以看到 对于算法的其它的版本而言
比如说迭代的版本
已经很好的计算出了结果
对于一些好的递归
比如说 线性递归也很好
但是对于有一些递归
比如说 我们现在所采用的这个版本
问题似乎很严重
因为我们看到
到目前为止
我们并没有用这个算法
足以完成刚才所说的任务
实际上我们会发现
一旦数值到40几
比如说43、44的时候
就已经出现了很大的延迟
我们能感觉到的延迟
或者精确地讲 已经能够到了十几秒
或者几十秒的 这样的一个延迟
这会多少令我们有些困惑
难道是其它的方面出了问题?
我们为了确认
不是其它的方面出了问题
不妨可以打开任务管理器
我们可以来看一下
比如说 对我这个机器而言
它的CPU的四个核里
已经有几乎是四分之一
也就是一个完整的
被这个叫Fibonacci的程序所占用
换句话说 它确实在满负荷工作
但是 依然计算的非常非常的慢
比如说 我们要等到下一项
第47项会怎么样呢?
不知道 只有天知道
但是 为了安全起见
我们还是把它终止掉
再回过头来 拿起我们的笔和纸
运用我们此前所学过的方法
来分析一下
这段代码之所以这么慢
从复杂度的角度来看
到底如何解释
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-OJ系统说明
--关于OJ
--1-注册与登录
--2-界面与选课
--3-提交测试
-关于课程教材与讲义
--课程教材与讲义
-关于讨论区
--关于讨论区
-微信平台
--html
-PA晋级申请
--PA晋级
-(a)计算
--演示
--(a)计算--作业
-(b)计算模型
-(b)计算模型--作业
-(c)大O记号
-(c)大O记号--作业
-(d)算法分析
-(d)算法分析--作业
-(e)迭代与递归
-(e)迭代与递归--作业
-(xc)动态规划
-- 演示
-(xc)动态规划--作业
-本章测验--作业
-(a)接口与实现
--02A-5 复制
-(a)接口与实现--作业
-(b)可扩充向量
-(b)可扩充向量--作业
-(c)无序向量
--02C-1 概述
--02C-3 插入
--02C-6 查找
--02C-8 遍历
-(c)无序向量--作业
-(d1)有序向量:唯一化
-(d1)有序向量:唯一化--作业
-(d2)有序向量:二分查找
-(d2)有序向量:二分查找--作业
-(d3)有序向量:Fibonacci查找
-(d3)有序向量:Fibonacci查找--作业
-(d4)有序向量:二分查找(改进)
-(d4)有序向量:二分查找(改进)--作业
-(d5)有序向量:插值查找
-第二章 向量(下)--(d5)有序向量:插值查找
-(e)起泡排序
--02E-2 改进
--02E-3 反例
-(e)起泡排序--作业
-(f)归并排序
-(f)归并排序--作业
-本章测验--作业
-(a)接口与实现
--03A-4 实现
-(a)接口与实现--作业
-(b)无序列表
--03B-2 查找
-(b)无序列表--作业
-(c)有序列表
--03C-3 查找
-(c)有序列表--作业
-(d)选择排序
--03D-1 构思
--03D-2 实例
--03D-3 实现
--03D-4 推敲
--03D-6 性能
-(d)选择排序--作业
-(e)插入排序
--03E-1 经验
--03E-2 构思
--03E-3 对比
--03E-4 实例
--03E-5 实现
-(e)插入排序--作业
-(xd)习题辅导:LightHouse
-本章测验--作业
- (a)栈接口与实现
--04A-1 栈
--04A-2 实例
--04A-3 实现
- (a)栈接口与实现--作业
-(c1)栈应用:进制转换
-第四章 栈与队列--(c1)栈应用:进制转换
-(c2)栈应用:括号匹配
-(c2)栈应用:括号匹配--作业
-(c3)栈应用:栈混洗
-第四章 栈与队列--(c3)栈应用:栈混洗
-(c4)栈应用:中缀表达式求值
-(c4)栈应用:中缀表达式求值--作业
-(c5)栈应用:逆波兰表达式
-第四章 栈与队列--(c5)栈应用:逆波兰表达式
-(d)队列接口与实现
--04D-1 接口
--04D-2 实例
--04D-3 实现
-第四章 栈与队列--本章测验
-(a)树
--05A-1 动机
--05A-2 应用
-(a)树--作业
-(b)树的表示
--05B-2 父亲
--05B-3 孩子
-第五章 二叉树--(b)树的表示
-(c)二叉树
-(c)二叉树--作业
-(d)二叉树实现
-(d)二叉树实现--作业
-(e1)先序遍历
-(e1)先序遍历--作业
-(e2)中序遍历
-第五章 二叉树--(e2)中序遍历
-(e4)层次遍历
-第五章 二叉树--(e4)层次遍历
-(e5)重构
-(e5)重构--作业
-本章测验--作业
-(a)概述
-(a)概述--作业
-(b1)邻接矩阵
-(b1)邻接矩阵--作业
-(c)广度优先搜索
--06C-2 策略
--06C-3 实现
--06C-5 实例
-(c)广度优先搜索--作业
-(d)深度优先搜索
--06D-1 算法
--06D-2 框架
--06D-3 细节
-(d)深度优先搜索--作业
-第六章 图--本章测验