数据结构与算法之 算法:动态规划

        算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

        算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。随机化算法在内的一些算法,包含了一些随机输入。        
 

动态规划算法

1、特点:
    一种思想:最优子结构--递推式 + 重叠子问题
            每个子问题只求解一次,保存求解结果
            之后需要此问题时,只需查找保存的结果


2、应用:

(1)斐波那契数列        

def fibnacci(n):
    """斐波那契数列
    速度比非递归慢的原因:子问题的重复计算
    """
    if n == 1 or n == 2:
        return 1
    else:
        return fibnacci(n-1) + fibnacci(n-2)

print(fibnacci(5))

def fibnacci_no_rec(n):
    """动态规划思想:非递归的斐波那契数列"""
    f = [0, 1, 1]
    if n > 2:
        for i in range(n-2):
            num = f[-1] + f[-2]
            f.append(num)
    return f[n]

print(fibnacci_no_rec(5))

(2)钢条切割问题:

                     

 

 

 

def cut_rod_recurision(p, n):
    """钢条切割问题:自顶向下递归实现,时间复杂度O(2^n)
    有子问题的重复计算
    p:钢条长度对应的价格
    n:钢条的长度
    """
    if n == 0:
        return 0
    else:
        res = p[n]
        for i in range(1, n):   
            res = max(res, cut_rod_recurision(p, i) + cut_rod_recurision(p, n-i))
        return res

    
def cut_rod_recurision_2(p, n):
    """钢条切割问题:自顶向下递归实现
    还是有子问题的重复计算
    p:钢条长度对应的价格
    n:钢条的长度
    """
    if n == 0:
        return 0
    else:
        res = p[n]
        for i in range(1, n+1):
            res = max(res, p[i] + cut_rod_recurision_2(p, n-i))
        return res

p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_recurision(p, 9))     # 25
print(cut_rod_recurision_2(p, 9))     # 25


def cut_rod_dp(p, n):
    """动态规划:钢条切割问题,自底向上		
    无子问题的重复计算,时间复杂度O(n^2)
    p:钢条长度对应的价格
    n:钢条的长度
    """
    # 对于每个子问题,保存求解结果
    r = [0]
    # 先求r1,r2,r3...
    for i in range(1, n+1):
        res = 0
        # 从i种方案里,比较原来的res和现在方案,取最大
        for j in range(1, i+1):
            res = max(res, p[j] + r[i - j])
        r.append(res)
    return r[n]    

p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_dp(p, 9))



def cut_rod_extend(p, n):
    """动态规划:钢条切割问题:自底向上,重构解
            从钢条的左边割下长度为i的一段,只对右边剩下的一段继续进行切割
    无子问题的重复计算
    p:钢条长度对应的价格
    n:钢条的长度
    """
    # 对于每个子问题,保存求解结果
    r = [0]
    # 对于每个子问题,保存坐标不切割的那一段的长度
    s = [0]
    # 自底向上,先求r1,r2,r3...
    for i in range(1, n+1):
        res_r = 0   # 记录价格的最大值
        res_s = 0   # 记录价格最大值对应方案的左边不切割长度
        # 从i种方案里,比较原来的res和现在方案,取最大
        for j in range(1, i+1):
            if p[j]+r[i - j] > res_r:
                res_r = p[j] + r[i - j]
                res_s = j
        r.append(res_r)
        s.append(res_s)
    return r[n], s    


def cut_rod_solution(p, n):
    """输出最优方案"""
    r, s = cut_rod_extend(p, n)
    ans = []    # 存放最优方案
    while n > 0:
        ans.append(s[n])
        n -= s[n]
    return ans    

p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_solution(p, 9))

(3)最长公共子序列

(4)欧几里得算法:最大公约数

def gcd(a, b):
    """欧几里得算法,最大公约数"""
    if b == 0:
        return a
    else:
        return gcd(b, a % b)

def gcd_no_rec(a, b):
    while b > 0:
        r = a % b
        a = b
        b = r
    return a

print(gcd(12,16))   # 4

利用欧几里得算法实现的分数类,支持分数的四则运算:

class Fraction:
    """利用欧几里得算法实现的分数类,支持分数的四则运算
    a:分子
    b:分母
    """
    def __init__(self, a, b):
        self.a = a
        self.b = b
        x = self.gcd(a, b)
        self.a /= x
        self.b /= x

    def gcd(self, a, b):
        """最大公约数"""
        while b > 0:
            r = a % b
            a = b
            b = r
        return a

    def zgs(self, a, b):
        """最小公倍数"""
        # 12 16 --->4   最小公倍数为3*4*4 = 48
        x = self.gcd(a, b)
        return a * b / x

    def __add__(self, other):
        """分数的加法运算"""
        # 1/12 + 1/20
        a = self.a
        b = self.b
        c = other.a
        d = other.b
        fenmu = self.zgs(b, d)
        fenzi = a * fenmu / b + c * fenmu / d
        return Fraction(fenzi, fenmu)

    def __str__(self):
        return "%d/%d" % (self.a, self.b)


a = Fraction(1,3)
b = Fraction(1,2)
print(a + b)    # 5/6

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值