解决最简化问题的演算法,其解题过程可看成是由一连串的决策步骤所组成,而每一步骤都有一组选择要选定。
贪婪演算法的特性是 每一次选择都采取区域最佳接(locally optimal solution)
,而透过每一个区域最佳解最后综合成为全域最佳解(globally optimal solution)
从而将问题解决
找出下面多级图的最短路径
则使用贪婪算法,从S到T的最短路径为:1+2+5 = 8
我们再来看一个比较复杂的例子:
我们是用贪婪算法得到的最短路径为:(S - A - D - T)
1+4+18 = 23
而真实的最短路径为:(S - C - F - T)
5+2+2 = 9
可以看出又是使用贪婪算法得出的最终路径并非是真实的最短路径。
n
个活动提出申请要使用一个场地,而这场地在同一时间点时最多只能让一个活动使用。n
个活动选一组数量最多,且可以在这场地举办的活动集。一个活动选择问题
假设我们有一集合 S = { a1, a2, …,an },其中有 n 个行销活动
如果间隔[si,fi)和[sj,fj)不重叠,则活动ai和aj是兼容的(compatible)
活动选择问题是选择相互兼容的活动的最大子集。
我们假设活动按完成时间单调递增的顺序排序:f1 ≤ f2 ≤ f3 ≤ … ≤ fn-1 ≤ fn
解题步骤如下:
动态规划解
,其中我们将两个子问题的最优解结合起来,形成原始问题的最优解。贪婪的选择
——并且当我们做出贪婪的选择时,其中一个子问题被保证为空,因此只剩下一个非空的子问题。活动选择问题的最优子结构
递归解决方案
Sij = Sik ∪ {ak} ∪ Skj
c[i, j] = c[i, k] + c[k, j] + 1
活动选择问题
设 P(A)
表示 A
为给定的活动集的问题,S
表示 P(A)
的最优解。对于 A
中的任何活动 ai,我们有
那么,我们可以做到更好吗?
动态规划(Dynamic programming): O(n2)
贪婪的解决方案 - 有没有办法反复做出局部决定?
1,选择最早开始的活动,i.e. min{s1, s2, s3, …, sn}?
↓ 可以看出,选择的不是最优解
,2,选择时间最短的活动,i.e. min{ f1 - s1, f2 - s2, f3 - s3, …, fn - sn }?
↓ 可以看出,选择的也不是最优解
3,选择冲突次数最少的活动(与其共同发生的活动称为冲突活动)
选择结果如下 ↓
4,选择最早结束的活动,i.e. argmin{ f1, f2, f3, …, fn }?
蓝色是我们选择出的活动,而红色是与蓝色冲突的活动
然后把红色活动删掉,并且选择出第二早结束的活动(再继续上边的操作:删除冲突活动)
最终,经过选择,会留下以下4个活动
考虑任何非空子问题Sij,并让am成为Sij中完成时间最早的活动:fm = min { fk : ak ∈ Sij}
则有如下性质:
一旦你确定了一个合理的贪婪算法(reasonable greedy heuristic):
贪婪方法概述
贪婪算法的特点
1,
抛出优化问题
2,
证明对原来的问题总是有一个最优解,使贪婪的选择总是安全的
3,
在做出贪婪的选择之后
一个活动选择的例子:
问题:选择相互兼容的活动的最大子集。
我们假设活动按结束时间的单调递增顺序排序:f1 ≤ f2 ≤ f3 ≤ … ≤ fn-1 ≤ fn
为了方便观察可以换个简易图,如下:
很容易看出,相互兼容(不冲突)活动的子空间有:
'递归活动选择器(s, f, k, n)'
RECURSIVE-ACTIVITY-SELECTOR(s, f, k, n)
1 m ← k +1
2 while m ≤ n and s[m] < f [k] # Find the first activity in Sij
3 do m ← m + 1
4 if m ≤ n
5 then return {am} ∪ RAS(s, f, m, n)
6 else return 0
'贪婪-活动-选择器'
GREEDY-ACTIVITY-SELECTOR(s, f)
1 n ← s.length
2 A ← {a1}
3 k ← 1
4 for m ← 2 to n
5 do if s[m] ≥ f[k]
6 then A ← A ∪ {am}
7 k ← m
8 return A
另一方面,我们可以在考虑贪婪选择的情况下,设计出最优的子结构。
定义
对定义进行整理得出
Algorithm 背包演算法
Input
: 背包的最大容量W,以及可以放入背包的n个物品的非负重量wi 与 价格piOutput
: 介于0与1之间的x1,…,xn分別代表第1个,…,第n个物品放入背包中的零碎部份。可以最大化 Σ1≤i≤n pixi,并且满足 Σ1≤i≤n wixi ≤ W(0≤xi≤1,1≤i≤n)1,将pi/wi由大至小排序。
2,根据此排序來將物品依序尽可能地放入背包中,直至背包容量W用完为止。
背包演算法时间复杂度
总时间复杂度: O(n log n)
定义
背包问题 与 0/1背包问题的不同点在于:
以这种方式表述的问题会产生许多重叠的子问题——动态规划的一个特征
字元编码(character coding)可以分为:
Huffman编码以字首码(prefix code)方式达到字元编码最佳资料压缩(optimal data compression)
假设给定一个仅用到a, b, c, d, e五个字元的文件,现在想针对五个字元进行编码,以下是可能的固定长度编码与可变长度的Huffman字首码。
字首码(Prefix code) 让出现频率较高字元的编码较短, 以达到使用最少位元就可以将所有资料儲存的目标。
100000个字符的数据文件只包含字符a–f
HUFFMAN( C )
1 n ← |C|
2 Q ← C
3 for i ← 1 to n – 1
4 do allocate a new node z
5 left[z] ← x ← EXTRACT-MIN(Q)
6 right[z] ← y ← EXTRACT-MIN(Q)
7 f[z] ← f[x] + f[y]
8 INSERT(Q, z)
9 return EXTRACT-MIN(Q)
Huffman编码演算法时间复杂度
总时间复杂度: O(n log n)