您的位置:首页 > 其它

[算法]约瑟夫环问题

2014-09-02 16:31 176 查看
昨天有个同学跟我说约瑟夫环问题的O(n)解法很费解,看不懂,所以我就看了一下,发现这种解法真是太奇妙了,所以打算写一下。

约瑟夫环问题(Josephus)问题描述:

输入m和n,有n个人编号从0到n-1,这n个人开始从0到m-1顺序循环数数,每次数到m-1的人就出列,剩下的人继续,直至剩余最后一人,求最后剩余的那个人的编号。

我觉得总体上来讲O(n)算法应该是分治的思想,首先是Divide,将n个人的游戏变成n-1个人,然后再变成n-2个人,......,直到最后剩余1个人。然后是Conquer,如果我们知道n-1个人的游戏结果的话,怎么得到n个人游戏的结果呢?我们来看看从n个人变成n-1个人后的情况,当m>n时,需要把下面的m变成m%n,为了简便我就直接写m了。

•0,1,2,3,4,…,m-2,m-1,m,…,n-1// 原始n个人的编号,第一轮报数后,m-1将被删除
•m,…,n-1,0,1,2,3,4,…,m-2// 把前m-1人挪到编号n-1后,即剩余n-1个人的原始编号{x}
•0,…,n-m-1,n-m,…,n-2// 剩余n-1个人重新编号{y}

假设我们已知n-1个人游戏时最后剩余的编号为y,则y对应的在n个人中的编号是什么呢?即x=? 我们可以看看{y}中的n-1个数字与{x}中的n-1个数字有什么关系。

y -> x

[0, n-m-1] -> [m, n-1] = [0 + m, n-m-1 + m]

[n-m, n-2] -> [0, m-2] = [n-m - (n-m), n-2
- (n-m)]

对于y属于[0,n-m-1]部分,原始编号x相当于y加上m,因为在原始的n个人中这些人之前已经有m个人已经报数了,后来删除m-1之后原始编号为m的需要从0开始编号;

对于y属于[n-m, n-2] 部分,原始编号x相当于y减去n-m,即相当于加上m再减去n,跟上面一样,重新编号的时候他们本来是前面的m-1个人,但是挪到了队尾,所以编号变大了,再加上m之后肯定会超过n,所以需要减去n;

但是有了取模就能把是否需要减去n这个问题解决,即x = (y + m) % n

有了这个公式写起代码来简直要逆天,几行就可以解决了。因为在Conquer部分的时间复杂度为O(1),即T(n) = T(n-1) + O(1),总的时间复杂度是O(n),只要从1到n递推一遍就可以找到答案了。代码如下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: