黎明号护卫舰吧 关注:102贴子:1,593
  • 6回复贴,共1

鸡蛋问题解法

只看楼主收藏回复

一筐鸡蛋: 1个1个拿,正好拿完。 2个2个拿,还剩1个。 3个3个拿,正好拿完。 4个4个拿,还剩1个。 5个5个拿,还剩4个。 6个6个拿,还剩3个。 7个7个拿,正好拿完。 8个8个拿,还剩1个。 9个9个拿,正好拿完。 问筐里有多少鸡蛋?
以下代码作者为@灌水硫酸铜





IP属地:广西1楼2016-04-05 16:02回复
    以上解法采用了遍历的思路,最坏情况下可能需要LCM(5, 6, 7, 8, 9)次运算才能找到结果(LCM()为最小公倍数)。由于思路逻辑简单,且问题本身并不复杂,可作为初学者对该类问题的一种通用解法。


    2楼2016-04-06 15:17
    回复
      此后,【金属战机】提出了一个更高效的解法,代码如下:

      该程序输出的结果是问题的通解(2520n+1449)。算法思路为先计算满足除c1余d1的通解an+b,再通过不断b+=a得到除c2余d2的通解an+b,如此循环直到计算完所有题设条件,此时的an+b即是问题的通解。
      容易发现,算法在每次更新b的值时步进为a,而a在算法的不断执行过程中是不停增长的,因此算法找到答案所需的步数会显著少于遍历法。事实上,应用该算法只需计算14步即可得到答案,比之前硫酸铜的遍历法(1449步)效率提高了百倍。


      3楼2016-04-06 15:29
      回复
        金属战机提供的算法将解决该问题所需的步数减小到了最多1+2+3+4+5+6+7+8+9次,其执行效率把硫酸铜的遍历算法甩开至少10条街。
        那么,该算法还有优化的余地吗?答案是肯定的。
        考虑到最终得到的b是由b不断加a得到的,因此,提高算法执行过程中的a,就能让b在更少次数内达到正确解。一个快速增大a的思路是,我们先让a,b满足除9余0,再去满足除8余1,以此循环直到满足除5余4。这么做,a的值首先为9,之后分别变为72,504,504,2520。而前述金属战机的算法中a为5,30,210,840,2520。两者的运行结果分别如下:

        至此,我们通过调整计算顺序的方式将金属战机的算法优化到了只需9步计算即可找到结果。
        现在,考虑calc函数中对a的更新计算:
        int t=a;
        while(a%c!=0)
        a+=t;
        显然,这三行代码求的是a与c的最小公倍数。金属战机在这里采用了遍历的方法计算LCM(a,c)——判断a,2a,3a,4a,……是否为c的整数倍。该思路显然还有优化的余地。
        对于两个整数x,y,它们的最小公倍数为x * y / GCD(x,y),其中GCD()是最大公约数。最大公约数可通过辗转相除法来计算。于是,算法被进一步优化为:

        于是,我们获得了一个效率较高的获得该类问题通解的方法。


        4楼2016-04-06 18:59
        回复
          当时我就推荐程序组吸纳金属战机,看来我还是颇有眼力的!


          5楼2016-05-28 09:05
          收起回复