约瑟夫环 & 猴王问题

本文详细介绍了使用PHP解决约瑟夫环问题的三种方法:普通循环法、递归法和数学模拟法。通过实例代码展示了每种方法的具体实现过程及运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题来源:

据说是新浪的一个面试题,具体题目如下:

 一群猴子排成一圈,按1,2,...,n依次编号。

然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,  再数到第m只,在把它踢出去...,

如此不停的进行下去,  直到最后只剩下一只猴子为止,那只猴子就叫做大王。

要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。

其实这是一个典型的约瑟夫环的问题,下面给出几种解法:

解法一:普通循环法

<?php
/**
 * 猴王问题普通循环算法
 * 将所有猴子编号放入一个数组
 * 开始循环数组
 * 判断是否只剩一个猴子,如果是,则猴王诞生,结束循环
 * 猴王未诞生,继续往下执行
 * 判断是否到第m个猴子了,如果到,踢出猴子,同时计数器归0,如果不到,不做任何操作,继续循环
 * 数组循环结束了,应该重置数组,继续下一轮的循环
 * 
 */	
function kickMonkey($n, $m) {
	$monkey = range(1, $n);			// 给猴子编号,生成数组
	
	$i = 0;
	while(list($key, $val) = each($monkey)) {		// each - 返回数组中当前的键/值对并将数组指针向前移动一步 
		if (count($monkey) == 1) {		// 剩最后一个猴子,你就是猴王了
			 echo  $val . '成为猴王了<br />';
			 exit;
		}
		
		if (++$i == $m) {
			echo $monkey[$key] . '出局<br />';
			unset($monkey[$key]);
			
			$i = 0;
		}
		
		if (!current($monkey)) {		// 循环到头了,重置数组
			reset($monkey);
		}
		
	}
}
kickMonkey(9,5);
?>


得到结果

5出局
1出局
7出局
4出局
3出局
6出局
9出局
2出局
8成为猴王了

解法二:递归法

<?php
/**
 * 猴王问题递归算法
 * 递归方法的关键点是需要引入一个计数器,计数器为当前开始数的第一个猴子的索引号(注意,非猴子自身的号码)
 * 老样子,先将猴子编号塞入数组待用
 * 判断是否只剩一个猴子,如果是,则猴王诞生,递归结束
 * 获取要出局的猴子的索引号
 * 将猴子踢出数组,重置猴子索引,开始递归
 */
function kickMonkey($monkey, $m, $index = 0) {
	$count = count($monkey);
	if ($count == 1) {
		echo current($monkey) . '成为猴王了<br />';
		exit;
	}
	
	/*
	 * 获得踢出的猴子索引号
	 * 每次踢出下一个猴子的编号为当前踢出猴子编号+$m-1,如果数组到头,则重头开始数
	 * 即第一次踢出索引为(0+5-1)% 9 == 4的猴子
	 * 第二次踢出 (4+5-1)%8 == 0 的猴子
	 * 第三次踢出(0+5-1)%7 == 4的猴子
	 */
	$index = ($index + $m - 1) % $count;
	
	echo $monkey[$index] . "出局<br />";
	unset($monkey[$index]);
	
	kickMonkey(array_values($monkey), $m , $index);
}

$monkey = range(1, 9);
kickMonkey($monkey, 5);
?>


得到结果

5出局
1出局
7出局
4出局
3出局
6出局
9出局
2出局
8成为猴王了

解法三:数学模拟型

<?php
function kickMonkey($n, $m) {
	$index = 0;
	for ($i = 2; $i<=$n; ++$i) {
		$index = ($index + $m) % $i;
	}
	echo ($index + 1) . '成为猴王了!';
}

kickMonkey(9,5);
?>


得到结果


8成为猴王了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值