算法:用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和朋友,然後他們就逃過此難了。由此看來,編程是可以救命的,大家一起來學習編程吧

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