题目
1.约瑟夫环
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
2.猴子选大王
一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫做大王。要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。
3.狼找兔子问题
一座山周围有n个洞,顺时针编号为0,1,2,3,4,…,n-1.一只狼从0号洞开始,顺时针计数,每当经过第m个洞时,就进洞找兔子.例如n=5,m=3,狼经过的洞依次为0,3,1,4,2,0.输入m,n.试问兔子有没有幸免的机会?如果有该藏在哪儿?
解题思路
以上的三种问题都是约瑟夫环问题只是叫法不同,这类问题都是可以解得:首先看一个图解(以总共10人,每数3个人就杀一个。为例)
首先最里面的圈是人位置的编号共10个,中间那个圈是一个开关圈,最外层的圈是数数的圈,原理就是从第一个人开始循环,外层数数3个数就讲中间的开关的0置为1,然后在这个置为1相当于打开开关如图,开关打开后再在这个人后面再数3个人,再将0置为1,进行比总人数少1的次数后,所有的人的开关都被打开了,(第二层都是1)就只剩最后一人(第二次开关是0)了,然后输出这个人最里面圈的位置编号就行了。
代码实现
function joseph(n,m){//n是总人数,m是数几个数
if(n>1||m>=1){//总人数要大于1,数的数要大于等于1才能进入下面的循环
var cricle = new Array(n);//创建一个n个的数组存放人的被杀情况(是0还是1)
var num = 0;//计数器用于每次数3个人
var count = 0;//记录杀人的次数
for(var i=0;i<cricle.length;i++){
cricle[i]=0;
}//这个for循环用于将数组中所有数置0
while(count<n-1){//如果杀人次数少于总人数少一人接着杀
for(var i=0;i<cricle.length;i++){//记录人的位置
if(cricle[i]==0){//当判断这个位置是0时
num++;//计数器加1
if(num==m){//当计数器与规定的计数(3)相等时
count++;//杀的人数加1
cricle[i]=1;//将这个人的开关置1
num=0;//为下一次准备计数器清零
}
if(count==n-1){//count与总人数少1相等时(杀了总人数少1)
break;跳出循环
}
}
}
}
}
for(var i=0;i<cricle.length;i++){遍历开关层
if(cricle[i]==0){找到开关为0的人
console.log(i+1);输出他的位置
}
}
}
joseph(10,3);
结果
4
第4个位置的人可以活下来。