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

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

贪婪算法

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

作业选择问题

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

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

选出最多个不冲突作业

i1234567891011
s i s_i si130535688212
f i f_i fi4567891011121314

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

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

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

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

用c[i,j]表示 S i j S_{ij} Sij的最优解集合中作业的数目
if S i j S_{ij} Sij=空集,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 &lt; k &lt; 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&lt;k&lt;j,a_k∈S_{ij}}\{c[i,k]+c[k,j]+1\}, if S_{ij}≠空集\\ \end{aligned} \right. c[i,j]={0, if Sij=maxi<k<j,akSij{c[i,k]+c[k,j]+1}, if Sij̸=

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

定理用途

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

贪婪法

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

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

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

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

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

贪婪算法步骤

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

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

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

Huffman编码

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

abcdef
Frequency(thousands)4513121695

变长编码
频率高的字符被赋予短编码,频率低的字符被赋予长编码
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) dT(c):字符编码长度

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

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

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

构造Huffman编码
算法:
Huffman(C)
 n ← |C|
 Q ← C      O ( n ) 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) O(nlgn)
   f[z]← f[x]+f[y]              |
   Insert(Q,z)               |
 return ExtractMin(Q)

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

背包问题

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

0/1背包问题

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

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

部分背包问题

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

策略:

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

算法:
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 vi/ w i w_i wi(选择最有价值之物)
   x i x_i xi ← min(1,w/ w i w_i wi)(拿走整个,还是部分)
  remove item i from list(从其余物件中装载)
  w ← w - x i w i x_iw_i xiwi(w:容量空余)

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值