贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

在开始之前我们引入一个很简单的问题,这个问题要求用尽可能少的硬币和纸币加出一个指定的金额总数。

首先我们会尽量从币值最大的地方开始,依次往下处理,下面附上代码:

# 100美元购买物品,找钱的程序

denom = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 25, 10, 5, 1]

owed = 9876

payed = []

for d in denom:

while owed >= d:

owed -= d

payed.append(d)

print(sum(payed))

print(payed)

编译之后,会输出如下结果:

9876

[5000, 2000, 2000, 500, 200, 100, 50, 25, 1]

但是这个解决方案很脆弱,稍微改变一下货币表的内容,可能就会遭到破坏。

下面说一下整数背包问题。

你可以把整数背包视为找零钱问题的泛化版。

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。

背包问题一般分为两类:

分数背包问题和整数背包问题。

分数背包问题:

分数背包问题其实可以看做背包问题的最简单的一种,因为这里的对象可以进行分割,只选取一部分。

比如,去野餐,在背包里面放什么,可以放金沙,威士忌和水。

我们先放金沙,放完了金沙,再放威士忌,因为威士忌价值介于两者之间,最后在放水。

其实分数背包问题的重点就是要找到权重比。

按照权重比排序,然后按照从高到低的顺序一个一个装包就好了。

整数背包问题:

整数背包问题可以分为无边界和有边界的情况。

有边界的情况会假设每个类别中的对象是固定的,无边界情况下对象时我们想要多少就用多少。

贪心策略在这两种情况下都不可行,都属于尚未解决的问题。现在还没有任何复杂度在多项式级以内的算法来解决他们。

其实有更好的解决方案,比如动态规划可以设计出伪多项式级别的时间复杂度的方案。

现在我们开始引入哈弗曼算法:

当我们在构建平衡二叉树的时候,我们会意识到平衡二叉树结构是在发生概率均匀分布的前提下构建的。

其实这个平衡二叉树的问题构建在压缩领域也有应用。比如压缩领域致力于用某种可变长度的编码来表示文本,使其形式上显得更为紧凑。在表示形式中,文本的每个字符都会有自己出现的概率,而我们将根据这些概率信息为其分配不同长度的字符编码。从而实现了文本长度的最小化。

下面给出具体的算法实现:

# 哈弗曼算法

from heapq import heapify, heappush, heappop

from itertools import count

def huffman(seq, frq):

num = count()

trees = list(zip(frq, num, seq))

heapify(trees)

while len(trees) > 1:

fa, _, a = heappop(trees)

fb, _, b = heappop(trees)

n = next(num)

heappush(trees, (fa+fb, n, [a, b]))

return trees[0][-1]

seq = "abcdefghi"

frq = [4, 5, 6, 9, 11, 12, 15, 16, 20]

print(huffman(seq, frq))

以上的输出结果:

[['i', [['a', 'b'], 'e']], [['f', 'g'], [['c', 'd'], 'h']]]

这个算法使用了堆结构(导入了heapq模块)。

上述算法中反复选取,合并两个最小无序列表项表示是一个平方级别的操作(线性级别的选取,乘以线性级别的迭代),我们通过使用堆结构简化为线性对数级的操作(对数级别的选取和重新添加操作)。

添加了元祖“概率,树”,在概率各不相同的情况下就能进行操作。但是有两颗树发生的概率相同时。堆结构就必须找出比较小的那个,这时候我们就遇到了不确定的比较操作。

但是Python中不兼容对象之间不能比较。所以我们就在增加一个字段用来区别其他对象。

这时候如果应用到文本的压缩和解压缩中,我们要进行一些处理和加工。比如要对个字符出现的概率进行计数。

下面附上实现,其中计数可以调用collections磨矿中的Counter类:

# 从哈弗曼树中提取出哈弗曼编码

def codes(tree, prefix=""):

if len(tree) == 1:

yield (tree, prefix)

return

for bit, child in zip("01", tree):

for pair in codes(child, prefix + bit):

yield pair

这时候还要验证贪心算法的正确性,这个时候可以用归纳法来证明。证明一般分为两个部分:贪心选择性和最优子结构。

贪心选择性是指,每次我们都通过贪心选择得到了一个最有解决方案的一部分。

最优子结构是指,我们做出选择之后剩下的问题和原有的问题有着同样的解决方案。

至于哈弗曼算法的证明,这里就不再写出详细的过程了。

然后看下一个问题,这里我们引入最小生成树问题。

最小生成树是指,一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。

这里会引入两个新的算法Kruskal和Prim算法。

我们先看一个最短边的问题。

这是一个欧几里得图形的最小生成树(加粗部分)。

因为(e, i)为最短边,而且生成树中必须包含(e, i)节点,所以肯定包含两点之间的路径。如果我们将(e, i)加入到回路中,就会出现环路。所以我们为了使该生成树回归正常,我们要一出一天边。因为(e, i)是最短的边,所以移除其他任何一条边所产生的生成树都会小于我们原先的数据结构。

最小生成树一定包含最短边,这其实也是Kruskal算法背后的基本思想。

我们继续看,b必须要进行连接,但是b只能和d和a点相连,看起来短边会更好一些。然后我们假设(b, a)是更好地选择,然后把他加入到结构中,从而形成环路,但是我们移除掉这个边,我们会发现所得的另一颗生成树会因为选择了更短的边而变得更小了。这个时候我们就假设错误了。所以不包含(b, d)的生成树不可能是最小生成树。这其实是Prim算法背后的思想。

然后我们先看Kruskal算法:

这个算法是先对图中的边进行排序,然后着手进行选取。由于这回要寻找的是短边,所以按照长度递增顺序进行排序。

这里最重要的问题就是要检查出会导致解决方案无效的边。

这时候我们通过标记解决方案中的每个节点来了解各自所属的部分,然后从每个部分的节点中选择一个当做代表。然后让该部分中的所有节点都指向它。

下面是代码实现:

# Kruskal算法实现的朴素版

def native_find(C, u):

while C[u] !=u:

u = C[u]

return u

def native_union(C, u, v):

u = native_find(C, u)

v = native_find(C, v)

C[u] = v

def native_kruskal(G):

E = [(G[u][v], u, v) for u in G for v in G[u]]

T = set()

C = {u:u for u in G}

for _, u, v in sorted(E):

if native_find(C, u) != native_find(C, v):

T.add((u, v))

na

其实这个算法还有改进的地方,最坏的情况下,我们用来跟踪引用链的naive_find()可能是线性级别的函数,在两个部分之间,我们让native_union()总是把较小的那个指向较大的那个,来寻找平衡。

我们也可以把他们直接看做一组平衡树,然后赋予各个节点某个高度。

这样子,调用native_find()和native_union()的整体操作时间应该为O(mlgn)。

进行优化之后的代码:

# Kruskal算法

def find(C, u):

if C[u] != u:

C[u] = find(C, C[u])

return C[u]

def union(C, R, u, v):

u, v = find(C, u), find(C, v)

if R[u] > R[v]:

C[v] = u

else:

C[u] = v

if R[u] == R[v]:

R[v] += 1

然后继续看Prim算法:

Prim算法的主要思路是从某个起始节点开始对目标图结构进行遍历,并始终将最短的链接加入到相对应的树结构中。

然后看具体的实现代码:

# Prim算法

from heapq import heappop, heappush

def prim(G, s):

P, Q = {}, [(0, None, s)]

while Q:

_, p, u = heappop(Q)

if u in P:

continue

P[u] = p

for v, w in G[u].items():

heappush(Q, (w, u, v))

return P

到这里,贪心算法的一些问题和一些算法的实现就说的差不多了。

这里说一点额外的。尽管在一般情况下,贪心算法的正确性是通过归纳法证明的,但是这个也可以使用一些额外的方法去做。

第一种方案是保持领先策略。

主要思想是,证明我们在一步一步构建出属于自己的解决方案时,贪心算法始终会越来越逼近某个家乡的最优解决方案。在到达终点时,自然而然就被证明是最优的算法了。

第二个方案是尽量做到完美。

这个方案,前面展示哈弗曼算法的贪心选择属性的时候就用过了。主要是要考虑如何将一个假想的最佳解决方案无伤效率地转化为一种贪心算法。、

第三个方案就是做好安全措施。

主要思想就是,确保贪心算法的正确性是我们一切工作的起点,而且一定要确保自己的这一路每一步所采用的贪心策略都是安全的。

这里就说这么多吧。

谢谢大家关注。

天气寒冷,大家注意身体。

python贪心算法几个经典例子_关于贪心算法的一些探讨、经典问题的解决和三种典型的贪心算法算法(哈弗曼,Kruskal,Prim)的Python实现。...相关推荐

  1. python函数拟合不规则曲线_python 对任意数据和曲线进行拟合并求出函数表达式的三种解决方案...

    第一种是进行多项式拟合,数学上可以证明,任意函数都可以表示为多项式形式.具体示例如下. ###拟合年龄 import numpy as np import matplotlib.pyplot as p ...

  2. AI领域3种典型的深度学习算法

    2019-11-23 10:38:48 ​深度学习(Deep Learning)是机器学习(Machine Learning)领域中一个新的研究方向,引领了第三次人工智能的浪潮. 本文整理了深度学习领 ...

  3. 三种强大的物体识别算法——SIFT/SURF、haar特征、广义hough变换的特性对比分析

    识别算法概述: SIFT/SURF基于灰度图, 一.首先建立图像金字塔,形成三维的图像空间,通过Hessian矩阵获取每一层的局部极大值,然后进行在极值点周围26个点进行NMS,从而得到粗略的特征点, ...

  4. python语言的三种数字类型_Python语言中的类型之数字类型--Python(10)

    数据从不一样的角度能够用不一样的含义去解释它,如: 10,011,101 对上面这个数据,咱们有以下多种含义去解释它: 1)它是1个二进制数字,或者是1个十进制数字: 2)一段文本: 3)用 , 号分 ...

  5. 用python计算今天是今年的第几天_Python计算指定日期是今年的第几天(三种方法)...

    今天早上和腾讯面试官进行了视频面试,由于音量和网络以及我的垃圾电脑的原因,个人感觉黄了... 最后面试官给了我一道简单的计算题:指定日期是今年的第几年 由于电脑卡到打字都打不动,我勉勉强强写了一点,虽 ...

  6. python对文件的追加写模式_Python中文件的读写、写读和追加写读三种模式的特点...

    本文主要讨论一下文件的三种可读可写模式的特点及互相之间的区别,以及能否实现修改文件的操作 由于前文已经讨论过编码的事情了,所以这里不再研究编码,所有打开操作默认都是utf-8编码(Linux系统下) ...

  7. java贪心算法几个经典例子_经典算法思想5——贪心(greedy algorithm)

    贪心算法,是指在对问题求解时,总是做出再当前看来是最好的选择.也就是说,不从整体最优上加以考虑,他所做出的仅是某种意义上的局部最优解. 贪心算法没有固定算法框架,算法设计的关键是贪心策略的选择.必须注 ...

  8. 信度和效度经典例子_浅析经典目标检测评价指标--mmAP(一)

    大家好,我是旷视科技南京研究院研究员赵博睿,主要研究领域为目标检测.今天和大家聊聊mmAP的那些事- 目标检测是计算机视觉领域的一项基础问题,在许多智能场景的落地应用中目标检测通常都是视觉感知的第一步 ...

  9. 博弈论66个经典例子_「百大管理学定律」博弈论Game Theory

    博弈论(Game Theory),博弈论是指研究多个个体或团队之间在特定条件制约下的对局中利用相关方的策略,而实施对应策略的学科.有时也称为对策论,或者赛局理论,是研究具有斗争或竞争性质现象的理论和方 ...

最新文章

  1. 【Silverlight】Bing Maps开发应用与技巧三:Bing Maps Silverlight Control的离线开发
  2. python网课什么平台好-python网课什么平台好
  3. 学习Windows编程遇到的问题
  4. leetcode -- 1091. 二进制矩阵中的最短路径
  5. RESTful 架构风格概述
  6. HDU4669_Mutiples on a circle
  7. IdentityServer4(六)授权码流程原理之SPA
  8. leetcode - 983. 最低票价
  9. iOS开发:创建真机调试证书
  10. 对于一组给定的叶子结点_高糊图片可以做什么?Goodfellow等人用它生成一组合理图像...
  11. 如果要在mFC客户区添加控件怎么办
  12. Atitit object 和class的理解 目录 1.1. 发现很多Object的方法都是相同的,他们被重复地放在一个个对象当中,太浪费了。 1 1.2. 那我们怎么把这些Object给创建起来
  13. eclipse中文版界面设置黑色_Eclipse设置黑色主题
  14. [预警]WebSOC多漏洞插件检测 预防勒索病毒“Satan”新变种
  15. 乱谈企业化信息规划与实施
  16. 美国三大股指再创新高:纳指开盘上涨0.29%
  17. C++:Timer类实现
  18. oracle怎么截取long类型,如何把long类型的值取出来
  19. Turtle 画正方形螺旋线
  20. VB中如何声明及使用多维数组,多层数组及动态数组

热门文章

  1. 高德地图发布AI引擎 可提供最优位置出行服务
  2. drf框架使用之 路飞学城(第一天)
  3. 我的ArchLinux使用反馈--(更新暂停-入Mac)
  4. Virtual box中win7分辨率问题解决
  5. 计算机网络实验:PPP配置与分析
  6. 孕妇练功法——三线放松功
  7. 虚拟直播与光学动作捕捉技术
  8. erp服务器和文件服务器,erp是用本地服务器还是云
  9. 【33】Android WebView加载html5 3D全景
  10. 解决VS调试web项目启动谷歌浏览器“无标题”、“已崩溃”问题