您的位置:首页 > 其它

递归思想和动态规划思想

2019-01-07 20:53 176 查看

递归:

1.介绍

递归算法是一种直接或者间接调用自身函数或者方法的算法。其中调用分为直接调用和间接调用,直接调用是指在函数体中调用自身,间接调用是调用别的函数,而这些函数调用函数本身。这样可以把很长的冗余的代码进行了简化。
递归算法解决问题的特点:

  • 递归就是方法里调用自身。

  • 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。

  • 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。

  • 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。

    递归算法要求。递归算法所体现的“重复”一般有三个要求:
     
    (1) 是每次调用在规模上都有所缩小(通常是减半);
    (2) 是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
    (3) 是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(例如还没达到一个明确的解或者接受范围是多大),无条件递归调用将会成为死循环而不能正常结束。

2.递归的用处

就举个例子:
计算阶乘:
n! = 1 × 2 × 3 × … × n
但是我们用递归方式就比较简洁:
n! = (n-1)! × n
注意要加上条件,n>=1。
代码实现如下

def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)

还有斐波那契数列、汉诺塔这些例子我就一一列举了。

动态规划算法:

1.动态规划算法的核心

我在网上找的资料,发现有个写的挺好的,动态规划算法的核心是下面的一张图片和一个小故事。

A * “1+1+1+1+1+1+1+1 =?” *
A : “上面等式的值是多少”
B : “等于8”
A *在上面等式的左边写上 “1+” *
A : “此时等式的值为多少”
B : 很快说“等于9"
A : “你怎么这么快就知道答案了”
A : “只要在8的基础上加1就行了”
A : "所以你不用重新计算因为你记住了第一个等式的值为8,动态规划算法也可以说是 '记住求过的解来节省时间’”

这段话和这个图片还是蛮清晰的,动态规划问题就是记住已经解决过的子问题的解。

2.动态规划算法的介绍

动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
这样不是回到递归了吗?,是的。但是你需要知道如何先拆分问题,而拆分问题,靠的就是状态的定义和状态转移方程的定义。

1.状态的定义

先看一道题:

给定一个数列,长度为N, 求这个数列的最长上升(递增)子数列(LIS)的长度. 以 1 7 2 8 3 4 为例。
这个数列的最长递增子数列是 1 2 3 4,长度为4; 次长的长度为3, 包括 1 7 8; 1 2 3 等。

要解决这个问题,我们首先要定义这个问题和这个问题的子问题。
有人可能会问了,题目都已经在这了,我们还需定义这个问题吗?需要,原因就是这个问题在字面上看,找不出子问题,而没有子问题,这个题目就没办法解决。

现在先试试重新定义这个问题:

给定一个数列,长度为N,设FkF_{k}Fk​为以数列中第k项结尾的最长递增子序列的长度. 求F1F_{1}F1​…FNF_{N}FN​中的最大值。

这个新问题与原问题等价。
而对于FkF_{k}Fk​来讲,$F_{1} $… Fk−1F_{k-1}Fk−1​都是FkF_{k}Fk​的子问题:因为以第k项结尾的最长递增子序列(下称LIS),包含着以第1…k-1中某项结尾的LIS。
上述的新问题FkF_{k}Fk​也可以叫做状态,定义中的“FkF_{k}Fk​为数列中第k项结尾的LIS的长度”,就叫做对状态的定义。
之所以把FkF_{k}Fk​做“状态”而不是“问题” ,一是因为避免跟原问题中“问题”混淆,二是因为这个新问题是数学化定义的。

2. 状态转移方程的定义?

上述状态定义好之后,状态和状态之间的关系式,就叫做状态转移方程。

比如,对于LIS问题,我们的第一种定义:

设F_{k}为:以数列中第k项结尾的最长递增子序列的长度.

设A为题中数列,状态转移方程为:

F_{1} = 1 (根据状态定义导出边界情况)
F_{k}=max(F_{i}+1 | A_{k}>A_{i}, i\in (1…k-1)) (k>1)

用文字解释一下是:
以第k项结尾的LIS的长度是:保证第i项比第k项小的情况下,以第i项结尾的LIS长度加一的最大值,取遍i的所有值(i小于k)。

第二种定义:

设F_{i, k}为:在数列前i项中,长度为k的递增子序列中,最后一位的最小值

设A为题中数列,状态转移方程为:

若A_{i}>F_{i-1,k-1}则F_{i,k}=min(A_{i},F_{i-1,k}) 否则:F_{i,k}=F_{i-1,k}

(边界情况需要分类讨论较多,在此不列出,需要根据状态定义导出边界情况。)
大家套着定义读一下公式就可以了,应该不难理解,就是有点绕。

这里可以看出,这里的状态转移方程,就是定义了问题和子问题之间的关系。
可以看出,状态转移方程就是带有条件的递推式。

3.动态规划算法的方法

实现动态规划有两种方法
1.直接自顶向下实现递归式,并将中间结果保存,这叫备忘录法;
2.按照递归式自底向上地迭代,将结果保存在某个数据结构中求解。
具体的话。
具体的例子感那段话还是很清晰的,就不详细列举了。

参考链接:https://www.geek-share.com/detail/2702235748.html
https://www.geek-share.com/detail/2711130709.html
https://www.zhihu.com/question/23995189

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: