假定一共有n个人围成一圈,随机从某个人开始报数,从0报数,第m个报数的人(报数为m-1)从圈中删除。那么最后剩下的人的在第一轮中报数是多少(即,开始时候的编号是多少?)
假定这里剩下的人是JACK,令函数f (n,m)表示jack在首轮n个人中的编号
第一轮n个人理论上报数顺序应该是 0 1 2 。。。 n-1(只不过报到M-1之后,后面的人不用继续报出来,但是编号还是存在的)
删除一个人,这个人假设编号为K 其实这个K是可以求出的 即:K =(M-1)%N ;
所以剩下的n-1个人 是 0 1 2 K-1 k+1 。。。 n-2 n-1
第二轮开始,要从第一轮中编号为 K+1 的那个人开始报数,报数的顺序在第一轮中对应的报数为
Num1: K+1 K+2 ... N-1 0 1 2 .... K-1
但是他们报数喊出的数还是要从0开始 ,也就是说第二轮开始对他们进行重新编号,编号为m-1的人要出列。
Num2: 0 1 N-K-2 N-2
所以如果知道第二轮Jack的编号为x的话,是可以反推出Jack在第一轮中的编号的。
即 Num2(x') ---》 Num1(x)的转换为:
0 K+1
1 k+2
。。。。。。。
N-2 K-1
总结规律是:用x表示上一轮的编号,x'表示这一轮的编号
那么 x = (x' +k+1)%n = (x'+ (m-1)%n +1 )%n = (x' + m) %n
所以对于Jack有上一轮他的编号与本轮编号之间的关系: f (n.m) = (f (n-1,m)+m)%n
那么最后剩下Jack一个人时,他的编号肯定是0所以有
f(n,m) = 0 ;n=1
f(n,m) = (f (n-1,m)+m)%n; n>1
那么利用迭代递推就可以从一开始确定第一次报数的人当中报数为几的人最终回留下来,编码实现就很简单了
public int Recusion(int n,int m){
if(n<1|m<1){return -1;}
int f = 0;
for(int i=2;i<=n;i++){
f =(f+m)%i;
}
return f;
}