约瑟夫算法php,php经典算法题2: 约瑟夫环问题

0x00 哪只猴子可以当大王?

一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,再把它踢出去……,如此不停地进行下去,直到最后只剩下一只猴子为止,那只猴子就可以当大王。要求编程模拟此过程,输入m、n,输出最后那个大王的编号。

通常使用数组和线性表2种方法来处理。

使用数组

有2种处理过程:

构建一个n个元素的数组,按顺序进行剔除,超过末尾则从头开始,在每次删除元素后重置一下数组。

构建一个n个元素的数组,不从头开始,但将不剔除的元素放至最后,直至只剩一个元素。

线性表应用

每个猴子出列后,剩下的猴子又组成了另一个子问题。只是他们的编号变化了。第一个出列的猴子肯定是a[1] = m mod n,它踢出后剩下的猴子是a[1]+1,a[1]+2,…,n,1,2,…a[1]-2,a[1]-1,对应的新编号是1,2,3…n-1。设此时某个猴子的新编号是i,它原来的编号就是(i+a[1])%n。于是,这便形成了一个递归问题。假如知道了这个子问题(n-1个猴子)的解是x,那么原问题(n个猴子)的解便是:(x+m%n)%n=(x+m)%n。

代码

//构建一个n个元素的数组,按顺序进行剔除,超过末尾则从头开始,在每次删除元素后重置一下数组

function monkeyKing_array1(int $n, int $m){

$monkeys = range(1, $n);

$i = 0;

//猴子数量大于1就一直进行剔除

while(count($monkeys) > 1){

$i += $m - 1;

if($i > count($monkeys) - 1){ //超过末尾则从头开始

$i = $i % count($monkeys);

}

unset($monkeys[$i]);

$monkeys = array_values($monkeys);//重置(数字索引)

}

return current($monkeys);

}

//构建一个n个元素的数组,不从头开始,但将不剔除的元素放至最后,直至只剩一个元素

function monkeyKing_array2(int $n, int $m){

$monkeys = range(1, $n);

$i = 0;

//猴子数量大于1就一直进行剔除

while(count($monkeys) > 1){

if(($i+1) % $m == 0){ //第m个剔除

unset($monkeys[$i]);

}else{ //不剔除的移动到最后

array_push($monkeys, $monkeys[$i]);

unset($monkeys[$i]);

}

$i++;

}

return current($monkeys);

}

//线性表应用: 每个猴子出列后,剩下的猴子又组成了另一个子问题。只是他们的编号变化了。第一个出列的猴子肯定是a[1] = m mod n,它踢出后剩下的猴子是a[1]+1,a[1]+2,…,n,1,2,…a[1]-2,a[1]-1,对应的新编号是1,2,3…n-1。设此时某个猴子的新编号是i,他原来的编号就是(i+a[1])%n。于是,这便形成了一个递归问题。假如知道了这个子问题(n-1个猴子)的解是x,那么原问题(n个猴子)的解便是:(x+m%n)%n=(x+m)%n。

function monkeyKing_linear(int $n, int $m){

$s = 0;

for($i=1; $i<=$n; $i++) {

$s = ($s+$m) % $i;

}

return $s+1;

}

$n=6; $m=2;

echo monkeyKing_array1($n,$m).',';

echo monkeyKing_array2($n,$m).',';

echo monkeyKing_linear($n,$m);

运行结果:

5,5,5

当然也可以用递归的方式来处理, 这里就省略了。

0x01 如何利用约瑟夫环来保护你与你的朋友?

据说著名犹太历史学家Josephus(约瑟夫)有过以下的故事:在罗马人占领乔塔帕特后, 39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,报数每报到第3个人该人就必须自杀,然后再由下一个人从1开始重新报数,直到所有人都自杀身亡为止。

然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第x个与第y个位置,于是逃过了这场死亡游戏。

如何才能避免被自杀呢? 那就只能让他们俩是最后两个自杀的人, 这样前面的人都死了, 没有人会知道他们俩没有自杀。So, 我们要求出所有的1-41人的自杀的顺序:

//获取所有人的自杀顺序

function get_suicide_sequence(int $n, int $m){

$arr_sequence = [];//编号与自杀顺序的数组: 自杀顺序=>人员编号

$men = range(1, $n);

$i = 0;

$suicide_sequence = 1;//自杀顺序

//人数大于1就一直进行

while(count($men) > 1){

if(($i+1) % $m == 0){ //第m个自杀

$arr_sequence[$suicide_sequence] = $men[$i];

$suicide_sequence++;

unset($men[$i]);

}else{ //不剔除的移动到最后

array_push($men, $men[$i]);

unset($men[$i]);

}

$i++;

}

$arr_sequence[$suicide_sequence] = current($men);//最后一个人

return $arr_sequence;

}

$rt = get_suicide_sequence(41,3);

print_r($rt);

执行结果是: (自杀顺序=>人员编号)

Array

(

[1] => 3

[2] => 6

[3] => 9

[4] => 12

[5] => 15

[6] => 18

[7] => 21

[8] => 24

[9] => 27

[10] => 30

[11] => 33

[12] => 36

[13] => 39

[14] => 1

[15] => 5

[16] => 10

[17] => 14

[18] => 19

[19] => 23

[20] => 28

[21] => 32

[22] => 37

[23] => 41

[24] => 7

[25] => 13

[26] => 20

[27] => 26

[28] => 34

[29] => 40

[30] => 8

[31] => 17

[32] => 29

[33] => 38

[34] => 11

[35] => 25

[36] => 2

[37] => 22

[38] => 4

[39] => 35

[40] => 16

[41] => 31

)

从自杀顺序上来看, 约瑟夫只要把自己和朋友放在16和31这2个位置上, 就可以避免被自杀的情况。

394720aad69e760fc24894051e527e1e.png

题目和部分资料整理自互联网。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值