算法:用Java求解约瑟夫环(Joseph Circle)的问题

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏

接下来,我就用Java来模拟这一过程,并得到上文所说的16和31两个位置,以躲过这场厄运:

import java.util.ArrayList;
import java.util.List;

/**
 * @author LiYang
 * @ClassName JosephCircle
 * @Description 约瑟夫环算法实现
 * @date 2019/10/31 16:32
 */
public class JosephCircle {

    /**
     * 求解约瑟夫环的算法
     * @param player 参与人数
     * @param interval 报数的间隔
     * @return 约瑟夫环的答案顺序
     */
    public static List<Integer> calculateJosephCircle(int player, int interval){
        //排队围成圈的队伍
        List<Integer> queue = new ArrayList<Integer>(player);
        
        //最后返回的答案顺序
        List<Integer> order = new ArrayList<Integer>();
        
        //初始化所有参与者的序号(1到player)
        for (int i = 0; i < player; i++) {
            queue.add(i+1);
        }
        
        //报数的标志
        int flag = 0;
        
        //当参与者还未全部清除
        while (queue.size() > 0){
            
            //轮流报数
            for (int i = 0; i < queue.size(); i++) {
                
                //报数标志++
                flag++;
                
                //如果数到了不幸的数字
                if (flag % interval == 0){
                    
                    //从当前的队伍中移除,并到答案队伍中
                    order.add(queue.remove(i));
                    
                    //重要:由于queue移除了当前元素,
                    //则下一个元素的下标,就是当前元素的下标,
                    //于是需要i--
                    i--;
                }
            }
        }
        
        //最后,返回移除者的先后顺序
        return order;
    }

    /**
     * 运行示例中的约瑟夫环的算法
     * @param args
     */
    public static void main(String[] args) {
        //41个人参与
        int player = 41;
        
        //每数到3,就……
        int interval = 3;
        
        //根据算法,求出移除者的先后顺序
        List<Integer> orderList = calculateJosephCircle(player, interval);
        
        //打印结果,看最后的两个元素,Joseph和朋友应该在第16、31的位置上
        System.out.println("顺序:" + orderList);
    }
    
}

最后,运行算法,控制台输出以下内容(为了方便查看,输出内容做了换行处理):

顺序:[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 
1, 5, 10, 14, 19, 23, 28, 32, 37, 41, 7, 13, 20, 26, 34, 
40, 8, 17, 29, 38, 11, 25, 2, 22, 4, 35, 16, 31]

我们可以看到,上面的顺序,就是不幸者序号的先后顺序,3首先遇到不幸,然后是6、9……直到最后还剩三个,也就是上面的最后三个:35、16、31。此时,Joseph和朋友如果在16和31,则最后一个不幸的就是35号。35号也去了之后,就只剩下16号和31号的Joseph和朋友,然后他们就逃过此难了。由此看来,编程是可以救命的,大家一起来学习编程吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章