約瑟夫環問題

問題描述:有n個人,編號分別從0到n-1排列,這n個人圍成一圈,現在從編號爲0的人開始報數,當報到數字m的人,離開圈子,然後接着下一個人從0開始報數,依次類推,問最後只剩下一個人時,編號是多少?

分析:這就是著名的約瑟夫環問題,關於來歷不再說明,這裏直接分析解法。

        解法一:蠻力法。我曾將在大一學c語言的時候,用蠻力法實現過,就是採用標記變量的方法即可。

        解法一:循環鏈表法。從問題的本質入手,既然是圍成一個圈,並且要刪除節點,顯然符合循環鏈表的數據結構,因此可以採用循環鏈表實現。

        解法三:遞推法。這是一種創新的解法,採用數學建模的方法去做。具體如下:

                   首先定義一個關於n和m的方程f(n,m),表示每次在n個編號0,1,...,n-1中每次刪除的報數爲m後剩下的數字,

                   在這n個數字中,第一個被刪除的數字是(m-1)%n,爲了簡單,把(m-1)%n記作k,那麼刪除k之後剩下的數字爲0,1,2,...,k-1,k+1,...,n-1

                   並且下一次刪除的數字從k+1開始計數,這就相當於剩下的序列中k+1排在最前面,進而形成k+1,..,n-1,0,1,2,...,k-1這樣的序列,這個序列最後剩下的數                      字應該和原序列相同,由於我們改變了次序,不能簡單的記作f(n-1,m),我們可以記作g(n-1,m),那麼就會有f(n,m)=g(n-1,m).

                  下一步,我們把這n-2個數字的序列k+1,..,n-1,0,1,2,...,k-1做一個映射,映射的結果是形成一個從0到n-2的序列。

                           k+1對0,k+2對1,......,n-1對n-k-2,0對n-k-1,1對n-k,....,k-1對n-2

                 這樣我們可以把這個映射定義爲p,則p(x)=(x-k-1)%n,它表示如果映射前的數字是x,映射後爲(x-k-1)%n,從而這個映射的反映射問爲p-1(x)=(x+k+1)%n

                 由於映射之後的序列和原始序列具有相同的形式,都是從0開始的序列,所以可以用函數f來表示,即爲f(n-1,m),根據映射規則有:

                 g(n-1,m)=p-1[f(n-n,m)]=[f(n-1,m)+k+1]%n,最後把之前的k=(m-1)%n帶入式子就會有f(n,m)=g(n-1,m)=[f(n-1,m)+m]%n.

                這樣我們就可以得出一個遞推公式,

                                                            當n=1時,f(n,m)=0;

                                                            當n>1時,f(n,m)=[f(n-1,m)+m]%n;

                有了這個公式,問題就變得多了。

      由於解法一和解法二,都很好理解,我就不再寫具體的代碼了,這裏我給出解法三Java代碼,用遞歸實現:

import java.util.*;
public class Main {
    public static int ysfh(int n,int m){
    	if(n<=1)return 0;
    	return (ysfh(n-n,m)+m)%n;
    }
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
        Scanner scan=new Scanner(System.in);
        int n=scan.nextInt();
        int m=scan.nextInt();
        System.out.println("最後剩下的編號爲:"+ysfh(n,m));
	}

}

測試樣例輸出爲:

10   3
最後剩下的編號爲:3

 

可以看出來,這麼複雜的一個問題遞歸只需要兩行代碼即可實現,因此在今後的編程的過程中,要靈活運用數學知識。


發佈了70 篇原創文章 · 獲贊 17 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章