贪心算法精讲

一.贪心算法的基本概念

    当一个问题具有最优子结构性质时,我们会想到用动态规划法去解它。但有时会有更简单有效的算法。我们来看一个找硬币的例子。假设有四种硬币,它们的面值分别为二角五分、一角、五分和一分。现在要找给某顾客六角三分钱。这时,我们会不假思索地拿出2个二角五分的硬币,1个一角的硬币和3个一分的硬币交给顾客。这种找硬币方法与其他的找法相比,所拿出的硬币个数是最少的。这里,我们下意识地使用了这样的找硬币算法:首先选出一个面值不超过六角三分的最大硬币,即二角五分;然后从六角三分中减去二角五分,剩下三角八分;再选出一个面值不超过三角八分的最大硬币,即又一个二角五分,如此一直做下去。这个找硬币的方法实际上就是贪心算法。顾名思义,贪心算法总是作出在当前看来是最好的选择。也就是说贪心算法并不从整体最优上加以考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,我们希望贪心算法得到的最终结果也是整体最优的。上面所说的找硬币算法得到的结果就是一个整体最优解。找硬币问题本身具有最优子结构性质,它可以用动态规划算法来解。但我们看到,用贪心算法更简单,更直接且解题效率更高。这利用了问题本身的一些特性。例如,上述找硬币的算法利用了硬币面值的特殊性。如果硬币的面值改为一分、五分和一角一分3种,而要找给顾客的是一角五分钱。还用贪心算法,我们将找给顾客1个一角一分的硬币和4个一分的硬币。然而3个五分的硬币显然是最好的找法。虽然贪心算法不是对所有问题都能得到整体最优解,但对范围相当广的许多问题它能产生整体最优解。如图的单源最短路径问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,但其最终结果却是最优解的很好的近似解。

二.求解活动安排问题算法

    活动安排问题是可以用贪心算法有效求解的一个很好的例子。该问题要求高效地安排一系列争用某一公共资源的活动。贪心算法提供了一个简单、漂亮的方法使得尽可能多的活动能兼容地使用公共资源。

    设有n个活动的集合e={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si<fi。如果选择了活动i,则它在半开时间区间[si,fi]内占用资源。若区间[si,fi]与区间[sj,fj]不相交,则称活动i与活动j是相容的。也就是说,当si≥fi或sj≥fj时,活动i与活动j相容。活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合。

    在下面所给出的解活动安排问题的贪心算法gpeedyselector中,各活动的起始时间和结束时间存储于数组s和f{中且按结束时间的非减序:.f1≤f2≤…≤fn排列。如果所给出的活动未按此序排列,我们可以用o(nlogn)的时间将它重排。

template< class type>

void greedyselector(int n, type s[ 1, type f[ ], bool a[ ] ]

 { a[ 1 ] = true;

 int j = 1;

 for (int i=2;i< =n;i+ + ) {

     if (s[i]>=f[j]) {

        a[i] = true;

        j=i;

}

else a[i]= false;

}

}

    算法greedyselector中用集合a来存储所选择的活动。活动i在集合a中,当且仅当a[i]的值为true。变量j用以记录最近一次加入到a中的活动。由于输入的活动是按其结束时间的非减序排列的,fj总是当前集合a中所有活动的最大结束时间,即:

     

    贪心算法greedyselector一开始选择活动1,并将j初始化为1。然后依次检查活动i是否与当前已选择的所有活动相容。若相容则将活动i加人到已选择活动的集合a中,否则不选择活动i,而继续检查下一活动与集合a中活动的相容性。由于fi

总是当前集合a中所有活动的最大结束时间,故活动i与当前集合a中所有活动相容的充分且必要的条件是其开始时间s 不早于最近加入集合a中的活动j的结束时间fj,si≥fj。若活动i与之相容,则i成为最近加人集合a中的活动,因而取代活动j的位置。由于输人的活动是以其完成时间的非减序排列的,所以算法greedyselector每次总是选择具有最早完成时间的相容活动加入集合a中。直观上按这种方法选择相容活动就为未安排活动留下尽可能多的时间。也就是说,该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。算法greedyselector的效率极高。当输人的活动已按结束时间的非减序排列,算法只需g(n)的时间来安排n个活动,使最多的活动能相容地使用公共资源。

例:设待安排的11个活动的开始时间和结束时间按结束时间的非减序排列如下:

i

1

2

3

4

5

6

7

8

9

10

11

s[i]

1

3

0

5

3

5

6

8

8

2

12

f[i]

4

5

6

7

8

9

10

11

12

13

14

 

算法greedyselector的计算过程如图所示。

 

    图中每行相应于算法的一次迭代。阴影长条表示的活动是已选人集合a中的活动,而空白长条表示的活动是当前正在检查其相容性的活动。若被检查的活动i的开始时间si小于最近选择的活动了的结束时间fj,则不选择活动i,否则选择活动i加入集合a中。

三.算法分析

    贪心算法并不总能求得问题的整体最优解。但对于活动安排问题,贪心算法greedyse—1ector却总能求得的整体最优解,即它最终所确定的相容活动集合a的规模最大。我们可以用数学归纳法来证明这个结论。

    事实上,设e={1,2,…,n}为所给的活动集合。由于正中活动按结束时间的非减序排列,故活动1具有最早的完成时间。首先我们要证明活动安排问题有一个最优解以贪心选择开始,即该最优解中包含活动1。设 是所给的活动安排问题的一个最优解,且a中活动也按结束时间非减序排列,a中的第一个活动是活动k。若k=1,则a就是一个以贪心选择开始的最优解。若k>1,则我们设 。由于f1≤fk,且a中活动是互为相容的,故b中的活动也是互为相容的。又由于b中活动个数与a中活动个数相同,且a是最优的,故b也是最优的。也就是说b是一个以贪心选择活动1开始的最优活动安排。因此,我们证明了总存在一个以贪心选择开始的最优活动安排方案。

    进一步,在作了贪心选择,即选择了活动1后,原问题就简化为对e中所有与活动1相容的活动进行活动安排的子问题。即若a是原问题的一个最优解,则a’=a—{i}是活动安排问题 的一个最优解。事实上,如果我们能找到e’的一个解b’,它包含比a’更多的活动,则将活动1加入到b’中将产生e的一个解b,它包含比a更多的活动。这与a的最优性矛盾。因此,每一步所作的贪心选择都将问题简化为一个更小的与原问题具有相同形式的子问题。对贪心选择次数用数学归纳法即知,贪心算法greedyselector最终产生原问题的一个最优解。

四.贪心算法的基本要素

    贪心算法通过一系列的选择来得到一个问题的解。它所作的每一个选择都是当前状态下某种意义的最好选择,即贪心选择。希望通过每次所作的贪心选择导致最终结果是问题的一个最优解。这种启发式的策略并不总能奏效,然而在许多情况下确能达到预期的目的。解活动安排问题的贪心算法就是一个例子。下面我们着重讨论可以用贪心算法求解的问题的一般特征。

    对于一个具体的问题,我们怎么知道是否可用贪心算法来解此问题,以及能否得到问题的一个最优解呢?这个问题很难给予肯定的回答。但是,从许多可以用贪心算法求解的问题中

我们看到它们一般具有两个重要的性质:贪心选择性质最优子结构性质

1.贪心选择性质

    所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。在动态规划算法中,每步所作的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能作出选择。而在贪心算法中,仅在当前状态下作出最好选择,即局部最优选择。然后再去解作出这个选择后产生的相应的子问题。贪心算法所作的贪心选择可以依赖于以往所作过的选择,但决不依赖于将来所作的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,我们必须证明每一步所作的贪心选择最终导致问题的一个整体最优解。通常可以用我们在证明活动安排问题的贪心选择性质时所采用的方法来证明。首先考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步作贪心选择,最终可得到问题的一个整体最优解。其中,证明贪心选择后的问题简化为规模更小的类似子问题的关键在于利用该问题的最优子结构性质。

2.最优子结构性质

    当一个问题的最优解包含着它的子问题的最优解时,称此问题具有最优子结构性质。问题所具有的这个性质是该问题可用动态规划算法或贪心算法求解的一个关键特征。在活动安排问题中,其最优子结构性质表现为:若a是对于正的活动安排问题包含活动1的一个最优解,则相容活动集合a’=a—{1}是对于e’={i∈e:si≥f1}的活动安排问题的一个最优解。

3.贪心算法与动态规划算法的差异

    贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。但是,对于一个具有最优子结构的问题应该选用贪心算法还是动态规划算法来求解?是不是能用动态规划算法求解的问题也能用贪心算法来求解?下面我们来研究两个经典的组合优化问题,并以此来说明贪心算法与动态规划算法的主要差别。

五. 0-背包问题

给定n种物品和一个背包。物品i的重量是w ,其价值为v ,背包的容量为c.问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。

    此问题的形式化描述是,给定c>0,wi>0,vi>0,1≤i≤n,要求找出一个n元0—1向

量(xl,x2,…,xn), ,使得 ≤c,而且 达到最大。

      背包问题:与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包。

    此问题的形式化描述是,给定c>0,wi>0,vi>0,1≤i≤n,要求找出一个n元向量

(x1,x2,...xn),0≤xi≤1,1≤i≤n 使得 ≤c,而且 达到最大。

    这两类问题都具有最优子结构性质。对于0—1背包问题,设a是能够装入容量为c的背包的具有最大价值的物品集合,则aj=a-{j}是n-1个物品1,2,…,j—1,j+1,…,n可装入容量为c-wi叫的背包的具有最大价值的物品集合。对于背包问题,类似地,若它的一个最优解包含物品j,则从该最优解中拿出所含的物品j的那部分重量wi,剩余的将是n-1个原重物品1,2,…,j-1,j+1,…,n以及重为wj-wi的物品j中可装入容量为c-w的背包且具有最大价值的物品。

    虽然这两个问题极为相似,但背包问题可以用贪心算法求解,而0·1背包问题却不能用贪心算法求解。用贪心算法解背包问题的基本步骤是,首先计算每种物品单位重量的价值

vj/wi然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过c,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直进行下去直到背包装满为止。具体算法可描述如下:

 void knapsack(int n, float m, float v[ ], float w[ ], float x[ ] )

 sort(n,v,w);

 int i;

 for(i= 1;i<= n;i++) x[i] = o;

 float c = m;

 for (i = 1;i < = n;i ++) {

     if (w[i] > c) break;

    x[i] = 1;

    c-= w[i];

    }

 if (i < = n) x[i] = c/w[i];

算法knapsack的主要计算时间在于将各种物品依其单位重量的价值从大到小排序。因此,算法的计算时间上界为o(nlogn)。当然,为了证明算法的正确性,我们还必须证明背包问题具有贪心选择性质。 

这种贪心选择策略对0—1背包问题就不适用了。看图2(a)中的例子,背包的容量为50千克;物品1重10千克;价值60元;物品2重20千克,价值100元;物品3重30千克;价值120元。因此,物品1每千克价值6元,物品2每千克价值5元,物品3每千克价值4元。若依贪心选择策略,应首选物品1装入背包,然而从图4—2(b)的各种情况可以看出,最优的选择方案是选择物品2和物品3装入背包。首选物品1的两种方案都不是最优的。对于背包问题,贪心选择最终可得到最优解,其选择方案如图2(c)所示。

 

    对于0—1背包问题,贪心选择之所以不能得到最优解是因为它无法保证最终能将背包装满,部分背包空间的闲置使每千克背包空间所具有的价值降低了。事实上,在考虑0—1背包问题的物品选择时,应比较选择该物品和不选择该物品所导致的最终结果,然后再作出最好选择。由此就导出许多互相重叠的于问题。这正是该问题可用动态规划算法求解的另一重要特征。动态规划算法的确可以有效地解0—1背包问题。

 

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

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

相关文章

Arcgis将shp图投影坐标转换地理坐标,投影失败的问题

问题来源&#xff1a; 目的&#xff1a;shp图需要将投影坐标系去掉&#xff0c;即投影坐标系转换为地理坐标系 正常操作&#xff1a; 法1&#xff1a;使用Arcgis中工具箱-数据管理工具-投影与变换-要素-投影&#xff0c;&#xff1a;这个工具进行坐标转换 法2&#xff1a;转换…

c#调用.exe程序

using System.Diagnostics; //需要添加这行引用&#xff0c;关于线程的 private void button1_Click(object sender, EventArgs e){ string path "C:\Program Files\Tencent\QQ\QQ.exe"; //这个path就是要调用的exe程序的绝对路径 System.Diagnostics…

idea创建maven项目

如果没有配置这个参数&#xff0c;在maven生成骨架的时候将会非常慢&#xff0c;有时候直接卡住。archetypeCatalog表示插件使用的archetype元数据&#xff0c;不加这个参数时默认为remote&#xff0c;local&#xff0c;即中央仓库archetype元数据&#xff0c;由于中央仓库的ar…

Mapgis图转换为可导入软件的shp

可导入软件的矢量图格式如图&#xff0c;是shp文件&#xff0c;由6个文件构成&#xff1a; 而从Mapgis中导出的shp格式缺少文件&#xff0c;需要将其导入ArcGIS再次导出&#xff0c;补充相关文件。 另外&#xff0c;软件默认的坐标系是WGS-84&#xff0c;不过其他坐标系也可以…

mapgis格式转arcgis的shp格式

转shp 经过试验发现&#xff0c;1.投影坐标系直接转shp的时候不仅会出现位置不正确而且还会出现属性不正确&#xff0c;2.e00数据导入&#xff0c;arcmap2shp软件都会存在一些小问题&#xff0c;可能是作者操作有误导致出现一系列问题 以下介绍个人尝试后没有错误的方法 先将…

高斯投影坐标系为什么是六七八位数

坐标系分为地理坐标系和平面坐标系 地理坐标系 也叫“大地坐标系”&#xff0c;基于地球椭球体建立的坐标系&#xff0c;以经纬度表示&#xff0c; 单位是度分秒&#xff08;10824′34″&#xff09;&#xff0c;或度小数&#xff08;108.24356710&#xff09; 平面坐标系 …

五大算法之三--贪心算法

一、基本概念&#xff1a;所谓贪心算法是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;他所做出的仅是在某种意义上的局部最优解。贪心算法没有固定的算法框架&#xff0c;算法设计的关键…

神经网络与深度学习——TensorFlow2.0实战(笔记)(一)

第一章人工智能的起源和发展 人工智能是一门融合了计算机科学、数学、生物学、脑神经学、 心理学和哲学等多种学科的综合性学科。 弱人工智能 ( Artificial Narrow Intelligence , ANI) 拥有某种特定领域智能行为&#xff0c;替代人类从事某些活动。 强人工智能 ( Artificia…

地图矢量化

地图矢量化&#xff0c;将影像图中的路网、河流等绘制成shp图&#xff0c;需要放大图片细细描摹&#xff0c;工作量很大&#xff0c;因此可以先借助一些已有的公开矢量数据&#xff0c;再进行补充绘制。 下载公开矢量数据 网站&#xff1a;全国地理信息资源目录服务系统 http…

图幅号与经纬度的换算

图幅号&#xff0c;如“I48E001004”&#xff0c;一般为10位数。 前三位“I48”表示所属1:100万图幅的行列号 &#xff0c;地图基本都是在1:100万比例尺的行列式编号基础上划分的。 1:100万图幅的划分规则&#xff1a; 纬度行&#xff1a;从赤道开始算&#xff0c;纬度四度…

五大算法之二--动态规划

动态规划--简单的理解 这个算法简单的来讲就是采用自底向上的方式递推求值&#xff0c;将待求解的问题分解成若干个子问题&#xff0c;先求解子问题&#xff0c;并把子问题的解存储起来以便以后用来计算所需要求的解。简言之&#xff0c;动态规划的基本思想就是把全局的问题化为…

数据库方面的操作示例

1 连接SQL Server数据库示例 // 连接字符串string ConnectionString System.Configuration.ConfigurationSettings.AppSettings["ConnectionSqlServer"]; // 创建SqlConnection对象SqlConnection connection new SqlConnection(ConnectionString); try{// 打开数据…

神经网络与深度学习——TensorFlow2.0实战(笔记)(二)(开发环境介绍)

开发环境介绍 Python3 1.结构清晰&#xff0c;简单易学 2.丰富的标准库 3.强大的的第三方生态系统 4.开源、开放体系 5.高可扩展性&#xff1a;胶水语言 6.高可扩展性&#xff1a;胶水语言 7.解释型语言&#xff0c;实现复杂算法时效率较低 &#xff08;解释型语言是相…

用python做一个简单的投票程序_以一个投票程序的实例来讲解Python的Django框架使...

&#xff08;一&#xff09;关于Django Django是一个基于MVC构造的框架。但是在Django中&#xff0c;控制器接受用户输入的部分由框架自行处理&#xff0c;所以 Django 里更关注的是模型&#xff08;Model&#xff09;、模板(Template)和视图&#xff08;Views&#xff09;&…

做点什么吧

写了6年代码了&#xff0c;回头发现&#xff0c;虽然写了那么多&#xff0c;真正在使用的少的可怜&#xff0c;真正意义上创造性的劳动也少的可怜。大部分时间都在重复着CtrlC和CtrlV&#xff0c;感觉和产线上的工人差不了多少&#xff1f; 总得做点什么吧? 留下点什么&#x…

BSS段、数据段、代码段、堆与栈

BSS段&#xff1a;BSS段&#xff08;bss segment&#xff09;通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。 数据段&#xff1a;数据段&#xff08;data segment&#xff09;通常是指用来存放程序…

python安装模块方法_Python安装模块的几种方法

一、方法1&#xff1a; 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2&#xff1a; 多文件模块&#xff0c;带setup.py 下载模块包&#xff0c;进行解压&#xff0c;进入模块文件夹&#xff0c;执行&#xff1a; python setup.py install 三、 方法3&#xff1a;easy…

使用参数来防止SQL注入

SQL注入的威力是不可忽视的&#xff0c;下面我们主要介绍防范方法——使用参数化SQL。对于不同的数据供应器都有对就的 Parameter 来表示SQL语句或者存储过程中的各种参数。参数和数据库字段的真实类型——对应&#xff0c;所有参数的值会仅仅被认为一个参数。因此&#xff0c;…

高通qca9565网卡驱动_修改注册表让Surface Go的无线网卡支持频段选择

我的Surface Go是第一代无LTE版本&#xff0c;无线网卡型号是Qualcomm Atheros QCA61x4A&#xff0c;因为一些原因急需优先选择5GHz频段wifi的功能&#xff0c;因此写下本文。本文的解决方案仅能保证对Qualcomm Atheros QCA61x4A这一型号的网卡有效&#xff0c;对于其他同品牌不…

异或运算^和他的一个常用作用

发现一个新知识&#xff0c;介绍给大家&#xff1a; 二进制异或运算&#xff1a;两者相等为0,不等为1. 这样我们发现交换两个整数的值时可以不用第三个参数。 如a11,b9.以下是二进制 aa^b1011^10010010; bb^a1001^00101011; aa^b0010^10111001; 这样一来a9,b11了。 举一个运…