算法设计与分析课程复习笔记8——贪婪算法

算法设计与分析课程复习笔记8——贪婪算法

贪婪算法

  • 与动态规划方法相似,是更简单的解决优化问题的方法,通常用于求解优化问题
  • 如有选择,选择眼下看起来最优的那个(局部最优→全局最优)
  • 贪婪算法不能保证一定得到最优解
  • 对于具有某些特征的问题,贪婪算法有最优解

作业选择问题

对n个作业进行排程,这些作业在执行期间需要专用某个共同的资源
S = { a 1 , a 2 , a n } S=\{a_1,a_2,……a_n\} 作业集合
a i a_i [ s i , f i ) [s_i,f_i) 需要专用资源【 s i s_i :起始时间, f i f_i :结束时间, 0 s i < f i < 0≤s_i<f_i<\infty

a i a_i a j a_j 没有冲突: a i a_i a j a_j 发生前结束或 a i a_i a j a_j 结束后发生。

选出最多个不冲突作业

i 1 2 3 4 5 6 7 8 9 10 11
s i s_i 1 3 0 5 3 5 6 8 8 2 12
f i f_i 4 5 6 7 8 9 10 11 12 13 14

作业按完成的先后顺序排列
a 3 , a 9 , a 11 {a_3,a_9,a_{11}} 是一组可协调作业集合
最多可协调作业集合: a 1 , a 4 , a 8 , a 11 {a_1,a_4,a_8,a_{11}} , a 2 , a 4 , a 9 , a 11 {a_2,a_4,a_9,a_{11}}

优化基础
定义子问题空间: S i j = a k S : f i s k < f k s j S_{ij} = { a_k ∈ S : f_i ≤ s_k < f_k ≤ s_j } 【在 a i a_i 结束之后和 a j a_j 开始之前的所有作业】
完整问题: S 0 , n + 1 S_{0,n+1}
a 0 = [ , 0 ) a_0 = [-\infty,0)
a n + 1 = [ , + 1 ) a_{n+1}= [\infty,\infty+1)

问题空间 S i j S_{ij} ,其中的作业按照完成时间递增的顺序排列
f 0 f 1 f 2 f n f n + 1 f_0≤f_1≤f_2≤……≤f_n≤f_{n+1}

解的组成
Solution to S i j S_{ij} = (Solution to S i k S_{ik} ) ∪ { a k a_k } ∪ (Solution to S k j S_{kj} )
|Solution to S i j S_{ij} | = |Solution to S i k S_{ik} | +1+ |Solution to S k j S_{kj} |
S i j S_{ij} 的最优解由子问题 S i k S_{ik} S k j S_{kj} 的最优解组成

用c[i,j]表示 S i j S_{ij} 的最优解集合中作业的数目
if S i j S_{ij} =空集,c[i,j]=0

c[i,j]=c[i,k]+c[k,j]+1

c [ i , j ] = { 0 ,   i f   S i j = m a x i < k < j , a k S i j { c [ i , k ] + c [ k , j ] + 1 } ,   i f   S i j c[i,j]=\left\{ \begin{aligned} 0, if S_{ij}=空集\\ max_{i<k<j,a_k∈S_{ij}}\{c[i,k]+c[k,j]+1\}, if S_{ij}≠空集\\ \end{aligned} \right.

定理
如果 S i j S_ij 是非空的作业集, a m a_m 是其中最先完成的一个,那么, a m a_m 是某个最优解中的一个作
业, S i m S_{im} 为空集, S m j S_{mj} 是唯一的非空子问题。

定理用途

动态规划 使用定理
子问题数目 2个, S i k S_{ik} , S k j S_{kj} 由于 S i m S_{im} 为空,所以只有一个子问题 S m j S_{mj}
选择情况 j-i-1个选择 1个选择: S i j S_{ij} 中最先完成的一个

贪婪法

  1. S i j S_{ij} 中选择不冲突的最多作业
    选择 S i j S_{ij} 中最先完成的作业 a m a_m ,此即贪婪选择
    a m a_m 加入到最优解集合
    继续求解 S m j S_{mj}
  2. 由定理保证选择 a m a_m ,是将其加入到了一个可能的最优解
    在作出选择时不必求解 S m j S_{mj}

递归贪婪算法
REC-ACT-SEL (s, f, i, n)
m ← i + 1
while m ≤ n and s m s_m < f i f_i ►在 S i , n + 1 S_{i,n+1} 确定第一个与 a i a_i 不冲突的事件
  do m ← m + 1
if m ≤ n
 then return { a m a_m } ∪ REC-ACT-SEL(s, f, m, n)
else return 空集

作业按完成时间递增顺序排列
运行开销: Θ ( n ) Θ(n)
初始调用:REC-ACT-SEL (s, f, 0, n)

增量算法(迭代贪婪算法)
GreedyActivitySelector(s,f,n)
 A ← { a 1 a_1 }
 i ← 1
 for m ← 2 to n
  do if s m s_m f i f_i    ► a m a_m a j a_j 不冲突
    then A ← A ∪ { a m a_m }
      i ← m  ► a i a_i 是最近添加到A的
 return A

作业按完成时间递增顺序排列
运行开销: Θ ( n ) Θ(n)

贪婪算法步骤

  1. 确定问题的优化结构
  2. 得到递归解
  3. 证明某个最优选择是贪婪选择
  4. 贪婪选择将产生唯一 一个非空子问题
  5. 用递归算法实现贪婪策略
  6. 将递归算法转换为迭代算法

动态规划与贪婪算法的比较

  • 动态规划
    每步选择
    选择与子问题解相关
    自底向上
  • 贪婪算法
    首先做出贪婪选择
    求解贪婪选择后产生的唯一子问题
    后续贪婪选择与前面的选择有关,但与子问题的解无关
    自顶向下,问题规模逐步缩小

Huffman编码

利用字符出现的频率,建立表示每个字符的最优方法,用于文件压缩

a b c d e f
Frequency(thousands) 45 13 12 16 9 5

变长编码
频率高的字符被赋予短编码,频率低的字符被赋予长编码
a = 0, b = 101, c = 100, d = 111, e = 1101, f = 1100

前缀码Prefix Codes
一个字符的编码必须不能是另一个字符编码的前缀
更准确的名字应该是“prefix-free codes”
利用前缀码可以进行文件压缩

通过合理的编码尽可能地压缩文件,标准的ASCII码用7位来表示每个字符
ASCII码
等长编码表示:
等长编码
前缀码表示:
前缀码
为什么要使用前缀码?
因为非前缀码会导致二义性。

前缀码表示
二分树,叶节点表示字符
0表示左,1表示右,从根到叶即为字符编码
从根到叶路径长度即是编码长度

最优编码
最优编码通过完整二分树表示,每个非叶节点都有两个子节点,定长编码不是最优的,变长是的。最优编码
编码一个文件需要多少位
C:字符集,f( c ):字符c的频率, d T ( c ) d_T(c) :字符编码长度

B ( T ) = c C f ( c ) d T ( c ) B(T)=\displaystyle \sum_{c∈C}f(c)d_T(c)

构造Huffman编码
贪婪算法
假设:
字符集C有n个字符
f(c)代表c的字符频率
自底向上构建完整二分树

想法:
从叶节点开始
每次合并频率最小的两个叶节点,生成的新节点的频率为两者之和
在新节点和其他节点中再找出两个频率最小的节点,合并生成新节点

构造Huffman编码
算法:
Huffman(C)
 n ← |C|
 Q ← C      O ( n ) O(n)
 for i ← 1 to n-1               |
  do allocate a new code z          |
   left[z] ← x ← ExtractMin(Q)        |
   right[z] ← y ← ExtractMin(Q)        O ( n l g n ) O(nlgn)
   f[z]← f[x]+f[y]              |
   Insert(Q,z)               |
 return ExtractMin(Q)

运行开销: O ( n l g n ) O(nlgn)

背包问题

0/1背包问题:要么拿走,要么放弃
部分背包问题:可部分拿走

0/1背包问题

背包的可容重量为W
n个物件,第i个的价值为 v i v_i ,重量为 w i w_i
目标:不超重,价值最大

如果你作出贪婪选择,即装入平均价值最高的物件,你最终不可能获得最大化的价值总量
0/1背包问题需要通过动态规划来解决!
0/1背包问题

部分背包问题

背包的可容重量为W
n个物件,第i个的价值为 v i v_i ,重量为 w i w_i
目标:不超重,价值最大

策略:

  • 挑选出单位重量价值最高的物件
  • 在容量许可下,尽量转载单位重量价值最高的物件,直至拿光
  • 然后依次装载其余物件中价值最高的物件,物件价值递减排列

算法:
FractionalKnapsack(W,v[n],w[n])
 while w>0 and as long as there are items remaining(背包有容量)
  pick item with maximum v i v_i / w i w_i (选择最有价值之物)
   x i x_i ← min(1,w/ w i w_i )(拿走整个,还是部分)
  remove item i from list(从其余物件中装载)
  w ← w - x i w i x_iw_i (w:容量空余)

部分背包问题
参考:任课教师邱德红教授的课件

发布了25 篇原创文章 · 获赞 19 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42605042/article/details/89787693