算法学习(三)贪婪算法

下面文章转自http://182190145.blog.163.com/blog/static/664424420089118405610/

1.贪婪算法引述  

 

先举个实例来通俗地说明贪婪算法。一个小孩买了价值33美分的糖,并将1美元的钱交给售货员。售货员希望用数目最少的硬币找给小孩。假设提供了数目不限的面值为2 5美分、1 0美分、5美分、及1美分的硬币。售货员分步骤组成要找的零钱数,每次加入一个硬币。

选择硬币时所采用的贪婪准则如下:每一次选择应使零钱数尽量增大。为保证解法的可行性(即:所给的零钱等于要找的零钱数),所选择的硬币不应使零钱总数超过最终所需的数目。

现在需要找给小孩6 7美分,首先入选的是两枚2 5美分的硬币,第三枚入选的不能是2 5美分的硬币,否则硬币的选择将不可行(零钱总数超过6 7美分),第三枚应选择1 0美分的硬币,然后是5美分的,最后加入两个1美分的硬币。

   

贪婪算法有种直觉的倾向,在找零钱时,直觉告诉我们应使找出的硬币数目最少(至少是接近最少的数目)。可以证明采用上述贪婪算法找零钱时所用的硬币数目的确最少。

可见贪婪算法的实现思路:在每一步中,它要求“贪婪”地选择最佳操作,并希望通过一系列局部的最优选择,能够产生一个整个问题的(全局)最优解。

 

2.贪婪算法原理

 

在传统贪婪算法中采用逐步构造最优解的方法。在每个阶段,都做出一个看上去最优的决策(在一定的标准下)。决策一旦做出,就不可再更改。做出贪婪决策的依据称为贪婪准则greedy criterion

贪心思想的本质是每次都形成局部最优解,换一种方法说,就是每次都处理出一个最好的方案。即,贪婪法建议通过一系列步骤来构造问题的解,每一步对目前构造的部分解做一个扩展,直到获得问题的完全解为止。这个技术的核心是,所做的每一步选择都必须满足:

l      可行的:即它必须满足问题的约束。

l      局部最优:它是当前步骤中所有可行选择中最佳的局部选择。

l      不可取消:即选择一旦做出,在算法的后面步骤中就无法改变了。

贪心算法的最大特点就是快。通常,二次方级的存储要浪费额外的空间,而且很不幸,那些空间经常得不出正解。但是,当使用贪心算法时,这些空间可以帮助算法更容易实现且更快执行。

 

3.贪婪算法实现的难点

 

a. 如何贪婪:

怎样才能从众多可行解中找到最优解呢?其实,大部分都是有规律的。在样例中,贪婪就有很明显的规律。在每一步中,它要求“贪婪”地选择最佳操作,并希望通过一系列局部的最优选择,能够产生一个整个问题的(全局)最优解。正因为贪心有如此性质,它才能比其他算法要快。然而,还有一些问题并不是这种情况,对于这样的问题,如果我们关心的是,或者说我们能够满足于一个近似解,贪婪算法仍然是有价值的。

 

b. 贪婪的正确性:

要证明所设计贪婪方法的正确性,才是真正挑战,因为并不是每次局部最优解都会与整体最优解之间有联系,往往靠贪婪方法生成的解不是最优解。这样,贪婪方法的证明就成了贪婪正确的关键。一个你想出的贪婪方法也许是错的,即使它在大部分数据中都是可行的,你必须考虑到所有可能出现的特殊情况,并证明你的贪婪方法在这些特殊情况中仍然正确。这样经过千锤百炼的性质才能构成一个正确的贪婪方法

 

    4.贪婪算法应用范围

 

贪 婪算法总是做出在当前看来最好的选择。也就是说贪婪算法并不从整体最优考虑,它所做出的选择只是在某种意义上的局部最优选择。当然,希望贪婪算法得到的最 终结果也是整体最优的。虽然贪婪算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。在一些情况下,即使贪婪算法不能得到整体最优解, 其最终结果却是最优解的很好近似。

如 果需要解决的问题有贪婪性质存在,那么采用贪婪算法无疑是最好的选择!因为基于贪婪算法的程序执行起来速度极快,并且节约空间。衡量算法的关键就是效率, 即程序执行时消耗的时间和空间。当然,贪心算法有一定使用范围,而且有一部分极难证明,若是没有把握,最好还是不要冒险,因为还有其他算法会比它要保险

 

自我编程学习

题目:人民币有100,50,20,10,5,2,1,0.5,0.2,0.1,在找零时有多种方案,我们选取先取最大的面额的找取

如找零68.9 就为 50+10+5+2+1+0.5+0.2+0.2,利用C#编出这道算法。

 

class Program
    {
        static void Main(string[] args)
        {
            Console.Write("请输入需要找零的钱:");
            double fMoney = Convert.ToDouble(Console.ReadLine());
            double IMoney = fMoney * 10;
            Console.Write("找零:");
            while (IMoney != 0)
            {
                Console.Write(GetMax(ref IMoney) / 10 + "");
            }
            Console.ReadKey();


        }

        private static double GetMax(ref double IMoney)
        {
            double[] MoneyCount = { 500, 200, 100, 50, 20, 10, 5, 2, 1 };
            double rtn = 0;
            for (int i = 0; i < MoneyCount.Length; i++)
            {
                if (MoneyCount[i] <= IMoney)
                {
                    IMoney -= MoneyCount[i];
                    rtn= MoneyCount[i];
                    break;
                }
            }
            return rtn;
        }

    }

 

 

 

posted @ 2014-05-12 23:39  清风君  阅读(5175)  评论(0编辑  收藏  举报