代码改变世界

求解约瑟夫问题(Joseph Problem)的数学方法 from 御剑晨风

2011-10-17 22:39  诸葛二牛  阅读(686)  评论(0编辑  收藏  举报

http://alexxyjiang.blog.163.com/blog/static/11713572120109210942883/

约瑟夫问题(Joseph Problem):

n个人(编号由0, 1, ..., n-1)围成一圈,由编号0的人从1开始报数,报到m的退出,剩下的人继续从1开始报数,直到圈内只剩余1人,求胜利者的编号。(n>0, m>0)

--------------------------------

求解约瑟夫问题,很容易想到的常规方法是模拟,不仅程序写起来比较烦,而且时间复杂度高达O(mn),当m,n非常大的时候,这样的时间复杂度就显得太高了。

有没有一种方法,能够降低计算的复杂度呢?答案是肯定的。

--------------------------------

我们可以通过数学推理得到这样的方法:

定义每一轮新的报数开始前后,以当前报数为1的人向正方向结合剩余的人构成的环为约瑟夫环。不失一般性,不妨设这是从(i+1)人到i人的过程(i>=0)

报数前后,分别对应着两个约瑟夫环:

报数前:0, 1, 2, 3, ..., i

报数后:0, 1, 2, 3, ..., (i-1)

这两个报数前后的环,显然是有关系的,是什么样的关系呢?

推理步骤如下:

1)在(i+1)报数到m的过程中,出列者的号码是(m-1)%(i+1),下一个报数人编号自然就是m%(i+1),不妨设为k;

2)建立报数前后的对应关系:

k    ---> 0

k+1  ---> 1

k+2  ---> 2

...

k-2  ---> i-1

(要注意:k-1号在前一轮出局)

3)设在i人中,最终剩余者的编号是x,那对于(i+1)人作逆推,容易得到(i+1)人中最终剩余者的编号x'满足如下关系:

x'=(x+k)%(i+1);

4)将(3)得到的公式作完整逆推,则可以得出最终的解;

用上述数学方法构建算法时间复杂度为O(n),不需要额外空间,空间复杂度为O(1)

--------------------------------

另外要说明的是,这个方法只能在o(n)的方法内求出第任意某个出去的人的位置。如果要求出整个出去序列,用这个方法还是o(n^2)的……求所以出去序列,用线段数可以做到o(nlog(n))..貌似没有比这个快的了……