python向下_python实现自顶向下,自底向上 | 学步园

常用的

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

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

一、自顶向下和自底向上

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

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

自顶向下(Top-Down)

自顶向下的方式其实就是使用递归来求解子问题,最终解只需要调用递归式,子问题逐步往下层递归的求解。我们可以使用缓存把每次求解出来的子问题缓存起来,下次调用的时候就不必再递归计算了。

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

#!/usr/bin/env python

# coding:utf-8

def fib(number):

if number == 0 or number == 1:

return 1

else:

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

if __name__ == '__main__':

print fib(35)

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

#!/usr/bin/env python

# coding:utf-8

cache = {}

def fib(number):

if number in cache:

return cache[number]

if number == 0 or number == 1:

return 1

else:

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

return cache[number]

if __name__ == '__main__':

print fib(35)

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

自底向上(Bottom-Up)

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

我们在求解子问题的最优解的同时,也相当于是在求解整个问题的最优解。其中最难的部分是找到求解最终问题的递归关系式,或者说状态转移方程。

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

你现在想买一大堆算法书,需要很多钱,所以你打算去抢一个商店,这个商店一共有n个商品。问题在于,你只能最多拿 W kg 的东西。wi和vi分别表示第i个商品的重量和价值。我们的目标就是在能拿的下的情况下,获得最大价值,求解哪些物品可以放进背包。对于每一个商品你有两个选择:拿或者不拿。

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

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

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

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

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

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

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

#!/usr/bin/env python

# 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

def m(i,w):

if str(i)+','+str(w) in cache:

return cache[str(i)+','+str(w)]

result = 0

# 特殊情况

if i == 0 or w == 0:

return 0

# w < w[i]

if w < weights[i]:

result = m(i-1,w)

# w >= w[i]

if w >= 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)

if take_it > ignore_it:

print 'take ',i

else:

print 'did not take',i

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

return result

if __name__ == '__main__':

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

print m(len(items)-1,W)

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

#!/usr/bin/env python

# coding:utf-8

cache = {}

items = range(1,9)

weights = [10,1,5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值