韩信点兵问题

来源:互联网 发布:php 发送模板消息代码 编辑:程序博客网 时间:2024/05/16 06:30
韩信点兵是一个有趣的猜数游戏。如果你随便拿一把蚕豆(数目约在100粒左右),先3粒3粒地数,直到不满3粒时,把余数记下来;第二次再5粒5粒地数,最后把余数记下来;第三次是7粒一数,把余数记下来。然后根据每次的余数,就可以知道你原来拿了多少粒蚕豆了。不信的话,你还可以试验一下。例如,假如3粒一数余1粒,5粒一数余2粒,7粒一数余2粒,那么,原有蚕豆有多少粒呢? 


这类题目看起来是很难计算的,可是我国古时候却流传着一种算法,名称也很多,宋朝周密叫它“鬼谷算”,又名“隔墙算”;杨辉叫它“剪管术”;而比较通行的名称是“韩信点兵”。最初记述这类算法的是一本名叫《孙子算经》的书,后来在宋朝经过数学家秦九韶的推广,又发现了一种算法,叫做“大衍求一术”。这在数学史上是极有名的问题,外国人一般把它称为“中国剩余定理”。至于它的算法,在《孙子算经》上就已经有了说明,而且后来还流传着这么一道歌诀: 


三人同行七十稀, 


五树梅花廿一枝, 


七子团圆正半月, 


除百零五便得知。 


这就是韩信点兵的计算方法,它的意思是:凡是用3个一数剩下的余数,将它用70去乘(因为70是5与7的倍数,而又是以3去除余1的数);5个一数剩下的余数,将它用21去乘(因为21是3与7的倍数,又是以5去除余1的数);7个一数剩下的余数,将它用15去乘(因为15是3与5的倍数,又是以7去除余1的数),将这些数加起来,若超过105,就减掉105,如果剩下来的数目还是比105大,就再减去105,直到得数比105小为止。这样,所得的数就是原来的数了。根据这个道理,你可以很容易地把前面的五个题目列成算式:


1×70+2×21+2×15-105 


=142-105 


=37 


因此,你可以知道,原来这一堆蚕豆有37粒。
证明:

设三次数的数目分别为a1,a2,a3(3,5,7),三次的余数分别为b1,b2,b3(1,2,2);定义m为一魔法数,m的求法如下:

m是a,b,c的魔法数,则m mod a = 1,m mod b = 0, m mod c = 0;即m是b和c的公倍数,而且除以a的余数是1。由此我们可以求出m1,m2,m3。设sm是各个余数与mi的乘积的和。由此可得:

         sm  mod a1 = (m1*b1+m2*b2 + m3*b3 ) mod a1


     又因为m2,m3是a1的公倍数,所以sm mod a1 = (m1*b1) mod a1 = b1,即sm满足条件1.
同理得证,sm满足条件2和条件3。因此sm是满足条件的一个数。        
又因为如果有一个数x是a1,a2,a3的最小公倍数,则sm+k*x也是满足条件的一个数。由此可知,在(0~x)的范围内,有一个数是满足条件的。得证。


算法如下:


#include "stdafx.h"int gcd(int a,int b){   while ( b != 0 )   {      int temp = b;      b = a % b;      a = temp;   }   return a;}int mgcd(int a,int b){   return (a*b)/gcd(a,b);}struct node_t{   int a;   int b;};int getminm(int a,int b,int c){   int m ;   int r;   r = m = mgcd(b,c);   while ( r%a != 1) r += m;   return r;}int getNum(node_t *pnode){   int m1,m2,m3,x;   int sm;     m1 = getminm(pnode[0].a,pnode[1].a,pnode[2].a);   m2 = getminm(pnode[1].a,pnode[0].a,pnode[2].a);   m3 = getminm(pnode[2].a,pnode[0].a,pnode[1].a);   x = mgcd(pnode[0].a,pnode[1].a);   x = mgcd(pnode[2].a,x);   sm = pnode[0].b * m1 + pnode[1].b *m2 + pnode[2].b * m3;   return sm%x;}int main(){   node_t node[3];   node[0].a = 3;   node[0].b = 1;   node[1].a = 5;   node[1].b = 2;   node[2].a = 11;   node[2].b = 3;   printf("蚕豆数:%d\n",getNum(node));   return 0;}


0 0
原创粉丝点击