1. 程式人生 > >約瑟夫環問題(josephus problem)詳解

約瑟夫環問題(josephus problem)詳解

約瑟夫環問題描述:
編號為1,2,3…n的人一詞圍成一圈,從第k個人開始報數(從1開始),數到m的人退出。接著下一個人又從1開始報數,數到m的人退出,以此類推。問:剩下的人的編號是多少?

如:n=6,m=3,k=1
原始序列: 1 2 3 4 5 6 ; 從編號1開始報數
第一輪數完後的序列為: 1 2 4 5 ; 3、6出列——從編號1開始報數
第二輪數完後的序列為: 1 2 5 ; 4出列——從編號5開始報數
第三輪數完後的序列為: 1 5 ; 2 出列——從5開始報數
第四輪數完後的序列為: 1 ;5 出列

所以,最後出列的編號為1.

求解方法一:模擬報數過程,時間複雜度O(M*N)

該方法的思想是將所有的編號構造成一個環形連結串列,然後移動到編號為k的節點開始報數,數到M的時候移除連結串列節點,接著從下一個節點開始報數,移除數到M的節點。以此類推,直到只剩下一個節點。

public static int josephusOMN(int n, int m) {
        return josephusOMN(n, m, 1);
    }
 
    public static int josephusOMN(int n, int m, int k) {
        if (n <= 0 || m <= 0 || k <= 0)
            return -1;
        // consruct josephus circle
        JosephusNode header = new JosephusNode(1);
        JosephusNode nodes = header;
        for (int i = 2; i <= n; i++) {
            nodes.mNext = new JosephusNode(i);
            nodes = nodes.mNext;
        }
        nodes.mNext = header;
        // end
        for (int i = 1; i < k; i++) {
            nodes = nodes.mNext; // move to start off person
        }
        while (nodes != nodes.mNext) {
            for (int i = 1; i < m; i++) {
                nodes = nodes.mNext;
            }
            // System.out.print(nodes.mNext.mNum + " , ");
            nodes.mNext = nodes.mNext.mNext;
        }
        return nodes.mNum;
    }
 
    private static class JosephusNode {
        public int mNum;
        public JosephusNode mNext;
 
        public JosephusNode(int num) {
            this(num, null);
        }
 
        public JosephusNode(int num, JosephusNode node) {
            mNum = num;
            mNext = node;
        }
    }

求解方法二:方便求解最後的勝利者同時也適合列印 出列 序列的,時間複雜度O(N).

下面方法借鑑自Java程式練習-約瑟夫環問題

public static int josepusONWithList(int n, int m) {
        return josepusONWithList(n, m, 1);
    }
 
    public static int josepusONWithList(int n, int m, int k) {
        if (n <= 0 || m <= 0 || k <= 0)
            return -1;
        LinkedList<Integer> list = new LinkedList<Integer>();
        for (int i = 1; i <= n; i++)
            list.add(i);
        int outPos;
        while (list.size() > 1) {
            outPos = (int) (k + m - 2) % list.size();
            // System.out.print(list.get(outPos)+" , ");
            list.remove(outPos);
            k = outPos + 1;
        }
        // System.out.println(list.get(0));
        return list.get(0);
    }

求解方法三:對模擬方法的改進——數學優化方法,時間複雜度O(N)。具體思想參見約瑟夫環的數學優化方法

public static int josephusON(int n, int m) {
        return josephusON(n, m, 1);
    }
 
    /**
     * Josephus 環的一個O(N)演算法
     * 
     * @param n 總人數
     * @param m 數到m的人出列
     * @param k 開始報數人的編號
     * @return 最後一個出列的編號
     */
    public static int josephusON(int n, int m, int k) {
        int p = 0;
        for (int i = 2; i <= n; i++) {
            // System.out.print((p + k == n ? n : (p + k) % n)+"  ");
            p = (p + m) % i;
        }
        return p + k == n ? n : (p + k) % n; // 返回最後一人的位置
    }

在有時候需要知道出列的序列,上述System.out部分就是出列序列。

約瑟夫環問題求解就先寫到這裡,朋友們若有其他方法也別忘了告訴樓主哦_… 收集到其他求解方法後再補充。