动态规划例子.docx

上传人:b****3 文档编号:3124452 上传时间:2022-11-17 格式:DOCX 页数:11 大小:202.30KB
下载 相关 举报
动态规划例子.docx_第1页
第1页 / 共11页
动态规划例子.docx_第2页
第2页 / 共11页
动态规划例子.docx_第3页
第3页 / 共11页
动态规划例子.docx_第4页
第4页 / 共11页
动态规划例子.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

动态规划例子.docx

《动态规划例子.docx》由会员分享,可在线阅读,更多相关《动态规划例子.docx(11页珍藏版)》请在冰豆网上搜索。

动态规划例子.docx

动态规划例子

有8万元的投资可以投给3个过目,每个项目在不同筒子数额下(以万元为单位)的利润如下表

投资额

盈利

项目

0

1

2

3

4

5

6

7

8

项目1

0

5

15

40

80

90

95

98

100

项目2

0

5

15

40

60

70

73

74

75

项目3

0

4

26

40

45

50

51

52

53

请安排投资计划,使总的利润最大。

写出你所设的状态变量、决策变量、状态转移方程与递推关系式和手工求解的详细步骤及结构。

求解:

状态变量:

xk表示留给项目k..n的投资额,其中n为项目总个数,k=1..n.

决策变量:

uk表示投给项目k的投资额.

允许决策集合:

状态转移方程:

递推关系式:

其中,

表示项目k的投资额为uk时的盈利.

针对本题,n=3,xk最大取8

手工详解过程:

•初始化k=3

x3

0

1

2

3

4

5

6

7

8

f3(x3)

0

4

26

40

45

50

51

52

53

•k=2

x2

0

1

2

3

4

5

6

7

8

f2(x2)

0

5

26

40

60

70

86

100

110

•k=1

x1

0

1

2

3

4

5

6

7

8

f1(x1)

0

5

26

40

80

90

106

120

140

最终结果:

给项目1投资4万元,项目2投资4万元,项目3不投资,将获得最大利润140万元.

 

python实现自顶向下,自底向上

常用的算法设计思想主要有动态规划、贪婪法、随机化算法、回溯法等等,这些思想有重叠的部分,当面对一个问题的时候,从这几个思路入手往往都能得到一个还不错的答案。

本来想把动态规划单独拿出来写三篇文章呢,后来发现自己学疏才浅,实在是只能讲一些皮毛,更深入的东西尝试构思了几次,也没有什么进展,打算每种设计思想就写一篇吧。

动态规划(DynamicProgramming)是一种非常有用的用来解决复杂问题的算法,它通过把复杂问题分解为简单的子问题的方式来获得最优解。

一、自顶向下和自底向上

总体上来说,我们可以把动态规划的解法分为自顶向下和自底向上两种方式。

一个问题如果可以使用动态规划来解决,那么它必须具有“最优子结构”,简单来说就是,如果该问题可以被分解为多个子问题,并且这些子问题有最优解,那这个问题才可以使用动态规划。

自顶向下(Top-Down)

自顶向下的方式其实就是使用递归来求解子问题,最终解只需要调用递归式,子问题逐步往下层递归的求解。

我们可以使用缓存把每次求解出来的子问题缓存起来,下次调用的时候就不必再递归计算了。

举例著名的斐波那契数列的计算:

#!

/usr/bin/envpython

#coding:

utf-8

deffib(number):

ifnumber==0ornumber==1:

return1

else:

returnfib(number-1)+fib(number-2)

if__name__=='__main__':

printfib(35)

有一点开发经验的人就能看出,fib(number-1)和fib(number-2)会导致我们产生大量的重复计算,以上程序执行了14s才出结果,现在,我们把每次计算出来的结果保存下来,下一次需要计算的时候直接取缓存,看看结果:

#!

/usr/bin/envpython

#coding:

utf-8

cache={}

deffib(number):

ifnumberincache:

returncache[number]

ifnumber==0ornumber==1:

return1

else:

cache[number]=fib(number-1)+fib(number-2)

returncache[number]

if__name__=='__main__':

printfib(35)

耗费时间为0m0.053s效果提升非常明显。

自底向上(Bottom-Up)

自底向上是另一种求解动态规划问题的方法,它不使用递归式,而是直接使用循环来计算所有可能的结果,往上层逐渐累加子问题的解。

我们在求解子问题的最优解的同时,也相当于是在求解整个问题的最优解。

其中最难的部分是找到求解最终问题的递归关系式,或者说状态转移方程。

这里举一个01背包问题的例子:

你现在想买一大堆算法书,需要很多钱,所以你打算去抢一个商店,这个商店一共有n个商品。

问题在于,你只能最多拿Wkg的东西。

wi和vi分别表示第i个商品的重量和价值。

我们的目标就是在能拿的下的情况下,获得最大价值,求解哪些物品可以放进背包。

对于每一个商品你有两个选择:

拿或者不拿。

首先我们要做的就是要找到“子问题”是什么,我们发现,每次背包新装进一个物品,就可以把剩余的承重能力作为一个新的背包来求解,一直递推到承重为0的背包问题:

作为一个聪明的贼,你用 m[i,w]表示偷到商品的总价值,其中i表示一共多少个商品,w表示总重量,所以求解m[i,w]就是我们的子问题,那么你看到某一个商品i的时候,如何决定是不是要装进背包,有以下几点考虑:

1.该物品的重量大于背包的总重量,不考虑,换下一个商品;

2.该商品的重量小于背包的总重量,那么我们尝试把它装进去,如果装不下就把其他东西换出来,看看装进去后的总价值是不是更高了,否则还是按照之前的装法;

3.极端情况,所有的物品都装不下或者背包的承重能力为0,那么总价值都是0;

由以上的分析,我们可以得出m[i,w]的状态转移方程为:

有了状态转移方程,那么写起代码来就非常简单了,首先看一下自顶向下的递归方式,比较容易理解:

#!

/usr/bin/envpython

#coding:

utf-8

cache={}

items=range(0,9)

weights=[10,1,5,9,10,7,3,12,5]

values=[10,20,30,15,40,6,9,12,18]

#最大承重能力

W=4

defm(i,w):

ifstr(i)+','+str(w)incache:

returncache[str(i)+','+str(w)]

result=0

#特殊情况

ifi==0orw==0:

return0

#w

ifw

result=m(i-1,w)

#w>=w[i]

ifw>=weights[i]:

#把第i个物品放入背包后的总价值

take_it=m(i-1,w-weights[i])+values[i]

#不把第i个物品放入背包的总价值

ignore_it=m(i-1,w)

#哪个策略总价值高用哪个

result=max(take_it,ignore_it)

iftake_it>ignore_it:

print'take',i

else:

print'didnottake',i

cache[str(i)+','+str(w)]=result

returnresult

if__name__=='__main__':

#背包把所有东西都能装进去做假设开始

printm(len(items)-1,W)

改造成非递归,即循环的方式,从底向上求解:

#!

/usr/bin/envpython

#coding:

utf-8

cache={}

items=range(1,9)

weights=[10,1,5,9,10,7,3,12,5]

values=[10,20,30,15,40,6,9,12,18]

#最大承重能力

W=4

defknapsack():

forwinrange(W+1):

cache[get_key(0,w)]=0

foriinitems:

cache[get_key(i,0)]=0

forwinrange(W+1):

ifw>=weights[i]:

ifcache[get_key(i-1,w-weights[i])]+values[i]>cache[get_key(i-1,w)]:

cache[get_key(i,w)]=values[i]+cache[get_key(i-1,w-weights[i])]

else:

cache[get_key(i,w)]=cache[get_key(i-1,w)]

else:

cache[get_key(i,w)]=cache[get_key(i-1,w)]

returncache[get_key(8,W)]

defget_key(i,w):

returnstr(i)+','+str(w)

if__name__=='__main__':

#背包把所有东西都能装进去做假设开始

printknapsack()

从这里可以看出,其实很多动态规划问题都可以使用循环替代递归求解,他们的区别在于,循环方式会穷举出所有可能用到的数据,而递归只需要计算那些对最终解有帮助的子问题的解,但是递归本身是很耗费性能的,所以具体实践中怎么用要看具体问题具体分析。

最长公共子序列(LCS)

解决了01背包问题之后,我们对“子问题”和“状态转移方程”有了一点点理解,现在趁热打铁,来试试解决LCS问题:

字符串一“ABCDABCD”和字符串二”BDCFG”的公共子序列(不是公共子串,不需要连续)是BDC,现在给出两个确定长度的字符串X和Y,求他们的最大公共子序列的长度。

首先,我们还是找最优子结构,即把问题分解为子问题,X和Y的最大公共子序列可以分解为X的子串Xi和Y的子串Yj的最大公共子序列问题。

其次,我们需要考虑Xi和Yj的最大公共子序列C[i,j]需要符合什么条件:

1.如果两个串的长度都为0,则公共子序列的长度也为0;

2.如果两个串的长度都大于0且最后面一位的字符相同,则公共子序列的长度是C[i−1,j−1]的长度加一;

3.如果两个子串的长度都大于0,且最后面一位的字符不同,则最大公共子序列的长度是C[i−1,j]和C[i,j−1]的最大值;

最后,根据条件获得状态转移函数:

由此转移函数,很容易写出递归代码:

#!

/usr/bin/envpython

#coding:

utf-8

cache={}

#为了下面表示方便更容易理解,数组从1开始编号

#即当i,j为0的时候,公共子序列为0,属于极端情况

A=[0,'A','B','C','B','D','A','B','E','F']

B=[0,'B','D','C','A','B','A','F']

defC(i,j):

ifget_key(i,j)incache:

returncache[get_key(i,j)]

res

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1