分治法,迭代与动态规划及贪心算法感悟

2023-05-16

分治法,动态规划法,贪心算法这三者之间有类似之处,比如都需要将问题划分为一个个子问题,然后通过解决这些子问题来解决最终问题。但其实这三者之间的区别还是蛮大的。

 

1.分治法

    分治法(divide-and-conquer):将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

   分治模式在每一层递归上都有三个步骤:

  • 分解(Divide):将原问题分解成一系列子问题;

  • 解决(conquer):递归地解各个子问题。若子问题足够小,则直接求解;

  • 合并(Combine):将子问题的结果合并成原问题的解。

 

   合并排序(merge sort)是一个典型分治法的例子。其对应的直观的操作如下:

  • 分解:将n个元素分成各含n/2个元素的子序列;

  • 解决:用合并排序法对两个子序列递归地排序;

  • 合并:合并两个已排序的子序列以得到排序结果。

 

2. 动态规划法

   动态规划算法的设计可以分为如下4个步骤:

  • 描述最优解的结构

  • 递归定义最优解的值

  • 自底向上的方式计算最优解的值

  • 由计算出的结果构造一个最优解

     分治法是指将问题划分成一些独立地子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解。与此不同,动态规划适用于子问题独立且重叠的情况,也就是各子问题包含公共的子子问题。在这种情况下,若用分治法则会做许多不必要的工作,即重复地求解公共的子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。

   适合采用动态规划方法的最优化问题中的两个要素:最优子结构和重叠子问题。 

   最优子结构:如果问题的一个最优解中包含了子问题的最优解,则该问题具有最优子结构。

       重叠子问题:适用于动态规划求解的最优化问题必须具有的第二个要素是子问题的空间要很小,也就是用来求解原问题的递归算法课反复地解同样的子问题,而不是总在产生新的子问题。对两个子问题来说,如果它们确实是相同的子问题,只是作为不同问题的子问题出现的话,则它们是重叠的。

    “分治法:各子问题独立   动态规划:各子问题重叠”

https://my.oschina.net/feistel/blog/1633592

     算法导论: 动态规划要求其子问题既要独立又要重叠,这看上去似乎有些奇怪。虽然这两点要求听起来可能矛盾的,但它们描述了两种不同的概念,而不是同一个问题的两个方面。如果同一个问题的两个子问题不共享资源,则它们就是独立的。对两个子问题俩说,如果它们确实是相同的子问题,只是作为不同问题的子问题出现的话,是重叠的,则它们是重叠的。

递归算法就是通过解决同一问题的一个或多个更小的实例来最终解决一个大问题的算法。为了在C语言中实现递归算法,常常使用递归函数,也就是说能调用自身的函数。递归程序的基本特征:它调用自身(参数的值更小),具有终止条件,可以直接计算其结果。

      在使用递归程序时,我们需要考虑编程环境必须能够保持一个其大小与递归深度成正比例的下推栈。对于大型问题,这个栈需要的空间可能妨碍我们使用递归的方法。

     一个递归模型为分治法,最本质的特征就是:把一个问题分解成独立的子问题。如果子问题并不独立,问题就会复杂的多,主要原因是即使是这种最简单算法的直接递归实现,也可能需要难以想象的时间,使用动态规划技术就可以避免这个缺陷。

     例如,斐波那契数列的递归实现如下:

    int F(int i)

    {

             if(i < 1)  return 0;

             if(i == 1) return 1;

              return F(i-1) + F(i - 2);

    }

    千万不要使用这样的程序,因为它的效率极低,需要指数级时间。相比之下,如果首先计算前N个斐波那契数,并把它们存储在一个数组中,就可以使用线性时间(与N成正比)计算F。

      F[0] = 0;F[1] = 1;

      for(i = 2; i <= N; i++)

            F[i] = F[i-1] + F[i-2];

     这个技术给了我们一个获取任何递归关系数值解的快速方法,在斐波那契数的例子中,我们甚至可以舍弃数组,只需要保存前两个值。

     由上面的讨论我们可以得出这样的结论:我们可以按照从最小开始的顺序计算所有函数值来求任何类似函数的值,在每一步使用先前已经计算出的值来计算当前值,我们称这项技术为自底向上的动态规划。只要有存储已经计算出的值的空间,就能把这项技术应用到任何递归计算中,就能把算法从指数级运行时间向线性运行时间改进。

    自顶向下的动态规划甚至是一个更简单的技术,这项技术允许我们执行函数的代价与自底向上的动态规划一样(或更小),但是它的计算是自动的。我们实现递归程序来存储它所计算的每一个值(正如它最末的步骤),并通过检查所存储的值,来避免重新计算它们的任何项(正如它最初的步骤)。这种方法有时也称作为备忘录法。

                       斐波那契数(动态规划)

通过把所计算的值存储在递归过程的外部数组中,明确地避免重复计算。这一程序计算的时间与N成正比。

                  int F(int i)

                  {

                          if(knownF[i] != unknown)

                                 return knownF[i];

                          if(i == 0) t = 0;

                          if(i == 1) t = 1;

                          if(i > 1)  t = F(i - 1) + F(i - 2);

                          return knownF[i] = t;

                  }

       性质:动态规划降低了递归函数的运行时间,也就是减少了计算所有小于或等于给定参数的递归调用所要求的时间,其中处理一次递归调用的时间为常量。

       我们不需要把递归参数限制到单整形参数的情况。当有一个带有多个整形参数的函数时,可以把较小子问题的解存储在多维数组中,一个参数对应数组的一维。其他那些完全不涉及整形参数的情形,就使用抽象的离散问题公式,它能让我们把问题分解为一个个的小问题。

      在自顶向下的动态规划中,我们存储已知的值;在自底向上的动态规划中,我们预先计算这些值。我们常常选择自顶向下的动态规划而不选自底向上动态规划,其原因如下:

     1 自顶向下的动态规划是一个自然的求解问题的机械转化。

     2 计算子问题的顺序能自己处理。

     3 我们可能不需要计算所有子问题的解。

     我们不能忽视至关重要的一点是,当我们需要的可能的函数值的数目太大以至于不能存储(自顶向下)或预先计算(自底向上)所有值时,动态规划就会变得低效。自顶向下动态规划确实是开发高效的递归算法实现的基本技术,这类算法应纳入任何从事算法设计与实现所需的工具箱。

3. 贪心算法

    对许多最优化问题来说,采用动态规划方法来决定最佳选择有点“杀鸡用牛刀”了,只要采用另一些更简单有效的算法就行了。贪心算法是使所做的选择看起来都是当前最佳的,期望通过所做的局部最优选择来产生出一个全局最优解。贪心算法对大多数优化问题来说能产生最优解,但也不一定总是这样的。

    贪心算法只需考虑一个选择(亦即,贪心的选择);在做贪心选择时,子问题之一必须是空的,因此只留下一个非空子问题。

    贪心算法与动态规划与很多相似之处。特别地,贪心算法适用的问题也是最优子结构。贪心算法与动态规划有一个显著的区别,就是贪心算法中,是以自顶向下的方式使用最优子结构的。贪心算法会先做选择,在当时看起来是最优的选择,然后再求解一个结果子问题,而不是先寻找子问题的最优解,然后再做选择。

        贪心算法是通过做一系列的选择来给出某一问题的最优解。对算法中的每一个决策点,做一个当时看起来是最佳的选择。这一点是贪心算法不同于动态规划之处。在动态规划中,每一步都要做出选择,但是这些选择依赖于子问题的解。因此,解动态规划问题一般是自底向上,从小子问题处理至大子问题。贪心算法所做的当前选择可能要依赖于已经做出的所有选择,但不依赖于有待于做出的选择或子问题的解。因此,贪心算法通常是自顶向下地做出贪心选择,不断地将给定的问题实例归约为更小的问题。贪心算法划分子问题的结果,通常是仅存在一个非空的子问题。

https://juejin.im/post/5d859087f265da03bd055832

原文:http://hxrs.iteye.com/blog/1055478

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分治法,迭代与动态规划及贪心算法感悟 的相关文章

随机推荐

  • C++11和c++14 新特性

    https www jianshu com p b8a36ff7ac27 1 nullptr nullptr 出现的目的是为了替代 NULL 传统 C 43 43 会把 NULL 0 视为同一种东西 nullptr 关键字 xff0c 专门
  • c++11并发与多线程(第一讲)

    一 xff1a 并发 进程的基本概念 并发 xff0c 线程进程要求必须掌握 1 1 并发 两个或者更多任务 xff08 独立的活动 xff09 同时发生 xff08 进行 xff09 xff1a 一个程序同时执行多个独立的任务 以前计算机
  • c++11并发与多线程(第二讲)

    范例演示线程运行的开始和结束 程序运行起来 xff0c 生成一个进程 xff0c 该进程所属的主线程开始自动运行 xff1b 实际上是主线程执行 xff0c 主线程从mian函数开始执行 xff0c 函数返回则线程执行结束 include
  • c++11多线程编程(第三讲)

    include lt iostream gt include lt thread gt include lt string gt using namespace std 自己创建一个线程 xff0c 也需要从一个函数开始运行 xff1b v
  • Prometheus 到底 NB 在哪里?- 每天5分钟玩转 Docker 容器技术(84)

    本节讨论 Prometheus 的核心 xff0c 多维数据模型 我们先来看一个例子 比如要监控容器 span style background color rgb 216 216 216 webapp1 span 的内存使用情况 xff0
  • 解读人生的四种汉堡模型

    解读人生的四种汉堡模型 自然界给了每一个人幸福的机会 xff0c 人们都知道 xff0c 却不知如何得到它 克劳狄 年度最重要的壁球赛就要临近了 我每天的训练已经极度艰苦 xff0c 同时还要严格控制饮食 尽管我的饮食习惯已经相当健康 xf
  • 进程间通信———共享内存的原理

    这篇写的不错给出链接 https blog csdn net ljianhui article details 10253345 下图是两个进程间使用共享内存通信的示意图 xff1a 进程A和进程B在操作系统os中都有自己的虚拟内存空间 这
  • linux进程间通信-共享内存

    linux进程间通信 共享内存 一 共享内存介绍 共享内存可以从字面上去理解 xff0c 就把一片逻辑内存共享出来 xff0c 让不同的进程去访问它 xff0c 修改它 共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式
  • Linux ipcs命令与ipcrm命令的用法详解

    以下是对Linux中的ipcs命令与ipcrm命令的用法进行了介绍 xff0c 需要的朋友可以过来参考下 是linux uinx上提供关于一些进程间通信方式的信息 xff0c 包括共享内存 xff0c 消息队列 xff0c 信号 ipcs用
  • c++11多线程编程 condition_variable wait notify_one notify_all 第八讲

    1 条件变量std condition variable wait notify one notify all wait 用来等一个东西 1 如果第二个参数返回值是true xff0c 那么这一行就继续往下运行 2 如果第二个参数返回值是f
  • c++11多线程 async、future、packaged_task、promise 第九讲

    1 std async std future创建后台任务并返回 2 std packaged task 3 std promise 4 小结 一 std async std future创建后台任务并返回 希望线程返回一个结果 xff1b
  • c++11多线程 windows临界区、其他各种mutex互斥量 第十二讲

    1 windows临界区 2 多次进入临界区实验 3 自动析构技术 4 recursive mutex递归的独占互斥量 5 带超时的互斥量std timed mutex和std recursive timed mutex include l
  • python 入门 第一讲 9种数据类型

    python语言简洁 计算1 100的整数和 result 61 0 for in range 1 100 result 43 61 i print s 计算并输出n xff01 def fact n if n 61 61 1 return
  • python 入门 第二讲 读取日志文件分析

    https www bilibili com video av77410524 p 61 29 计算传感器日志文件中温度数据的平均值 日志文件包含1万条数据温度数据在文件的第3列输入 xff1a 日志文件 sensor data txt输出
  • python 入门 第三讲 读取中文分析

    统计中文词语出现的次数 以政府一号文件为例 xff0c 统计出现的中文词语数量按照一定标准输出 xff0c 如出现次数等需要解决中文分词问题 xff0c 如 xff1a 这是一门好课 gt 这是 一门 好课 输入 xff1a 2018年一号
  • docker命令

    1 docker version docker version 显示 Docker 版本信息 docker version 例如 xff1a span class token comment docker version span Clie
  • python 入门 第四讲 基本数据类型

    python主要包括9中基本数据类型 数值类型 xff1a 整数 浮点数 复数字节类型 xff1a 字符串 字节串组合类型 xff1a 集合 元祖 列表 字典 为什么会出现不确定尾数 xff1f 计算机 不确定尾数问题来源于浮点数在计算机中
  • Linux - Shell - 在多个文件中查找关键字

    https www cnblogs com xy14 p 11735343 html 1 概述 在多个文件中 查找内容 2 想干啥 目的 在 多个文件 中 查找内容准备 之前在 单个文件里 查找过内容 工具 awk 前提 文件有固定格式查找
  • protobuf repeated数组类型的使用

    http www cppblog com API archive 2014 12 09 209070 aspx protobuf是Google开发的一个序列化框架 xff0c 类似XML xff0c JSON xff0c 基于二进制 xff
  • 分治法,迭代与动态规划及贪心算法感悟

    分治法 xff0c 动态规划法 xff0c 贪心算法这三者之间有类似之处 xff0c 比如都需要将问题划分为一个个子问题 xff0c 然后通过解决这些子问题来解决最终问题 但其实这三者之间的区别还是蛮大的 1 分治法 分治法 xff08 d