PHP实现约瑟夫问题:递归与递推两种方法详解

咖啡半糖
咖啡半糖
发布时间:2026-10-14 | 5人浏览过
原创

约瑟夫问题(Josephus Problem)是一个经典的数学与编程应用题,通常描述为n个人围成一圈,从第一个人开始报数,每次数到m的人出列,直到只剩一人。本文将通过PHP语言,提供两种实现思路:一种是通过数组操作的递归模拟,直观易懂;另一种是利用数学规律的高效递推,适合大规模数据。

问题场景与基本逻辑

约瑟夫环的问题可以具体化为:一群猴子排成一圈,按1至n依次编号。从第1只开始数,数到第m只时将其移出队列,再从下一只重新从1开始计数。重复此过程直到最后只剩一只猴子,即为获胜者(大王)。我们需要编写程序输入总人数n和报数上限m,输出最终获胜者的原始编号。

方法一:基于数组的递归模拟

这种方法最符合题目的文字描述,通过维护一个包含所有参与者编号的数组,不断移除元素并递归调用自身,直到数组中只剩下一个元素。

第1步:定义递归函数getKing

目的:处理当前的猴子队列和报数位置。

  • $monkeys:当前剩余的猴子编号数组。
  • $m:报数的目标值。
  • $current:当前起始位置的索引。

入口与动作:

  1. 首先判断数组长度是否为1,若是则直接输出获胜者编号并终止递归。
  2. 若不止一只猴子,使用while循环移动$current指针,配合模运算% $number确保索引在数组范围内循环。
  3. 找到需移除的元素后,使用array_splice()删除该元素。
  4. 再次递归调用getKing,传入更新后的数组和新起点。

第2步:初始化参数并执行

设置总数量$n=13,报数值$m=34。使用range(1, $n)生成初始编号数组,然后调用函数。

<?php
function getKing($monkeys , $m , $current = 0){
  $number = count($monkeys);
  $num = 1;
  
  // 基本情况:只剩一只猴子
  if(count($monkeys) == 1){
    echo '<font color="red">编号为'.$monkeys[0].'的猴子成为猴王了!</font>';
    return;
  }else{
    // 移动到需要踢出的位置
    while($num++ < $m){
      $current++ ;
      $current = $current%$number;
    }
    echo "编号为".$monkeys[$current]."的猴子被踢掉了...<br/>";
    // 移除该猴子
    array_splice($monkeys , $current , 1);
    // 递归调用
    getKing($monkeys , $m , $current);
  }
}

$n=13;   // 总猴子数目
$m = 34; // 报数上限
$monkeys = range(1,$n); // 初始化数组
getKing($monkeys , $m);   // 执行
?>

方法二:基于数学公式的递推解法

当n的值较大时,递归模拟会因为频繁的数组操作和栈深度而效率低下。约瑟夫问题存在一个明确的数学递推公式,可以直接计算出最后幸存者的位置。

第1步:理解递推公式

设f(n, m)为n个人报到m时幸存者的编号(从0开始计):

f(n, m) = (f(n-1, m) + m) % n
f(1, m) = 0

为了适配题目中从1开始计数的编号,最终结果需加1。

第2步:代码实现getLeader

通过一个简单的for循环迭代计算即可得到结果。

<?php
function getLeader($n,$m) { 
  $res=0; 
  for($i=2; $i<=$n; $i++) {
    $res=($res+$m)%$i; 
  }
  return $res+1; 
} 

// 测试用例
$leader = getLeader(13,34);
echo $leader;?>

结果对比与总结

上述两种方法针对相同输入n=13, m=34均能得到一致的正确答案。递归法胜在逻辑直观,便于初学者理解“淘汰”过程;递推法则以O(n)的时间复杂度和极低的内存开销,展现出更高的算法效率。

以上就是PHP实现约瑟夫问题的详细内容,更多关于PHP算法优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表