约瑟夫环递推公式解析

约瑟夫环递推公式解析

在力扣做了面试题第62题,是一个约瑟夫环问题,作为一个转行java的,并不知道有约瑟夫环这种经典算法题,于是提交了java暴力的解法。

class Solution {
    public int lastRemaining(int n, int m) {
        List<Integer> ns = new ArrayList<Integer>();
		for(int i = 0;i<n;i++){
			ns.add(i);
		}
		int point = 0;
		for(int i = 0;i<n-1;i++){
			int mp = m+point;
			point = (mp-1)%ns.size();
			ns.remove(point);
		}

		return ns.get(0);
    }
}

提交代码发现成绩令人汗颜,于是查看了别人的解法,才发现有递推公式的数学方法,不过较难理解,现在记录一片博客总结一下。

 public static int lastRemaining(int n, int m) {
        int flag = 0;
        for(int i=2;i <=n;i++){
            flag = (flag + m)%i;
        }
        return flag;
    }

由代码可以看到核心递推公式为 a_{n-i} = (a_{n} + 3 ) % i % i ,其中总人数为n ,第n-1轮存活人的当前数组坐标为0 ,原数组坐标为a_{0}

暴力解法的逻辑,是按照题意,每轮杀了谁,就让数组中这个人移除数组,直到最后一轮得出这个人的数组角标。递推公式的逻辑则相反,求的是最后存活下来的这个人在原数组中的位置。

1.以总5人 每次第3个人为例

轮数\位置01234
001234
13401 
2134  
313   
43    

可以看到第一轮之后,原数组下标为3的元素的当前数组下标为0,第二轮要被杀掉的是当前数组下标为2原数组下标为0的元素。

可以看出每次,杀掉一个人之后,所有元素的当前下标-3,即左移三位,减3后小于0的按循环补位。

2.由以上思路可以反向思考,便可以得到递推公式。已知轮数是有总人数决定的,所以设最后存活的人在原数组的坐标为Y,可知第4轮为

轮数\位置01234
4y    

 

可以知道y这个人的第4轮后当前数组坐标必为f(4)=0,逆推第3轮后,剩下的人的原数组坐标为x,y,此时可以逆推y的当前数组坐标为第4轮后的当前数组坐标+3即:

轮数\位置01234
3xyxy 
4y    

 

所以第三轮y的当前数组坐标为 f(4)+3 = 3 ,又因为第3轮后最多只有2个元素,所以按照循环补齐f(3)=(f(3)+3)% 2 = 1 ,所以第3轮后y的当前数组坐标为1

同理第二轮 为 坐标为f(2) = f(2)+3 = 4

 

其中第二轮后最多只有3个元素,所以第二轮f(2) = (f(3)+3) % 3 = 1,所以第2轮后y所在当前数组坐标为1

同理,接下来就是第一轮后是y所在的数组位置

(黄色y为模拟右移3位的位置,黑色y为第1轮后y的实际位置,红色字母为补充位置方便展示循环补位)

f(1) = (f(2)+3) % 4 = 0

最后一步

f(0) = (f(1)+3)%5 = 3,此时的当前数组位置就是原数组位置,就可以知道最后存活下来的人在原数组的位置在哪里了。

总结递推公式为 a_{n-i} = (a_{n} + 3 ) % i % i 其中总人数为n ,第n-1轮存活人的当前数组坐标为0 ,原数组坐标为a_{0}

i  = n-完成轮数  例如:f(3) = (f(4) + 3)  % 2

于是有了下面的代码来求存活下来的元素的原数组下标,i从2开始的,因为i为1时当前坐标必为0。

 public static int lastRemaining(int n, int m) {
        int flag = 0;
        for(int i=2;i <=n;i++){
            flag = (flag + m)%i;
        }
        return flag;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值