Python中的动态规划算法2025 年 1 月 5 日 | 阅读 9 分钟 动态规划 (DP) 是一种解决计算和数学问题的算法技术,通过将其分解为更小、重叠的子问题来实现。DP 对于优化问题非常有效,当你需要在众多可能的选项中找到最优答案时,例如发现最短路径、最大化/最小化某个值或计算组合数。通过只解决每个子问题一次并保存答案,DP 有助于最大限度地减少重复计算,使过程更有效。 ![]() 我们在哪里使用动态规划算法?1. 最优子结构 如果一个问题的最优解可以由其子问题的最优解构造而成,则该问题就具有最优子结构性质。动态规划利用最优子结构原理,这意味着一个问题的最佳解决方案可以从其子问题的最佳解决方案获得。 2. 重叠子问题 子问题是单独完成的任务,而重叠子问题涉及多次重复相同的任务。 动态规划存储重叠子问题的解,以避免冗余计算。 动态规划是一种解决问题的技术,它涉及到将复杂问题分解为更小的子问题,而这些子问题又可以进一步分解为更小的组件。这些子问题是重叠的,并且依赖于先前计算的值。动态规划会存储这些计算出的值,从而减少了重复计算的需要,从而节省了大量时间并提高了解决方案的速度。 动态规划方法![]()
自顶向下 (记忆化)在自顶向下方法中,您从主问题开始,然后将其分解为更小的子问题。通常会使用递归来解决这些子问题,并将它们的结果存储在缓存中以防止重复计算。每当重新遇到一个子问题时,就会从缓存中检索其先前计算的结果。这种方法通常被称为“记忆化”。 优点 记忆化存储并重用先前计算的子问题的结果,以避免冗余计算,从而显着提高性能。 易于实现:实现自顶向下的动态规划解决方案可能比自底向上的方法更简单。在后者中,需要管理用于子问题结果的表或数组。 降低复杂度:自顶向下方法通过避免用于子问题的嵌套循环或复杂索引来简化代码结构,从而得到更优雅的解决方案。 清晰度和可读性:在使用自顶向下动态规划解决问题时,问题的递归结构通常会被紧密地反映出来。因此,代码更直观且易于理解,因为它直接代表了问题分解为子问题的过程。 缺点 在自顶向下方法中使用递归时,额外的函数调用可能会引入开销,并且执行速度比使用迭代的自底向上方法慢。高度嵌套的递归调用可能导致严重的性能问题。 自顶向下方法是递归的,在跟踪已解决的子问题和记忆化的结果方面可能会遇到困难。这可能会导致调试复杂化,并使跟踪子问题状态具有挑战性。 自底向上 (制表)解决问题的自底向上方法包括先解决最小的子问题(基本情况),然后逐步构建以解决主问题。此方法不使用递归,而是使用循环来计算子问题的结果并将其存储在数组或表中。此技术也称为“制表”。 优点 可预测的空间复杂度:自底向上方法明确为存储子问题结果的表或数组分配内存,从而可以轻松预测和控制空间复杂度。这种方法更适合内存限制已知的问题。 并行性:自底向上动态规划促进了并行性,允许同时或有序地解决子问题,从而利用多个处理器或线程来加速解决方案。 无递归开销:与自顶向下方法相比,自底向上方法避免了函数调用和递归开销,从而提高了执行速度并减少了内存消耗,尤其对于递归深度大的问题。 缺点 在处理动态规划问题时,定义基本情况和表的初始化可能很棘手,尤其是在基本情况不明显的情况下。 在解决子问题时,自底向上动态规划通常需要比自顶向下方法更复杂的代码,这可能导致代码可读性降低。 当处理具有更自然递归结构的问题时,自顶向下方法可能比自底向上动态规划更灵活。后者不允许选择性计算或递归树中的分支。 与普通方法和自顶向下方法的比较让我们看一个简单的程序,它使用自顶向下方法和普通递归方法打印第 n 个斐波那契值。 自顶向下方法程序 输出 The 10th Fibonacci number is: 55 递归方法程序 输出 The 10th Fibonacci number is: 55 两种方法的解释为了获得更好的时间复杂度,通常推荐使用记忆化实现的自顶向下动态规划方法,而不是朴素的递归方法。这是因为带有记忆化的自顶向下动态规划会存储并重用先前已解决子问题的解,从而避免了不必要的计算。 递归方法 指数时间复杂度 (O(2^n))。 自顶向下 (带记忆化) 时间复杂度:O(n) 或 O(n log n)(线性或对数线性) 因此,为了优化计算斐波那契数的时间复杂度,带有记忆化的自顶向下动态规划方法是更优的选择。 动态规划算法Dijkstra 算法Dijkstra 算法是一种在图中查找节点之间最短路径的方法。它通过选择距离起始节点最近的节点,然后继续选择下一个最近的节点,直到到达目的地。这种方法基于在每个步骤中选择最佳解决方案的原则,这也称为贪婪算法。当所有边权重都为非负时,它效果很好。 相比之下,动态规划会考虑所有可能的解决方案,并选择能带来最佳整体结果的方案。它通过探索所有可能的路径并跟踪迄今为止找到的最佳解决方案来实现此目的。这种方法更耗时,但可以保证找到最优解决方案。 Bellman-Ford 算法Bellman-Ford 算法是计算机科学和图论中一个著名的算法,用于查找加权有向图中源顶点到所有其他顶点的最短路径。它可以处理具有负权重边的图,并且可以检测负权重圈,使其成为各种应用的最佳选择。 在 Bellman-Ford 算法的上下文中,它使用动态规划通过迭代松弛边来确定从源顶点到所有其他顶点的最短距离。该算法使用动态规划来确保它检查图中的所有可能路径,从而使其能够处理具有负权重边的图并识别负权重圈。这种迭代和基于松弛的策略使该算法能够有效地计算各种场景下的最短路径。 最大子数组和 (Kadane's Algorithm)Kadane 算法是一种广泛使用且高效的技术,可帮助查找一维数字数组中子数组的最大和。它在寻找最大子数组和等问题中特别有用,例如最大子数组和或相关问题,如最大子序列和。该算法对于正数和负数都有效。它的时间复杂度为 O(n),使其非常高效。 Kadane 算法是一种动态规划算法,它专门且高效地查找最大子数组和。它利用动态规划的原理来维护数组中每个位置结尾的最大子数组和以及迄今为止找到的最大子数组和。通过迭代更新这些值,它可以在不重新评估相同子问题多次的情况下找到最大子数组和。 背包问题背包问题是计算机科学和数学领域中一个著名的优化问题。在此问题中,您会得到一组具有相应重量和价值的物品,以及一个具有最大重量容量的背包。主要目标是在不超出背包容量的情况下,选择一组物品以最大化总价值。这个问题通常被称为 0/1 背包问题,因为对于每个物品,您有两个选择:要么将其放入背包(0),要么将其排除(1)。 在解决背包问题时,我们经常会遇到相同的子问题多次。例如,我们需要知道使用容量为 W 的背包和前 k 个物品的子集可获得的最大价值。这些重叠的子问题由动态规划解决,它将结果存储在表或数组中。通过重用这些结果,我们可以避免冗余计算并提高效率。 0/1 背包问题一个经典的优化问题,称为“背包问题”,旨在在控制其总重量的情况下最大化一组商品的总体价值。 在 0/1 背包问题中,您不能只取一部分物品,因为每个物品都可以被放入背包或从背包中移除。 这里有一个简单的程序,它使用动态规划来演示 0/1 背包问题。 程序输出 Maximum value in 0/1 Knapsack: 220 说明 提供的 0/1 背包问题的 Python 程序结合了动态规划,通过将其分解为更小的子问题并将它们的解存储在表中来高效地解决它。 设置表 (dp)该程序初始化一个名为 dp 的表,其维度为 (n + 1) x (capacity + 1),其中 n 是物品数量,capacity 是背包的最大容量。 使用前 i 个物品和容量为 w 的背包可以达到的最高价值由符号 dp[i][w] 表示。 自底向上填充表 (dp)然后,程序使用自底向上方法填充表。内循环遍历背包的潜在容量 (w),而外循环遍历物品 (i)。 对于每个单元格 dp[i][w],程序考虑两种可能性
最终结果来自表的右下角单元格 (dp[n][capacity]) Floyd-Warshall 算法Floyd-Warshall 算法是一种强大的动态规划算法,用于查找加权边有向图中所有顶点对之间的最短路径。该算法能够处理具有正负边权重的图,甚至可以处理带有负权重圈的图。 要使用 Floyd-Warshall 算法确定顶点 i 和 j 之间的最短路径,我们需要考虑顶点 i 和所有其他顶点 k 之间的最短路径,以及 k 和 j 之间的最短路径。该算法通过迭代地考虑所有可能的中间顶点 k 来帮助我们找到所有顶点对之间的最短路径。 Floyd-Warshall 算法使用动态规划来查找图中所有顶点对之间的最短路径。它通过考虑所有可能的中间顶点并计算递增数量的最短路径来实现。这个过程建立在先前计算的子问题解的基础上,从直接边开始。这种算法的迭代性质使其成为解决复杂图问题的强大工具。 结论动态规划是一种高度通用且必不可少的问题解决技术,在算法设计和优化中发挥着重要作用。它是一种强大的工具,可以通过将大问题分解为更小、更易于管理子问题来有效地识别最优解决方案。学习和掌握动态规划对于任何程序员或数学家来说都是一项宝贵的技能,因为它能帮助他们更高效、更有效地解决挑战性问题。总之,动态规划是一个值得学习和掌握的重要概念。 |
简介 在广阔的编程和脚本语言领域,Python 和 Bash 作为强大的工具脱颖而出,每种工具都有其独特的优点和用途。虽然两者都在自动化和脚本领域广泛使用,但它们满足不同的需求并展现出独特的特性....
阅读 4 分钟
? Matplotlib 是一个强大的 Python 图表工具包,经常用于创建可视化。有时,可能需要在单个窗口中绘制多个图形,但有时,你可能需要单独显示它们。这对于构建复杂的可视化或比较各种图表很有帮助...
阅读 4 分钟
? Python 是世界上最受欢迎的编程语言之一,为应用程序的开发和执行提供了强大的环境。使 Python 灵活易用的关键组件之一是其环境变量。在这些变量中,PYTHONPATH 环境变量...
5 分钟阅读
在以下教程中,我们将学习并讨论在 Python 中表达联合类型的各种可用方法。联合类型表达式简介 为了表明变量或函数参数可以接受多种值,Python 类型提示使用……
阅读 4 分钟
在计算机程序开发领域,性能优化通常是构建可扩展且成功的程序的关键组成部分。基准测试和分析是实现性能提升的两种关键策略。工程师可以利用这些方法来发现编码瓶颈和低效之处,以便...
阅读 6 分钟
简介 在机器学习和数据科学领域,随机森林规则集是一个强大而灵活的工具。它属于集成学习算法类别,该类别混合了多个学习模型的预测,以提供...
阅读 6 分钟
Python 中的朴素时间序列预测 朴素预测方法是销售和财务部门常用的一种最简单的需求预测形式。该方法遵循简单性原则:它假设未来的需求最好通过观察到的模式来建模……
阅读 10 分钟
在 Python 中,异步上下文管理器允许您在 async/await 情况下管理需要异步操作的对象。上下文管理器(with 语句)可以在同步上下文中创建和销毁对象;异步上下文管理器(async with)将此概念扩展到管理异步进程,例如...
阅读25分钟
Python 中的输入处理 Python 中的输入处理对于编写健壮且用户友好的程序至关重要。它涉及捕获用户输入、验证输入,并确保程序能够优雅地处理各种类型的数据和意外输入。以下是一些关键概念和技术...
11 分钟阅读
简介 数据加密标准 (DES) 是一种对称密钥分组密码算法,过去曾广泛用于数据加密。尽管由于密钥长度较短,DES 在现代加密应用中不再被认为安全,但它为学习...提供了绝佳的机会。
阅读 6 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India