【算法进阶2-动态规划】斐波那契数列(递归调用、动态规划)、钢条切割问题(自定而下实现、自底向上、切割方案)

news2024/11/13 9:27:53

1 斐波那契数
2 钢条切割问题
2.1 最优解情况
2.2 钢条切割问题之自定而下实现
2.3 钢条切割问题之自底向上实现
2.4 钢条切割问题-重构解-切割方案

1 斐波那契数

# 1 子问题的重复计算
def fibonacci(n: int) -> int:
    """
    使用递归方式计算第 n 个斐波那契数。
    该方法效率低,因为会重复计算相同的子问题。

    :param n: 要计算的斐波那契数的索引(从 1 开始)
    :return: 返回第 n 个斐波那契数
    """
    # 基础情况:第 1 个和第 2 个斐波那契数都是 1
    if n == 1 or n == 2:
        return 1
    else:
        # 递归调用:第 n 个斐波那契数等于前两个斐波那契数之和
        return fibonacci(n - 1) + fibonacci(n - 2)


print(fibonacci(10))  # 使用递归方法,输出 55


# 2 动态规划--》递推式
def fibonacci_no_recursion(n: int) -> int:
    """
    使用迭代方式计算第 n 个斐波那契数,避免递归的重复计算。

    :param n: 要计算的斐波那契数的索引(从 1 开始)
    :return: 返回第 n 个斐波那契数
    """
    # 初始化斐波那契数列的前两个值
    f = [0, 1, 1]

    # 当 n 大于 2 时,通过迭代计算后续的斐波那契数
    if n > 2:
        for i in range(n - 2):
            # 计算下一个斐波那契数,并将其添加到列表中
            num = f[-1] + f[-2]
            f.append(num)

    # 返回第 n 个斐波那契数
    return f[n]


print(fibonacci_no_recursion(100))  # 354224848179261915075

2 钢条切割问题

在这里插入图片描述
在这里插入图片描述

2.1 最优解情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 钢条切割问题之自定而下实现

import time


def cal_time(func):
    """
    装饰器,用于计算函数的执行时间。

    :param func: 被装饰的函数
    :return: 包装后的函数,附加了执行时间的计算
    """

    def wrapper(*args, **kwargs):
        # 记录开始时间
        t1 = time.time()

        # 执行被装饰的函数并获取返回值
        result = func(*args, **kwargs)

        # 记录结束时间
        t2 = time.time()

        # 计算并打印函数运行时间
        print(f"{func.__name__} running time: {t2 - t1} seconds.")

        # 返回函数执行的结果
        return result

    return wrapper


def cut_rod_recurision_1(p: list, n: int) -> int:
    """
    使用递归方法求解钢条切割问题。

    钢条切割问题的目标是将一根长度为 n 的钢条切成若干段,
    使得每段的总价值最大化。价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 如果钢条长度为 0,则最大收益为 0
    if n == 0:
        return 0

    # 初始化最大收益为切割长度为 n 的收益
    res = p[n]

    # 递归地计算所有可能的切割方式
    for i in range(1, n):
        # 对每个可能的切割点 i,计算最大收益
        res = max(res, cut_rod_recurision_1(p, i) + cut_rod_recurision_1(p, n - i))

    return res


# price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
# print(cut_rod_recurision_1(price, 9))  # 25

def cut_rod_recurision_2(p: list, n: int) -> int:
    """
    使用递归方法求解钢条切割问题的优化版本。

    该函数通过递归计算长度为 n 的钢条的最大收益。
    钢条的价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 如果钢条长度为 0,则最大收益为 0
    if n == 0:
        return 0

    # 初始化最大收益为 0,因为我们可能不切割钢条,即收益为 0
    res = 0

    # 递归地计算所有可能的切割方式
    for i in range(1, n + 1):
        # 对每个可能的切割点 i,计算当前切割方案的收益
        # res = max(当前最大收益, 将钢条切成长度 i 和 n-i 两部分的收益)
        res = max(res, p[i] + cut_rod_recurision_2(p, n - i))

    # 返回最大收益
    return res


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

price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 28, 30, 33, 36, 37, 39, 40]
# print(len(price))
# print(cut_rod_recurision_2(price, 20))  # 56

@cal_time
def c1(p: list, n: int) -> int:
    """
    使用装饰器 cal_time 来计算并打印 cut_rod_recurision_1 函数的运行时间。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    return cut_rod_recurision_1(p, n)


@cal_time
def c2(p: list, n: int) -> int:
    """
    使用装饰器 cal_time 来计算并打印 cut_rod_recurision_2 函数的运行时间。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    return cut_rod_recurision_2(p, n)


print(c1(price, 20))
print(c2(price, 20))

2.3 钢条切割问题之自底向上实现

在这里插入图片描述

import time


def cal_time(func):
    """
    装饰器,用于计算函数的执行时间。

    :param func: 被装饰的函数
    :return: 包装后的函数,附加了执行时间的计算
    """

    def wrapper(*args, **kwargs):
        # 记录开始时间
        t1 = time.time()

        # 执行被装饰的函数并获取返回值
        result = func(*args, **kwargs)

        # 记录结束时间
        t2 = time.time()

        # 计算并打印函数运行时间
        print(f"{func.__name__} running time: {t2 - t1} seconds.")

        # 返回函数执行的结果
        return result

    return wrapper


def cut_rod_recurision_2(p: list, n: int) -> int:
    """
    使用递归方法求解钢条切割问题的优化版本。

    该函数通过递归计算长度为 n 的钢条的最大收益。
    钢条的价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 如果钢条长度为 0,则最大收益为 0
    if n == 0:
        return 0

    # 初始化最大收益为 0,因为我们可能不切割钢条,即收益为 0
    res = 0

    # 递归地计算所有可能的切割方式
    for i in range(1, n + 1):
        # 对每个可能的切割点 i,计算当前切割方案的收益
        # res = max(当前最大收益, 将钢条切成长度 i 和 n-i 两部分的收益)
        res = max(res, p[i] + cut_rod_recurision_2(p, n - i))

    # 返回最大收益
    return res


price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 28, 30, 33, 36, 37, 39, 40]


@cal_time
def c2(p: list, n: int) -> int:
    """
    使用装饰器 cal_time 来计算并打印 cut_rod_recurision_2 函数的运行时间。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    return cut_rod_recurision_2(p, n)


@cal_time
def cut_rod_dp(p: list, n: int) -> int:
    """
    钢条切割问题的动态规划解决方案,自底向上实现。

    该函数通过动态规划方法求解长度为 n 的钢条的最大收益。
    钢条的价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 初始化一个长度为 n+1 的列表 r,其中 r[i] 表示长度为 i 的钢条的最大收益
    r = [0] * (n + 1)

    # 遍历每一个可能的钢条长度 i
    for i in range(1, n + 1):
        # 对于每个长度 i,初始化当前最大收益为 0
        res = 0

        # 遍历所有可能的切割点 j,从 1 到 i
        for j in range(1, i + 1):
            # 计算切割方案的收益,并更新最大收益
            # res = max(当前最大收益, 切割成长度 j 和 i-j 两部分的收益)
            res = max(res, p[j] + r[i - j])

        # 将长度为 i 的钢条的最大收益存储在 r[i]
        r[i] = res

    # 返回长度为 n 的钢条的最大收益
    return r[n]


print(cut_rod_dp(price, 15))  # 42

2.4 钢条切割问题-重构解-切割方案

在这里插入图片描述

import time
from typing import Tuple, List


def cal_time(func):
    """
    装饰器,用于计算函数的执行时间。

    :param func: 被装饰的函数
    :return: 包装后的函数,附加了执行时间的计算
    """

    def wrapper(*args, **kwargs):
        # 记录开始时间
        t1 = time.time()

        # 执行被装饰的函数并获取返回值
        result = func(*args, **kwargs)

        # 记录结束时间
        t2 = time.time()

        # 计算并打印函数运行时间
        print(f"{func.__name__} running time: {t2 - t1} seconds.")

        # 返回函数执行的结果
        return result

    return wrapper


def cut_rod_recurision_2(p: list, n: int) -> int:
    """
    使用递归方法求解钢条切割问题的优化版本。

    该函数通过递归计算长度为 n 的钢条的最大收益。
    钢条的价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 如果钢条长度为 0,则最大收益为 0
    if n == 0:
        return 0

    # 初始化最大收益为 0,因为我们可能不切割钢条,即收益为 0
    res = 0

    # 递归地计算所有可能的切割方式
    for i in range(1, n + 1):
        # 对每个可能的切割点 i,计算当前切割方案的收益
        # res = max(当前最大收益, 将钢条切成长度 i 和 n-i 两部分的收益)
        res = max(res, p[i] + cut_rod_recurision_2(p, n - i))

    # 返回最大收益
    return res


# price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 28, 30, 33, 36, 37, 39, 40]


@cal_time
def c2(p: list, n: int) -> int:
    """
    使用装饰器 cal_time 来计算并打印 cut_rod_recurision_2 函数的运行时间。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    return cut_rod_recurision_2(p, n)


@cal_time
def cut_rod_dp(p: list, n: int) -> int:
    """
    钢条切割问题的动态规划解决方案,自底向上实现。

    该函数通过动态规划方法求解长度为 n 的钢条的最大收益。
    钢条的价格由列表 p 提供,其中 p[i] 表示长度为 i 的钢条的价格。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回长度为 n 的钢条的最大收益
    """
    # 初始化一个长度为 n+1 的列表 r,其中 r[i] 表示长度为 i 的钢条的最大收益
    r = [0]  # 只包含一个元素 0,后续会扩展到长度为 n+1

    # 遍历每一个可能的钢条长度 i 从 1 到 n
    for i in range(1, n + 1):
        # 对于每个长度 i,初始化当前最大收益为 0
        res = 0

        # 遍历所有可能的切割点 j,从 1 到 i
        for j in range(1, i + 1):
            # 计算当前切割方案的收益,并更新最大收益
            # 比较当前最大收益和切割成长度 j 和 i-j 两部分的收益
            res = max(res, p[j] + r[i - j])

        # 将长度为 i 的钢条的最大收益存储在 r[i]
        r.append(res)

    # 返回长度为 n 的钢条的最大收益
    return r[n]


# price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 28, 30, 33, 36, 37, 39, 40]
# print(cut_rod_dp(price, 20))  # 56


from typing import List, Tuple


def cut_rod_extend(p: list, n: int) -> Tuple[int, List[int]]:
    """
    扩展的钢条切割问题动态规划解决方案,自底向上实现。

    该函数不仅计算长度为 n 的钢条的最大收益,还追踪实现最大收益的切割方案。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回一个元组,其中第一个元素是长度为 n 的钢条的最大收益,第二个元素是一个列表,表示每个长度的钢条的最佳切割点
    """
    # 初始化一个长度为 n+1 的列表 r,其中 r[i] 表示长度为 i 的钢条的最大收益
    r = [0] * (n + 1)

    # 初始化一个长度为 n+1 的列表 s,用于存储每个长度的钢条的最佳切割点
    s = [0] * (n + 1)

    # 遍历每一个可能的钢条长度 i
    for i in range(1, n + 1):
        # 对于每个长度 i,初始化当前最大收益为 0
        res_r = 0  # 当前长度 i 的最大收益
        res_s = 0  # 对应最大收益的最佳切割长度

        # 遍历所有可能的切割点 j,从 1 到 i
        for j in range(1, i + 1):
            # 比较通过切割长度 j 和 i-j 两部分的收益是否大于当前最大收益
            if p[j] + r[i - j] > res_r:
                # 更新最大收益和最佳切割长度
                res_r = p[j] + r[i - j]
                res_s = j

        # 将当前长度 i 的最大收益和最佳切割长度存储在 r 和 s 列表中
        r[i] = res_r
        s[i] = res_s

    # 返回长度为 n 的钢条的最大收益和最佳切割点列表
    return r[n], s


def cut_rod_solution(p: list, n: int) -> list:
    """
    根据最佳切割点列表还原钢条切割方案

    该函数使用最佳切割点列表 `s` 还原出钢条的切割方案。

    :param p: 列表,包含各个长度的钢条对应的价格,p[0] 通常为 0
    :param n: 钢条的总长度
    :return: 返回一个列表,表示钢条的切割方案
    """
    # 调用 cut_rod_extend 函数获取最佳切割点列表
    _, s = cut_rod_extend(p, n)

    ans = []  # 存储最终的切割方案
    while n > 0:
        # 将当前长度的最佳切割点添加到切割方案中
        ans.append(s[n])
        # 更新剩余长度
        n -= s[n]

    # 返回最终的切割方案
    return ans


price = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 28, 30, 33, 36, 37, 39, 40]
_, s = cut_rod_extend(price, 20)
print(s)  # [0, 1, 2, 3, 2, 2, 6, 1, 2, 3, 2, 2, 6, 1, 2, 3, 2, 2, 6, 1, 2]
print(cut_rod_extend(price, 20))  # (56, [0, 1, 2, 3, 2, 2, 6, 1, 2, 3, 2, 2, 6, 1, 2, 3, 2, 2, 6, 1, 2])
print(cut_rod_solution(price, 20))  # [2, 6, 6, 6]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2073330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计选题推荐-法律援助平台-Java/Python项目实战

✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

有限差分学习笔记

有限差分介绍 ​ 在数学中,有限差分法(finite-difference methods,简称FDM),是一种微分方程数值方法,是通过有限差分来近似导数,从而寻求微分方程的近似解。 由泰勒展开式的推导 显式方…

给Go+Sciter开发的桌面客户端软件添加系统托盘图标

在桌面端软件开发中,系统托盘图标是提升用户体验的重要元素。托盘图标不仅能提供直观的状态反馈,还能让软件在后台运行时依然保持与用户的交互。通过托盘图标,用户可以轻松最小化软件、退出程序,甚至弹出通知,从而避免…

【海外EI 会议合集】电网系统/绿色能源/新材料主题均可

第五届电网系统与绿色能源国际学术会议(PGSGE 2025) 2025 5th International Conference on Power Grid Systems and Green Energy 重要信息 会议官网:www.pgsge.org 会议时间:2025年1月10-12日 会议地点:马来西亚…

Linux 部署 MinIO(远程服务器)

1. 下载安装 进入 Linux 内 cd /usr/local/ # 新建目录 mkdir minio # 进入目录 cd minio # 下载地址 wget https://dl.min.io/server/minio/release/linux-amd64/minio# 授权 chmod x minio 2. 自定义配置 自定义账号与登录密码,直接在本目录 默认登录账号和…

【吊打面试官系列-Memcached面试题】memcached 能够更有效地使用内存吗?

大家好,我是锋哥。今天分享关于 【memcached 能够更有效地使用内存吗?】面试题,希望对大家有帮助; memcached 能够更有效地使用内存吗? Memcache 客户端仅根据哈希算法来决定将某个 key 存储在哪个节点上,而…

ES的介绍和使用

全文搜索引擎 Elastic Search 第一节 引言 当系统数据量上了10亿、100亿条的时候,我们用什么数据库好?如何解决单点故障?如何提升检索速度?如何解决统计分析问题? 传统数据库的应对解决方案 关系型数据库 通过主从备…

后端Web之登录校验(下篇)

目录 1.概述 ​2.过滤器Fliter 3.拦截器Interceptor 1.概述 Filter过滤器:在Web开发中,过滤器(Filter)是一种非常重要的组件,用于在请求到达目标资源(如Servlet或静态资源)之前或之后&#…

10、Redis高级:多级缓存、JVM进程缓存、OpenResty本地缓存、缓存同步Canal

多级缓存 0.学习目标 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈 …

Earth‘s Future | 西南大学时伟宇团队揭示长江上游径流变化对气候变化与人类活动响应的驱动机制不同

本文首发于“生态学者”微信公众号! 径流是全球水循环的重要组成部分,对社会经济发展、维持农业生产和维护生态安全具有重要意义。自20世纪末,气候变化与人类活动双重加剧,长江上游径流变化对长江上游乃至长江流域具有重要影响。因…

SSRF以及CSRF

ssrf 服务端请求伪造:由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据 数据流:攻击者…

AI大模型日报#0823:GPT-4无师自通预测蛋白质结构登Nature子刊、豆包版《Her》升级上新

导读:AI大模型日报,爬虫LLM自动生成,一文览尽每日AI大模型要点资讯!目前采用“文心一言”(ERNIE-4.0-8K-latest)、“智谱AI”(glm-4-0520)生成了今日要点以及每条资讯的摘要。欢迎阅…

第一次运行Neo4J

在浏览器中输入127.0.0.1:7474(如Neo4J装在其它机器上输入相应的IP地址即可) 1、创建简单节点 这里我创建一个简单的“Employee”节点,在数据浏览器中的命令框(美元提示符下)键入以下命令 CREATE (emp:Employee) 执行…

leetcode139. 单词拆分,动态规划

leetcode139. 单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例 1: 输入: s…

JDK、JRE、JVM关系

JDK:Java Development Kit,是java开发工具包 ,开发java必备工具,JDKJRE开发工具集(javac等) JRE:Java Runtime Environment,是java运行时的环境,包含了java虚拟机jvm java基础类库,是使用java…

【GD32】FreeRTOS实时操作系统移植(GD32F470ZGT6)

1. 简介 在日常的应用开发项目中,常常需要单片机具有处理多种任务的需求,如果使用裸机开发那么肯定是不现实的,因为受限于IO与处理器的巨大速度差异,在裸机下处理器常常要等待当前IO操作完成才能进行下一个任务,效率大…

亦菲喊你来学机器学习(11) --回归树算法

文章目录 回归树回归树结构回归树的工作原理优点与缺点构建回归树模型回归树模型参数介绍训练模型测试模型 总结 回归树 决策树是一种常用的机器学习算法,广泛应用于分类和回归任务中。当决策树用于回归任务时,我们称之为回归树(Regression …

零基础5分钟上手亚马逊云科技 - 网络安全分析最佳实践

简介: 欢迎来到小李哥全新亚马逊云科技AWS云计算知识学习系列,适用于任何无云计算或者亚马逊云科技技术背景的开发者,通过这篇文章大家零基础5分钟就能完全学会亚马逊云科技一个经典的服务开发架构方案。 我会每天介绍一个基于亚马逊云科技…

【逐行注释】基于CV/CT模型的IMM|MATLAB程序|源代码复制后即可运行,无需下载

订阅专栏后可以直接查看完整的源代码(和注释),无需付费下载或其他的操作。代码复制到MATLAB上面可以得到和我一样的运行结果。 文章目录 程序概述完整代码与逐行注释运行结果解释按模块分析代码程序概述 基于EKF的多模型交互。以CV和CT两个模型进行交互,这里对代码进行逐…

Django后台管理Xadmin使用DjangoUeditor富文本编辑器

Django后台管理Xadmin使用DjangoUeditor富文本编辑器 一、下载 点击github下载 https://github.com/twz915/DjangoUeditor3 1、下载完后解压到跟xadmin同一层级目录: 2、解压后名称可能为DjangoUeditor3-master,需要改为DjangoUeditor 3、进入DjangoUeditor目录,把Djan…