[博客迁移][算法] 约瑟夫环

498 阅读4分钟

from 约瑟夫环


引言:不知不觉已经大三下学期了,还记得当时大二下学期算法课上老师提到过这个算法,当时课上没听懂,后来课后请教老师,弄懂了。不过这学期,ztt他们上算法课遇到这个问题,我本来想教她的,结果发现自己突然不会了,很惭愧,于是赶紧补一下知识点。我觉得光看懂不行,必须写成博客,加深自己的理解,也方便以后忘记的时候,快速回想起来。


目录

1.进入正题
2.开始讲解代码


进入正题

题目描述:

30个游客同乘一条船,因为严重超载, 加上风浪大作,危险万分。因此船长告诉乘客,只有将全船 一半的旅客投入海中,其余人才能幸免于难。无奈,大家只 得同意这种办法,并议定30 个人围成一圈,由第一个人数起,依次报数,数到第9人,便把他投入大海中,然后再从 他的下一个人数起,数到第9人,再将他投入大海中,如此 循环地进行,直到剩下 15 个游客为止。问:哪些位置是将 被扔下大海的位置?

首先给出代码

/**
* @method 约瑟夫递归
* @param sum 约瑟夫环的长度(人数)
* @param value 步长(在题目描述中为9)
* @param n 第几轮
* @return 在约瑟夫环中的位置(下标从0开始)
*/
int ysfdg ( int sum, int value, int n)
{
    if ( n == 1 )
        return ( sum + value - 1 ) % sum;
    else
        return ( ysfdg ( sum-1, value,n-1 ) + value ) % sum;
}

例如,约瑟夫环:0, 1, 2, 3, 4, 5, 6, 7, 8, 9。这里的数字代表人的编号。 ysfdg(10,4,3)就表示长度为10的约瑟夫环,在步长为4的前提下,第三轮落水的人的编号。如果这个值为4,则说明编号为4的人第三轮落水。

开始讲解代码

首先,从最简单的n等于1说起。 当n为1时,那么ysfdg(10,4,1)就等于3,这是显而易见的。

那么如何求n等于2呢?当然,我们可以一个一个数,数到第8个,即编号为7的人,但是这样没有一般性,我们的解法是这样的。

第一层约瑟夫环(长度为10):
0,1,2,3,4,5,6,7,8,9
当选完第一个人ysfdg(10,4,1)==3后,约瑟夫环会有空缺:
0,1,2,X,4,5,6,7,8,9
我们创建第二层约瑟夫环(长度为9):
6,7,8,X,0,1,2,3,4,5

那么,在第二层约瑟夫环中,选取第一个人,即ysfdg(9,4,1)==3,这里代码的意思是,在第二层约瑟夫环(长度为9)中,选取第一个人(为什么是第一个人呢?因为在第一层约瑟夫环中,已经选出一个人了,所以在第二层约瑟夫环中,选第一个人,即第一层约瑟夫环中的第二个人)。

我们现在已经知道,在第二层约瑟夫环中,选中的人是编号为3的人,那么他对应在第一层约瑟夫环中是编号为几呢?ysfdg(10,4,2)==(ysfdg(9,4,1)+4)%10,这就是两层约瑟夫环之间的递推关系。

通过这个关系,如果我们要求ysfdg(10,4,3),即第三个被选中的人,那么只需要去求第三层约瑟夫环中第一个被选出的人的编号,然后求出这个编号对应于第二层约瑟夫环中的编号,然后求出对应于第一层约瑟夫环中的编号,即答案。

求解过程:
第一层:0,1,2,3,4,5,6,7,8,9
第二层:6,7,8,X,0,1,2,3,4,5
第三层:2,3,4,X,5,6,7,X,0,1

ysfdg(8,4,1)==3对应于第三层约瑟夫环中的编号
ysfdg(9,4,2)==(3+4)%9==7对应于第二层约瑟夫环中的编号
tsfdg(10,4,3)==(7+4)%10对应于第一层约瑟夫环中的编号

参考博客:约瑟夫环问题--递归解法的理解