一个欧几里得扩展的应用

来源:互联网 发布:java正则匹配时间格式 编辑:程序博客网 时间:2024/06/18 08:19

前阵子从一个师兄的blog中看到个比较有意思的题目,师兄的blog中给出了多种解法,平时自个儿也不怎么研究数论,这次看到这题目感觉比较吃力,写下来以供后期参考。

题目:

5个水手在岛上发现一堆椰子, 夜晚睡觉后,第一名水手把椰子分为等量的5堆,还剩下一个给了猴子,自己藏起一堆。第二个水手把剩下的4堆混合后重新分为 等量的5堆,剩下一个给了猴子;自己藏起一堆。第三第四第五位水手依此办理。天亮以后,大家把剩下的椰子分为等量的5堆,剩下一个给了猴子。问原来这堆椰子最少为多少个。

刚接触这个问题,没什么头绪,难不成傻儿巴叽的一个个的去试,但是这肯定不是解题的方法,再继续往下看,不难看出这是一个二元一次线性方程,从最后一个海盗开始的递归:

(n-1)x=ny+1

欧几里得扩展给出了解决线性方程 的解法。

首先欧几里得算法定义ax+by=gcd(a,b)有解,证明如下:

1.显然当b=0时,gcd(a,b)=a,则x=1,y=0;

2.当a,b都不为0时,设

ax1+by1=gcd(a,b);bx2+(a%b)y2=gcd(b,a%b);/*由朴素欧几里得定理得到gcd(a,b)=gcd(b,a%b);则*/ax1+by1=bx2+(a%b)y2;ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;/*根据恒等定理得到*/x1=y2; y1=x2-(a/b)*y2;

通过递归求解就可以得到x,y的值

二元线性方程ax+by=c有整数解,则c%gcd(a,b)=0,因为d=gcd(a,b)=>d|(ax+by);

#扩展欧几里得算法def extend_eulid(a,b):if b==0:return [1,0,a]else:x,y,d=extend_eulid(b,a%b)return [y,x-(a//b)*y,d]

#线性方程的求解def liner_equation(a,b,c):rst=extend_eulid(a,b)d=rst[2]x=rst[0]y=rst[1]if c%d:return NULL        #线性方程的一个解k=c/dreturn [x*k,y*k]

但是此题需要求解最小的正数解,由a(x0+q*(b/d))+b(y0-q*(a/d))=c;(x0,y0是方程的一个解,d=gcd(a,b))可知,通过改变q可以得到最小正数解:

#最小正数解def liner_equation(a,b,c):rst=extend_eulid(a,b)d=rst[2]x=rst[0]y=rst[1]if c%d:return NULLk=c/dx1=x0=x*ky1=y0=y*kn=0while x1<=0:n+=1x1=x0+(b/d)*ny1=y0-(a/d)*nreturn [x1,y1]

由此,这个问题也就解决了:

#海盗个数NUM_PIRATE =5#猴子个数NUM_MONKEY=1a=NUM_PIRATE-1b=-NUM_PIRATEc=NUM_MONKEYfor i in range(NUM_PIRATE):rest=liner_equation(a,b,c)vx=rest[0]b=b*NUM_PIRATEc=NUM_MONKEY+NUM_PIRATE*vxprint vx*NUM_PIRATE+NUM_MONKEY



原创粉丝点击