这个问题可能每个学算法的同学都会遇见,我没那么聪明,第一次看见时做不出来,只发现一些规律,后面看到算法也挺久才看懂,这里是将别人的算法结果做一个解析,例子是暂时网上找的递归最简洁的例子:
下面就以这个例子做一个解析,这个解法的实际问题就是n个东西围成一个圈,从一开始叫到m号的东西出去,求最后剩下的编号。(算法面试极为常见,就想知道你递归学的怎么样,就少用模拟法什么的了)
我们肯定一眼是无法进行一个通项函数的建立的,m=1的时候是个例外,剩下的是最后一个,但我们知道前一个和后一个是有一定关系的,以前上课的时候老师说这个叫解题思路或者方向。
其中变量有两个一个是人数n,一个是标号数m。所以我们求的问题函数就是f(n,m),而标号数是固定的,每次计数的时候都是这个标号,所以这样我们的思路就可以建立起来了。
首先是首项f(1,m)=0,也可以直接理解为已经完成结果留下来的就是1号,这里不影响。
然后是第2项f(2,m),这里就要看是单数还是双数了,所以就可以用取余数计算 2%m,m是双数留下的就是1,m是单数留下的就是2。
然后是第3项f(3,m),到这里的时候大家就知道就不光是单双的关系了比如m=2,留下的是3;m=3,留下的是2;m=4,留下的是2;m=5,留下的是1;m=6,留下的是1,m=7,留下的是3;等等然后再和第2项对应一下,看有什么规律,如下表:一眼看不出规律吧,没关系我们看看第三项是如何计算的,这个时候我们我要考虑队伍重置问题了,还是举简单的例子,当n=3,m=2时,第一轮编号都是123,用n除m,这里余1,也就是这一行这一圈的计数已经完成了,出局的是2,而这个2和m的关系很明显,余数是等于0的,所以这一圈结束了之后我们还有两个人,还要继续报数,那后面剩了多少人呢,也就是余数,1个(3号),所以下一圈的报数就是这样,3 1,但是他们的编号已经改变了,3变成1号,1变成2号,也就是n=2了,这个时候就回归到了第2项,这里m是双数,留下的就是1号,而1号现在是3,所以f(3,2)就是3了,这里的递归就完成了,后面怎么加都是一样的,最终都将回归到f(2,m)来得到答案,然后下一步知道原理了,就开始建立算法了,下面表是不是看起来很有规律,当然是有规律的,不过要从这个规律去解题,恐怕是一条不归路。
m 2 3 4 5 6 7 8 9 10 11 12
第2项 1 2 1 2 1 2 1 2 1 2 1
第3项 3 2 2 1 1 3 3 2 2 1 1
这里就开始算法的建立了,这个看个人思维了,有人有高效的思维就其实是因为他高效和低效的方法都知道,自然能直接选择高效的方法,多学习多思考吧。
首先列两个方程
f(1,m) =0;这里上面提过了,主要就是求f(n,m)和f(n-1,m)的对应关系了。
最简单的f(2,m)应该是多少,从上面的算法知道留下的应该是m%2,那这样结果就是0或者1,那我们需要返回的是1或者2这个之间数,这个很简单,加一个1就行,这个问题一定要考虑到,所以
f(2,m)=m%2+1 。
然后是
f(3,m)=(f(2,m)-1+m)%3 + 1 , 这里加一减一原因和上面一样,加m是因为对表进行重置,是上层表加m做下层表的计算,比如f(2,3) n=2,m=3,m%2+1就是留下来的数其中f(2,3)-1=0,那么f(3,3) n=3,m=3 就是第一轮1,2,3 ;第二轮1,2,1,2,是不是就是f(2.3)的重复(两个值交替出现)。那再往下推,f(4.3)第一轮,1,2,3,4;
第二轮;1,2,4,1,2,4这个时候是不是又和上面一样了,三个值交替出现,第三轮,1,2,1,2换算成公式,r2= m%2, r3=(r2 +m)%3, r4=(r3+m)%4,这个r就是在m定值下取的余数举个例子大家就知道了,当m=3时,n=2时,r2=1 ,那n=3的时候我们把这个r2=1加到上面也就是r3=(3+1)%3=1 说明余了一个数,那留下来的就是2号,然后我们在返回来推,在n=3时是1,2,3中的余数是0,留下来的就是1,2继续余就是1,留下来的就是2,那这里最后的余数是1,而r2+1=2=f(2,3)
那算法就建立好了我们带入检查一下吧, 这个时候就可以对照上面的表了,随机选两个值,f(2,3)=2, f(3,3) = (2-1+3)%3 +1 =2,嗯这一组正确;
下一组,f(2,7)= 2, f(3,7) =(2-1+7)%3 +1 =3 ,嗯也正确,
最后一组,f(2,12) =1, f(3,12) = (1-1+12)%3+1 =1 ,ok没有问题,那说明约瑟夫环的递归公式我们已经推导出来了。
然后就是写入程序,其实有上面的公式再看下面的就很好理解了。
最终该问题的递归公式为
f(2,m)=m%2+1
f(3,m)=(f(2,m)-1+m)%3 + 1
f(n,m)=(f(n,m)-1+m)%n + 1
package test;
public class cl {
public void way1(int n, int m){
int r = 0;
for (int i = 2; i <= n; i++) {
r = (r + m) % i;
}
System.out.println(r+1);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
cl a1 = new cl();
a1.way1(3, 7);
}
}