约瑟夫环问题

约瑟夫环历史

约瑟夫环问题(Joseph Ring)是以弗拉维奥·约瑟夫命名的,它是1世纪的一名犹太历史学家。

他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中,最终决定自杀,并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人向罗马军队投降,不再自杀。约瑟夫斯把他的存活归因于运气或天意。

问题描述

已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号1的人开始报数,数到k的人出局;下一个人又从1开始报数,数到k的人出局。依此规律重复下去,直到只剩下一个人。求每次出局的人的编号及最后剩下的人的编号。

解析:题目要求输出每次出局的人的编号,因此采用模拟法模拟过程。

以5个人为例,从第一个人开始报数,数到3的人出局。

过程如下:

循环链表解法

可以采用循环链表解决约瑟夫环问题。示例如下:

public class Joseph {

    private static Node head;

    static class Node {
        int number;
        Node next;
    }

    /**
     * 创建循环链表
     * @param n 节点个数
     */
    private static void create(int n) {
        head = new Node();
        head.number = 1;
        Node p = head, q;
        for (int i = 1; i < n; i++) {
            q = new Node();
            q.number = i + 1;
            p.next = q;
            p = q;
        }
        p.next = head;
    }

    /**
     * 开始约瑟夫环淘汰过程
     * @param k 淘汰号
     */
    private static void start(int k) {
        Node node = head;
        int t = 1;
        while (node.next != node) {
            while (t < k - 1) {
                node = node.next;
                t++;
            }
            System.out.println("本次出局编号:" + node.next.number);
            node.next = node.next.next;
            node = node.next;
            t = 1;
        }
        System.out.println("===============");
        System.out.println("幸存者编号:" + node.number);
    }

    /**
     * @param total 总人数
     * @param number 淘汰号
     */
    public static void run(int total, int number) {
        if (number <= 1) {
            System.out.println("请输入正确数字(大于1)!");
            return;
        }
        System.out.println("总人数(编号1-5):" + total + "\t" + "淘汰号:" + number);
        create(total);
        start(number);
    }

    public static void main(String[] args) {
        int total = 5;
        int number = 3;
        run(total, number);
    }
}

输出结果:

总人数(编号1-5)5   淘汰号:3
本次出局编号:3
本次出局编号:1
本次出局编号:5
本次出局编号:2
=============
幸存者编号:4

迭代法求解

模拟法可以输出每次出局人的编号。如果题目不需要输出每次出局人的编号,可以采用迭代法求解。相比模拟法,迭代法的效率更高。

该算法的主要思想是寻找递归关系式。假设现在淘汰号为k,当只剩下幸存者时,其在数组中的下标为0,即 f(1,k) = 0。能否根据 f(1,k) 求出剩下两个人时,幸存者在数组中的下标,即 f(2,k) 的值呢?

详细讲解可以看下参考资料,在此只讲结果:f(2,k) = (f(1,k) + k) % 2

推广到一般情况,即

f (n,k) = ( f (n - 1,k) + k ) % n

我们可以利用这个公式推出幸存者最初在数组中的编号。

示例如下:

public class Joseph1 {
    
    public static int quickSolve(int total, int number) {
        int p = 0;
        for (int i = 2; i <= total; i++) {
            p = (p + number) % i;
        }
        //因为编号从1开始,所以p需要加1
        return p + 1;
    }
    
    public static void main(String[] args) {
        int total = 5;
        int number = 3;
        System.out.println("总人数(编号1-5):" + total + "\t" + "淘汰编号:" + number);
        System.out.println("幸存者编号:" + quickSolve(total,number));
    }
}

输出结果:

总人数(编号1-5)5   淘汰编号:3
幸存者编号:4

参考资料:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
约瑟夫问题是一个经典的问题,其中已知有n个人围坐在一张圆桌周围,从编号为k的人开始,按照顺时针方向从1开始报数,每次报到m的人出列并输出,然后继续从出列的下一个人开始,继续报数,直到圆桌上的人全部出列。 根据提供的引用和,约瑟夫问题的输入格式可以是:一个整数n代表人数,整数k代表从编号为k的人开始报数,整数m代表每次报数到m的人出列。输入的合法性要求n、k、m都要大于等于1,且k不能大于n。 如果您想解决约瑟夫问题,您可以使用以下步骤: 1. 首先,根据输入的n、k、m,确定圆桌上的人数和从哪个编号开始报数。 2. 接着,使用循环来模拟约瑟夫问题的过程,直到圆桌上的人全部出列。 3. 在每次循环中,使用计数器来记录当前报数的人的编号,当计数器等于m时,将该人出列并输出。 4. 更新下一个报数的人的编号,继续循环直到所有人都出列。 5. 最后,输出每次出列的人的编号,即为约瑟夫问题的解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [数据结构 1.约瑟夫问题](https://blog.csdn.net/c_h_q_/article/details/118579886)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [实验7-1 约瑟夫环](https://blog.csdn.net/abc767234318/article/details/101542182)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值