约瑟夫环的递推公式个人解析

        经典问题约瑟夫环:有n个人围成一圈,第一个人从1开始报数,报m的人将被淘汰出圈,下一个人接着从1开始报。如此反复,直到最后留下一个胜利者。

例如有三个人甲、乙、丙,他们围成一圈,从甲开始报数,规则是报2的人被淘汰。第一轮,甲报1,乙报2,乙被淘汰;第二轮,丙接着报1,甲报2,甲被淘汰;最后丙获胜。

        采用循环链表法可以解决问题,但时间复杂度较高,实现思路比较简单,这里不做讨论。接下来就递推公式法谈一谈我个人的理解,欢迎大家指正。

先给出递推公式:f(n,m)=(f(n-1),m)+m)%n 。其中n表示共有n个人参加报数,m表示报到m的人被淘汰出局。

现以n取10,m取3为例,我们用数字编号1,2,3,4,5,6,7,8,9,10来表示这10个人,他们先排成一排,每报到3的人将被淘汰。

  • 游戏开始,头一个人编号是1,从他开始报数,第一轮被淘汰的是编号为3的人。
  • 编号为4的人从1重新开始报数,这时我们可以认为编号4这个人是队伍的头,新队伍有9个人,旧编号为4的这个人有了一个新编号是1。第二轮新编号是3旧编号为6的人被淘汰。
  • 编号为7的人从1重新开始报数,这时我们可以认为原始编号7这个人是队伍的头,新队伍只有8个人了,原始编号为7,上一轮编号是4的人成为了新队伍的头,他现在的最新编号是1。第三轮原始编号为9的人被淘汰。
  • ......
  • 直到最后,原始编号为4的人获胜。

第一轮如图所示:

12345678910
X4567891012xx
123456789xx

可以观察得到规律:(1+3)%10=4 ; (2+3)%10=5 ; (3+3)%10=6 ; 但有一个问题:(7+3)%10=0!

基本符合公式 f(10,3)=(f(9,3)+3)%10,f(10,3)所代表的其实就是旧的编号,f(9,3)所代表的就是新的编号。通过递推公式,我们就能轻易的用新的编号来表示旧的编号,也就能用最新的编号(即n=1时)来表示最原始的编号了!

怎么样解决旧编号中最大的那个(即排在末尾的人的编号)无法用递推公式表示的问题呢?我们可以先从0开始编号,最后通过递推公式得出旧编号后再加一即可。

从0开始编号,第一轮如图所示:

0123456789
淘汰X345678901xx
012345678xx

        可以看出,现在完全符合递推公式了。(0+3)%10=3 ; (7+3)%10=0 ; (8+3)%10=1 。完全符合递推公式 f(10,3)=( f(9,3)+3)%10 。后续 f(9,3)=( f(8,3)+3)%9 ,直到最后 f(2,3)=( f(1,3)+3)%2  也完全正确。检验最后的式子如下表:

01
淘汰X1Xx
0

这个递推公式的推导主要就在于新旧队伍编号之间的对应关系,每轮执行淘汰后,就要以接下来报1的人为新的队头,建立一个新队伍。然后想办法用新的编号来表示旧的编号,这样就可以不断用更新的编号来表示之前的旧编号,直到最后结束,编号无法再更新为止。找关系的过程中要特别注意是否有特殊情况,如果有特殊情况不满足一般关系式,就要想办法解决。除上述改变起始编号外,我以为或许还能用 if 语句判断是否为0来解决上述(7+3)%10=0 的问题,不过改变起始编号的方式更为巧妙。以上就是我对约瑟夫环问题的递归解决公式的理解。以下附上我的代码(以m=3为例)

package com.exerciseproblems.question49;

import java.util.Scanner;

//有 n 个人围成一圈,顺序排号。从第一个人开始报数
// (从 1 到 3 报数),凡报到 3 的人退出圈子,
// 问最后留下的是原来第几号的那位。
public class T49Plan1 {
    public static void main(String[] args) {
        System.out.print("请输入总人数 n :");
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        System.out.println("最后留下来的是原来的第 " + (winnerOfJoseph(n) + 1) + " 号。");
        sc.close();
    }

    public static int winnerOfJoseph(int n) {
        if (n == 1) {
            return 0;
        } else {
            return (winnerOfJoseph(n - 1) + 3) % n;
        }
    }
}

运行结果如图:        

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值