约瑟夫环

“约瑟夫环”是一个数学的应用问题:一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数, 再数到第m只,在把它踢出去…,如此不停的进行下去, 直到最后只剩下一只猴子为止,那只猴子就叫做大王。要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。

看了一些这个环的解决方案,看的时候明白,看完就忘了如何实现,突然自己写出了一种,逻辑分析加递归。


思路:

遍历猴子编号的数组,设置查找指针,到了间隔的值,就把该值置为0,表示是被剔除的猴子,然后指针重置。

遇到值为0的位置,说明该位置的猴子已经被剔除,所以不做处理,指针继续,位置计数记得要加一。继续按照上述方法查找。

位置到达数组最后一位,就重新递归遍历该数组,进行查找。

若该数组所有位置上都是0,只有一位是大于0的数,表示猴王出现。这个检查要在被剔除一个猴子的时候检查。


<?php 

/*
 * 判断新循环的数组中是否只有一个猴子,如果是,就是猴王,如果不是,继续递归查找。
 */
function getZreo( $array )
{
	$num = 0;
	foreach( $array as $key => $value )
	{
		if( $value == 0 )
		{
			$num++;
		}
	}
	if(( count($array) - $num ) == 1){
		return 1; //可以返回
	} else if(count($array)==$num){
		return -1; //返回值错误
	} else {
		return 0; //继续查找
	}
}


/*
 * 递归获取猴王
 * @param array $array 初始的数组
 * @param int $m 踢出去的间隔值
 * @param int $k 查找指针
 * 
 */
function getMonkeyKing( $array, $m,$k=1 )
{
	
	if( getZreo($array) == 1 )
	{
		return $array[0];
	}
	//$k = 0; //指针
	$c = count($array);
	$t = 1; //位置计数t 和 $c 比较,相等(表示位置已经达到数组的尾部)重新循环
	foreach( $array as $key => $value )
	{
		//如果循环值为0,所以已经是剔除的位置,直接循环下一个值,指针不变,但是位置计数加1
		if( $value == 0 )
		{
			if( $t == $c ) //保证t=5 value=0的时候,可以运行
			{
				$array = getMonkeyKing( $array, $m, $k );
				return $array;
			}
			$t++;
			continue;
		}
		//剔除位,置为0
		if( $k==$m){ 
			//$t是位置编号,剔除当前位置是t-1的值。
			$array[$t-1] = 0;	
			//把指针重新赋值,因为最后要对k做加一处理,这里k=0
			$k = 0;
			
			//每次剔除一位,就判断一下数组中是否查出猴王
			$result = getZreo( $array );
			//查出猴王就做输出
			if( $result == 1 )
			{
				//变换最终结果数组
				foreach( $array as $k=>$v)
				{
					if($v>0)
					{
						return $v;
					}
				}
				
				return $array;
			}
		}
		//正常情况下循环完本次遍历,排除以上两种情况。
		if( $t == $c ) //重新循环
		{
			$array = getMonkeyKing( $array, $m, $k+1 );
			return $array;
		}
		//保证正常循环,查找指针加一,位置计数加一
		$k++;
		$t++;
		
	}
}

//$arr = array(1,2,3,4,5);
$arr = range(1,11);
$m = 3;
$king = getMonkeyKing($arr, $m );
var_dump($king);
//echo getZreo(array(0,0,0,0,0));
?>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值