算法(09):动态编程
来源:互联网 发布:人工智能的文献 编辑:程序博客网 时间:2024/06/04 18:38
分治法的一个最基本的特性是把一个问题划分为几个独立的子问题。当子问题不独立时,情况就变得更复杂了,这是因为这种问题小规模的最简单的直接递归实现需要无法接受的时间。
先看一个简单的例子:斐波纳契数列
F(N)=F(N-1)+F(N-2),F(0)=0,F(1)=1
根据公式定义,我们很容易实现递归实现:
static int F(int i)
...{
if(i<1)return 0;
if(i==1)return 1;
return F(i-1)+F(i-2);
}
...{
if(i<1)return 0;
if(i==1)return 1;
return F(i-1)+F(i-2);
}
这个递归简洁优美,但并不能作为实际使用,因为它要花费指数时间来计算F(N)。计算F(N+1)的递归调用次数是F(N+1)次,而F(N)约为φ^N次幂,φ≈1.618它是黄金比例。
下面的图很清楚地显示了重复计算的量,在递归的过程中存在着子问题重叠的问题
一般来说我们要通过递归程序来分析问题的特性,然后设计出在线性的时间内(与N成比例)能完成工作的程序进行计算。
我们通过计算前N个斐波纳契数列并把它存入一个数组中,供计算F(N+1)时进行调用,这样就可以避免重复计算已经存在的值。因为数列是成指数增长的,因此数组并不大,比如:F(45)=1836311903是能表示成32位整数的最大斐波纳契数列,所以大小为46的数组就可以了。
我们有了指导思路,我们就可以对前面的递归程序进行改造,我们只需要检查保存值并保存就可以了。
static final int maxN=47;
static int knownF[]=new int[maxN];
static int F(int i)
...{
if(knownF[i]!=0)return knownF[i];//我们使用数组存储了已经计算过的数值,这样就避免了重复计算带来的重复调用开销
int t=i;
if(i<0)return 0;
if(i>1)t=F(i-1)+F(i-2);
return knownF[i]=t;
}
static int knownF[]=new int[maxN];
static int F(int i)
...{
if(knownF[i]!=0)return knownF[i];//我们使用数组存储了已经计算过的数值,这样就避免了重复计算带来的重复调用开销
int t=i;
if(i<0)return 0;
if(i>1)t=F(i-1)+F(i-2);
return knownF[i]=t;
}
动态编程把递归方法的运行时间降低到至多是小于或等于给定参数的所有参数对函数求值时所需要的时间,把递归调用的开销看作常数。
下图说明了如何通过存储计算值把开销从指数级降低到线性级的
我们在以后的高级问题算法设计中会再次讨论动态编程技术,但它也存在着致命的问题。就是对于超大规模的问题求解,我们无法提供足够的空间存储所有值,这时动态编程就不可用了。
- 算法(09):动态编程
- java算法:动态编程
- 编程算法之动态规划算法
- VC++动态链接库编程之DLL算法
- 编程算法 - 背包问题(三种动态规划) 代码(C)
- 编程算法 - 背包问题(三种动态规划) 代码(C)
- 动态编程之序列比对:Needleman-Wunsch 算法和Smith-Waterman算法
- 编程之美之 饮料供货 之 动态规划算法和备忘录算法实现
- 编程常用算法--分治法,动态规划,回溯法,分支界限法,贪心算法
- 算法--动态规划算法
- 算法编程
- 编程算法
- 编程算法
- 编程算法之动态规划之最长公共子序列(java版)
- 2013腾讯编程马拉松复赛第三场--威威猫的故事 动态规划算法
- 编程之美1.6 饮料供货[动态规划vs贪心算法]
- 编程之美1.6 饮料供货[动态规划vs贪心算法]
- 数据结构与算法学习笔记——动态规划的入门与编程实现
- 上帝让谁灭亡,首先让他疯狂
- 男人女人终身相伴十要素
- 一些关于VC的文章
- ListCtrl的总结
- 将POI封装为COM组件
- 算法(09):动态编程
- [收藏]Linux国外的镜像服务器比较多
- CListCtrl
- 基于Win32平台下Winsock API的网络编程
- 让人郁闷的Weblogic:antlr.TokenStreamIOException
- CEdit & CRichEdit 使用技巧
- 系统引导管理器GRUB,为初学者指南
- SQL语法大全
- 用如下uc2gb函数分析字符串会出现数字丢失的解决办法