Bootstrap

算法系列—贪心算法

关键字:局部最优

引言
    
贪婪是一种人类本能的东西,贪心算法也是最接近人类日常思维的一种解题策略。比如,找零钱问题:
     假设提供了数目不限的面值为25美分、10美分、5美分、1美分的硬币。一个小孩买了价值少于1美元的糖,并将1美元交给售货员,售货员希望用最少的硬币数找钱给小孩。假设小孩买了33美分的糖果。(需要找钱67美分)
      找钱的方法:25+25+10+5+1+1.
      我们有种直觉倾向:在找零时,直觉告诉我们使用面值越大的硬币找零,还差找零的剩余金额最少,用的硬币也越少。

一、基本概念:
     所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解
     贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
     所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

二、算法基本思路:
    1.建立数学模型来描述问题。
    2.把求解的问题分成若干个子问题。
    3.对每一子问题求解,得到子问题的局部最优解。
    4.把子问题的解局部最优解合成原来解问题的一个解。

三、算法特点:
    1. 不能保证求得的最后解是最佳的;
    2. 不能用来求最大或最小解问题;
    3. 只能求满足某些约束条件的可行解的范围。
    4. 速度快,通常是线性二次式,不需要多少额外的内存。
    5. 程序运行过程中无回溯过程,后面的每一步都是当前看似最佳的选择,这种选择依赖于已做出的选择,不依赖于未作出的选择。

四、适用问题
      适合贪心法求解的问题一般具有两个重要性质:贪心选择性质最优子结构性质
     1.贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
        与动态规划算法相同的是,都具有最优子结构性质相同点;不同的是,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题不同点
     2.当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
 
五、贪心算法的实现框架
    从问题的某一初始解出发;
    while (能朝给定总目标前进一步)
    { 
          利用可行的决策,求出可行解的一个解元素;
    }
    由所有解元素组合成问题的一个可行解;
  
六、实例分析
    贪心算法可以求解很多问题,比如哈夫曼树问题、单源最短路径问题、最小生成树问题等,现就著名的背包问题,采用贪心法分析之。
    问题描述给定n个物品和一个容量为C的背包,物品i的重量是Wi,其价值为Vi,背包问题是如何选择入背包的物品,使得装入背包的物品的总价值最大,注意和0/1背包的区别,在背包问题中可以将物品的一部分装入背包,但不能重复装入。
    算法分析思路:用贪心法求解背包问题的关键是如何选定贪心策略,使得按照一定的顺序选择每个物品,并尽可能的装入背包,知道背包装满。至少有三种看似合适的贪心策略。
  1. 选择价值最大的物品,因为这可以尽可能快的增加背包的总价值,但是,虽然每一步选择获得了背包价值的极大增长,但背包容量却可能消耗的太快,使得装入背包的物品个数减少,从而不能保证目标函数达到最大。
  2. 选择重量最轻的物品,因为这可以装入尽可能多的物品,从而增加背包的总价值。但是,虽然每一步选择使背包的容量消耗的慢了,但背包的价值却没能保证迅速的增长,从而不能保证目标函数达到最大。
  3. 以上两种贪心策略或者只考虑背包价值的增长,或者只考虑背包容量的消耗,而为了求得背包问题的最优解,需要在背包价值增长和背包容量消耗二者之间寻找平衡。正确的贪心策略是选择单位重量价值最大的物品。

    例如:有三个物品,其重量分别为{20,30,10},价值分别为{60,120,50},背包的容量为50,应用三种贪心策略装入背包的物品和获得的价值如下图所示:(部分转载

        

    设背包容量为C,共有n个物品,物品重量存放在数组W[n]中,价值存放在数组V[n]中,问题的解存放在数组X[n]中,贪心法求解背包问题的算法如下:

算法:贪心法求解背包问题

输入:背包的容量C,物品重量W[n],物品价值V[n]

输出:数组X[n]

  1. 改变数组WV的排列顺序,使其按单位重量价值V[i]/W[i]降序排列;
  2. 将数组X[n]初始化为0
  3. i=0
  4. 循环直到(W[i]>C

    4.1   将第i个物品放入背包:X[i]=1;

    4.2    C=C-W[i];

    4.3    i++;

  1. X[i]=C/W[i]


    算法分析算法的时间主要消耗在将各种物品按照单位重量的价值从大到小的排序,因此,其时间复杂性为O(nlog2n)

    背包问题与0/1背包问题类似,所不同的是在选择物品i装入背包时,可以选择该物品的一部分【分割物品】,不一定要全部装入背包。而0/1背包问题必须保证物品的完整性,即要么整体放入,要么不放,非0即1。背包问题可以用贪心法求解,而0/1背包问题却不能用贪心法求解,下图给出了一个贪心法求解0/1背包问题的示例。从下图可以看出,对于0/1背包问题,贪心法之所以不能得到最优解,是由于物品不允许分割,因此,无法保证最终能将背包装满,部分闲置的背包容量使背包的单位重量价值降低了。事实上,在考虑0/1背包问题时,应比较选择该物品和不选择该物品所导致的方案,然后再做出最优选择,由此导出许多相互重叠的子问题,所以,0/1背包问题合适用动态规划法求解。

        

   算法实现实现函数KnapSacks实现贪心法求解背包问题,简单起见,假设物品已按单位重量降序排列,算法C++语言描述如下:

int KnapSack (int W[],int V[],int N,int C)  
    {  
        double X[10]={0};  
        int maxValue=0;  
        for(int i=0;W[i]<C;i++)  
        {  
            X[i]=1;  
            maxValue+=V[i];  
            C=C-W[i];  
              
        X[i]=(double)C/W[i];  
        maxValue+=X[i]*V[i];  
        return maxValue;  
        }  
    }

七、总结
     贪心法并不是从整体最优考虑,它所做出的选择只是某种意义的局部最优,贪心算法对于大部分的优化问题都能产生最优解,但不能总获得整体最优解,通常可以获得近似最优解。


转载请注明出处:http://blog.csdn.net/daijin888888/article/details/53079358


悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;