您的位置:首页 > 其它

约瑟夫环算法研究

2014-11-27 17:48 260 查看
今天在CSDN论坛上看到这个问题:

500个小孩围成一圈,从第一个开始报数:1,2,3,1,2,3,1,2,3,……每次报3的小孩退出

问最后剩下的那个小孩,在以前500人里是第几个???

用LinkedList模拟报数和删除行为,算出最终结果是436。但是当总人数特别大时,此算法效率就非常低。

import java.util.*;
class Test{
public static void main(String[] args){
List<Integer> list = new LinkedList<Integer>();
for(int i = 1; i <= 500; i ++){
list.add(i);
}
int index = 2;//第一个报到3的小孩,索引是2
while(list.size()> 1){
list.remove(index);
index = index - 1;//移除该位元素后,下次需要从该位开始遍历,故减1
index = (index +3)%list.size();
}
System.out.println(list.get(0));

}
}

回帖里有人提及这是约瑟夫问题,于是百度了一下,找到一般性的问题描述:

已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列,求最后一个出列人的编号。

在写算法思路之前,先记录自己做题时走过的弯路:
先假设m,n均为正整数,m%n和(m-1)%n+1,这两个式子在大部分情况下是等效的,但是当m为n的倍数时,前一个式子的值是0,后一个式子的值是n。

下面讲此题思路:

题中第k人开始报1,则第(k+m-2)%n+1人报到m出列,接下来第(k+m-1)%n+1继续开始报1。一共n-1个数次序为:

(k+m-1)%n+1, (k+m)%n+1, (k+m+1)%n+1……(k+m-3)%n+1  ①

调整次序,使第一个数的次序仍为k:

k, k+1, k+2,……k-2 ②

可见问题转化成为n-1人时的约瑟夫环。假设此时最后一个出列的人的编号为f(n-1),那么将它作一个逆向变换就可以得到n人约瑟夫环的解:

f(n) = [f(n-1)+m-1]%n+1

由于n是题设中的最大人数,将它换成其他变量以免混淆:

f(i) = [f(i-1)+m-1]%i+1

由于始终是第k人开始报1,那么当总人数为1时,f(1)=k。据此可写出迭代或递推算法。

以下是迭代算法:
public int fun(int n, int m, int k){
int result = k;//当总人数为1时,
for(int i = 2; i <= n; i++){
result = (result + m - 1) % i + 1;
}
return result;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: