约瑟夫环问题的递推解法(数学解法)


问题背景

约瑟夫环问题的起源可以追溯到古罗马时期的一个传说。在一次战争中,40个士兵被敌人包围,为了避免被俘,他们决定自杀。他们围成一个圈,每数到第3个人就自杀,直到最后只剩下一个人。这个问题被称为约瑟夫问题,是因为其中一个士兵约瑟夫(Josephus)据说通过计算的位置逃过了这一劫。


一、递推公式

从最基本的情况开始分析

题目可以简化为: 有N个人排列成一个环,每隔M个人就淘汰一个人,直到只剩下一个人。我们需要找出最后幸存者的位置。
当只有一个人时N=1,显然他是幸存者,位置为0。
当有两个人时N=2,每数到第 M个人自杀,剩下的人的位置可以通过递推公式计算出来。递推公式的推导过程如下
假设 N=5, M=3

  • N=1时,幸存者的位置是:
    f(1,3)=0
  • N=2时,幸存者的位置是:
    f(2,3)=(f(1,3)+3)%2=(0+3)%2=1
  • N=3时,幸存者的位置是:
    f(3,3)=(f(2,3)+3)%3=(1+3)%3=1
  • N=4时,幸存者的位置是:
    f(4,3)=(f(3,3)+3)%4=(1+3)%4=0
  • N=5时,幸存者的位置是:
    f(5,3)=(f(4,3)+3)%5=(0+3)%5=3

所以,当N=5时,最后幸存者的位置在从0开始编号的情况下是3
递推公式为:f(N,M)=(f(N−1,M)+M)%N
我们可以用这个公式逐步计算出较大的 N 的情况。

为什么人数为N时的幸存者位置,取决于N-1?

回顾递推公式

f(N,M)=(f(N−1,M)+M)%N

这个公式的核心思想是:如果我们知道N−1个人时的幸存者位置,那么可以通过这个位置计算出 N个人时的幸存者位置。
假设我们已经知道N−1个人时的幸存者位置为f(N−1,M)
当有N个人时,我们从第一个人开始数,每数到第M个人时,把这个人移除。
移除这个人后,剩下的N−1个人的情况和之前的情况是一样的,只是位置发生了变化

位置变化的详细分析

为了推导递推公式,我们先理解从N个人到 N−1个人的转换过程。
假设我们知道N−1 个人的情况下,最后幸存者的位置是f(N−1,M)
N 个人到N−1 个人
当有N 个人时,编号从 0N−1
第一次淘汰发生在编号为(M−1)%N的人。
淘汰后,剩下N−1个人,并且从淘汰的下一个人开始重新编号。
重新编号
淘汰编号为(M−1)%N的人后,剩下的N−1个人的编号变为:
(M%N),(M+1%N),…,(N−1),0,1,…,(M−2)
我们可以将这些编号重新映射为从0N−2的新编号:
0,1,2,…,N−2
对于N−1个人,我们已经知道幸存者的位置是f(N−1,M),这个位置是重新编号后的相对位置。
我们需要将这个相对位置转换回原来的编号体系
重新编号的第f(N−1,M)个人在原来编号中的位置是:(f(N−1,M)+M)%N

二、代码实现

1.递归实现

public class JosephusProblem {
    public static int josephus(int n, int m) {
        if (n == 1) {
            return 0;
        } else {
            return (josephus(n - 1, m) + m) % n;
        }
    }

    public static void main(String[] args) {
        int n = 5; // 总人数
        int m = 3; // 每数到第 m 个人
        int result = josephus(n, m); // 结果从 0 开始编号
        System.out.println("最后剩下的人的位置是: " + result);
    }
}

2.迭代实现

public class JosephusProblem {
    public static int josephus(int n, int m) {
        int ans = 0;
        for (int i = 2; i <= n; i++) {
            ans = (ans + m) % i;
        }
        return ans;
    }

    public static void main(String[] args) {
        int n = 5; // 总人数
        int m = 3; // 每数到第 m 个人
        int result = josephus(n, m); // 结果从 0 开始编号
        System.out.println("最后剩下的人的位置是: " + result);
    }
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值