用PHP实现丢手帕问题(约瑟夫问题)

本文探讨了约瑟夫环问题,并提供了两种解决方案:一种是使用PHP进行模拟实现,适用于较小规模的问题;另一种是通过数学推导的方式,适用于大规模问题的高效解决。

问题描述:有n个人围成一圈,然后从任意指定的一个 人那里为起点,以m个人为单位,每转m个人第m个人被杀死。求最后不会被杀死的人。

遗留问题:

在此用php做简单的实现,php中对递归有100次的深度限制,所以在此不用递归,用循环;php中处理数组的函数比较多,所以采用顺序表(数组),顺序表删除元素比较复杂,所以效率比较低,只能处理10000一下的数据。链表中的遍历比较复杂,同样会导致效率低下,以后再做顺序表与链表的结合。

模拟实现:

[php]  view plain  copy
  1. class Dhc  
  2. {  
  3.     private function dropHandkerchief($start=0,$distance,$menArray)  
  4.     {  
  5.         $count = count($menArray);    
  6.         $pos = $distance - 1;    
  7.         $start = $start > ($count-1) ? 0 : $start;//开始位置大于总人数则默认从第一个开始    
  8.         $pos = $start + $pos;//第一个要被出列的人的位置,pos为下标,所以要 -1;    
  9.         while($count > 1)    
  10.         {    
  11.             if($pos < $count)//判断要出列的人的位置是否超出数组大小,超出则减去(或取模)数组大小,从头开始    
  12.             {    
  13.                 echo "第"$menArray[$pos] ."人出列<br />";    
  14.                 array_splice($menArray,$pos,1);//删除要出列的人    
  15.                 $count  = count($menArray);//重新计算大小    
  16.                 $pos += $distance - 1;//下一个要出列的人的位置,pos 为要数的第一个人,所以第 n 个人的下标为 pos + n -1    
  17.             }else    
  18.             {    
  19.                 //$pos -= $count;    
  20.                 $pos = $pos % $count;    
  21.             }  
  22.         }    
  23.         echo '<br />';    
  24.         echo "第" .$menArray[0]. "人被留下";    
  25.     }    
  26.   
  27.     public function drop()  
  28.     {  
  29.         $menArray = array();//    
  30.         $total = 100;//总人数    
  31.         $distance = 50;//间隔人数    
  32.         $start = 3;//从第几个人开始    
  33.         $i = 0;    
  34.         while($i < $total)//初始化    
  35.         {    
  36.             $menArray[$i] = $i + 1;    
  37.             $i++;    
  38.         }    
  39.         $this->dropHandkerchief($start$distance$menArray);    
  40.     }  
  41. }  

数学推导实现:(20170914)

简单改变一下问题的描述:有 n 个人,编号是 0 - n-1,从 0 开始数,数到 m 则 m 死,下一个人继续从 0 开始数,直到只剩最后一个人,求这个人最开始的编号。

每死一个人就重新开始,相当于减小了问题的规模,就是要解 n 个规模的解:n, n-1, n-2, n-3 …… 3, 2, 1。

假如在第二轮(n-1个人的规模)中死的那个人编号是 x(这个编号是第一个人死后,重新从 0 开始编排的),则可以推导出这个人在第一轮(人数为 n 时)中的编号是:(x + m)%n。

(n-2)中死的人在(n-1)中的编号是:(x + m)%(n-1)

(n-3)中死的人在(n-1)中的编号是:(x + m)%(n-2)

……

( 1 )中死的人在( 2 )中的编号是:(x + m)%2, 此时 x = 0;

把上面的过程倒过来,已知规模为 1 时,x = 0;

求规模为 2 时,x2 的值:(x + m) % 2 = x2

求规模为 3 时,x3 的值:(x2 + m) % 3 = x3

 ……

求规模为 n 时 x 的值。

[php]  view plain  copy
  1. $n = 100;  
  2. $m = 3;  
  3. $s = 0;  
  4.   
  5. $x = 0;  
  6. for ($i=2; $i<=$n$i++) {  
  7.     $x = ($x + $m) % $i;  
  8. }  
  9. echo ($x + $s) % $n;  
  10. // $s=0,表示从第 0 个开始数,如果不是从 0 开始,则只需要向后推 $s 个即可 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值