您的位置:首页 > 其它

Joseph(约瑟夫环)问题分析

2015-10-28 10:07 113 查看
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus
和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

上面是对问题的由来描述,下面我们将问题进行分析,分析方法一采用递归的方式进行算法设计。

递归算法设计:

初始的编号 1 2 3 4 5 ......k-2 k-1 k k+1 k+2 ...... n

一轮后编号分析:首先是第k个元素要出圈,然后是第k+1个元素再次从1开始记数,具体编号分析如下:

原始编号 进行第二轮时的编号

k+1 1

k+2 2

k+3 3

k+4 4

............

n n-k

1 n-k+1

2 n-k+2

...... ......

k-1
n-1

讲到这里,读者不妨想想如果我们现在只是考虑第二轮的编号中的数据进行分析,这不就是原约瑟夫环的一个子问题而已,关键点在于如何将两个问题进行关联起来

如果我们能够找到从第二轮编号到初始编号的关联关系,问题便会迎刃而解。通过观察分析,如果将k表示第二轮编号,t表示初始编号,那么大家快就会发现其关系式为

t = (k+m)%n,之后我们便只需解决一些递归的跳出条件细节问题。

下面附上java版源码:

public static int joseph(int n, int m)
{
if (n == 2) return (m%2) ? 2 : 1;
int v = (m+josephus0(n-1,m)) % n;
if (v == 0) v = n;
return v;
}

非递归算法设计:

递归算法分析简单,容易编程,但是却会花费较大的代价,运行效率并不高,因此必须考虑下约瑟夫环的非递归算法,非递归算法的设计思想是引入一维数组表示该问题本体,设置另外数组表示该位置的是否出列状态,出列后下阶段的计算将不会将其考虑在内,最后输出唯一的状态为未出列的位置,具体算法实现如下:
import java.util.Scanner;
public class JosephProblem {
public static void main(String[] args)
{
//约瑟夫环非递归算法
Scanner sc = new Scanner(System.in);
System.out.print("请输入要报数的总人数:");
int n = sc.nextInt();
int leftnumber = n;
System.out.print("请输入隔几报数:");
int k = sc.nextInt();
int countnumber = 0;
int index = 0;
int[] array = new int
;
for(int i = 0;i <= n-1;i++)
array[i] = 1;
while(leftnumber > 1)
{
if(array[index] == 1)
{
countnumber++;
if(countnumber == k)
{
countnumber = 0;
array[index] = 0;
leftnumber--;
}
}
index++;
if(index == n)
index = 0;
}
for(int j = 0;j <= n-1;j++)
{
if(array[j] == 1)
System.out.print(j+1+" ");
}
}
}



本人算法水平有限,如果本文有误请不吝指出,我们共同进步

约瑟夫环问题是一个较为普遍的算法问题,许多在线OJ中都会出现这种或那种的变体,例如九度OJ中剑指off专栏就有这样一道编程题,连接如下

题目1356:孩子们的游戏(圆圈中最后剩下的数)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: