約瑟夫環(huán)問題與遞歸問題(詳解)

今天呢,阿Q給大家?guī)硪粋€小故事,那就是著名的約瑟夫問題。公元66年,約瑟夫不情愿地參與領(lǐng)導(dǎo)了猶太同胞反抗羅馬統(tǒng)治的起義,后來起義失敗,他和一些寧死不降的起義者被困于一個山洞之中。羅馬將軍韋斯巴薌(Vespasian)派人來勸降,他主張投降,其余的人不答應(yīng),并以死相逼。最后,約瑟夫提議,與其死在自己的手上,不如死在彼此的手上。因此他便將游戲規(guī)則告知眾人:N個人圍成一圈,從第一個人開始報數(shù),報到m的人被殺,剩下的人繼續(xù)從1開始報數(shù),報到m的人繼續(xù)被殺;如此往復(fù),直到剩下最后一個人。他就是運(yùn)用這個游戲規(guī)則最終活了下來,被后人稱為約瑟夫環(huán)問題。

接下來呢,就讓我們用java代碼來模擬一下故事的進(jìn)程。

方式一:

public static void getLucklyNum(int num) {
    ArrayList<Integer> list = new ArrayList<>();        //創(chuàng)建集合存儲1到num的對象
    for(int i = 1; i <= num; i++) {
        list.add(i);                                    //將1到num存儲在集合中
    }

    int count = 1;                                      //用來數(shù)數(shù)的,只要是3的倍數(shù)就殺人
    for(int i = 0; list.size() != 1; i++) {             //只要集合中人數(shù)超過1,就要不斷的殺
        if(i == list.size()) {                          //如果i增長到集合最大的索引+1時
            i = 0;                                      //重新歸零
        }

        if(count % 3 == 0) {                            //如果是3的倍數(shù)                   
            System.out.println(list.get(i));
            list.remove(i--);                           //就殺人
        }
        count++;            
    }
}

方式二:

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();        //創(chuàng)建集合存儲1到num的對象
    for(int i = 1; i <= 20; i++) {
        list.add(i);                                    //將1到num存儲在集合中
    }
    cycleCal(list,1);
}


public static void cycleCal(ArrayList<Integer> list,int count) {
    int len = list.size();
    if (len > 1) {
        for (int i = 0; i < len; i++) {
            if (count == 3) {   //用來數(shù)數(shù)的,只要是3的倍數(shù)就殺人
                System.out.println(list.get(i));
                list.remove(i--);
                len = list.size();
                count=0;
            }
            count++;
        }
        cycleCal(list,count); //帶著count是為了形成循環(huán),首尾相連
    }
}

第二種方法是用遞歸解決的,所謂遞歸呢,就是方法里面調(diào)用方法本身的現(xiàn)象。我們在使用遞歸時不需要明確循環(huán)次數(shù),可以很容易的解決一些for循環(huán)和while循環(huán)很難解決的問題。

注意事項:

1)構(gòu)造方法不能遞歸

2)遞歸次數(shù)不能太多,否則就棧內(nèi)存溢出

3)遞歸必須有出口 否則就是死遞歸

接下來就舉幾個例子來了解一下。

案例一:5的階乘

public static void main(String[] args) {
    //使用遞歸求5的階乘
    System.out.println(fun(5)); //次數(shù)不能太多,否則會棧內(nèi)存溢出
}

public static int fun(int num) {
    if(num == 1) {      //if(num == 1)是遞歸出口
        return 1;
    }else {
        return num * fun(num - 1);  //調(diào)用方法本身
    }
}

案例二:文件遍歷

/**
  - 需求:從鍵盤輸入接收一個文件夾路徑,打印出該文件夾下所有的.java文件名
  - 分析:
  - 從鍵盤接收一個文件夾路徑
  - 1,如果錄入的是不存在,給與提示
  - 2,如果錄入的是文件路徑,給與提示
  - 3,如果是文件夾路徑,直接返回
  - 
  - 打印出該文件夾下所有的.java文件名
  - 1,獲取到該文件夾路徑下的所有的文件和文件夾,存儲在File數(shù)組中
  - 2,遍歷數(shù)組,對每一個文件或文件夾做判斷
  - 3,如果是文件,并且后綴是.java的,就打印
  - 4,如果是文件夾,就遞歸調(diào)用
    */
public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);        //創(chuàng)建鍵盤錄入對象
    System.out.println("請輸入一個文件夾路徑");
    File dir = null;
    while(true) {
        String line = sc.nextLine();        //將鍵盤錄入的文件夾路徑存儲
        dir = new File(line);       //封裝成File對象
        if(!dir.exists()) {
            System.out.println("您錄入的文件夾路徑不存在,請重新錄入");
        }else if(dir.isFile()) {
            System.out.println("您錄入的是文件路徑,請重新錄入文件夾路徑");
        }else {
            printJavaFile(dir);
        }
    }   
}

public static void printJavaFile(File dir){
    //獲取到該文件夾路徑下的所有的文件和文件夾,存儲在File數(shù)組中
    File[] subFiles = dir.listFiles();  
    //有的文件夾 windows系統(tǒng)不讓其訪問,所以獲取某個文件夾里面的所有文件和文件夾,有時候獲取成null
    
    //遍歷數(shù)組,對每一個文件或文件夾做判斷
    if(subFiles!=null){ //所以此處需要判斷一下獲取的subFiles數(shù)組是否為null,不判斷的話 “for (File subFile : subFiles)” 會報空指針異常
        for (File subFile : subFiles) {
            //3,如果是文件,并且后綴是.java的,就打印
            if(subFile.isFile() && subFile.getName().endsWith(".java")) {
                System.out.println(subFile);

                //4,如果是文件夾,就遞歸調(diào)用
            }else if (subFile.isDirectory()){
                printJavaFile(subFile);
            }
        }
    }
}

了解了約瑟夫問題,是不是覺得數(shù)學(xué)也能救命了?哈哈。想了解更多學(xué)習(xí)知識,請關(guān)注微信公眾號“阿Q說”,獲取更多學(xué)習(xí)資料吧!你也可以后臺留言說出你的疑惑,阿Q將會在后期的文章中為你解答。每天學(xué)習(xí)一點點,每天進(jìn)步一點點。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天看了一下約瑟夫問題,嗯,感覺自己智商欠費(fèi):( 還是來總結(jié)下好啦~ 問題 約瑟夫是猶太軍隊的一個將軍,在反...
    ying_zhang閱讀 7,094評論 0 2
  • 原創(chuàng) 猴子選大王 一群猴子要選新猴王。新猴王的選擇方法是:讓N只候選猴子圍成一圈,從某位置起順序編號為1~N號。從...
    Cipolee閱讀 1,603評論 1 3
  • 你從我眼前跑開 像脫韁之馬 像越獄之徒 風(fēng)由此而起 掀起一圈灰塵 太陽把樹蔭往左移了一下 正好擋住了我追趕的方向 ...
    指繭閱讀 411評論 1 9
  • 《七絕·憶同窗》(新韻) 溫志齡 粵渝遠(yuǎn)距憶同窗,歲月蹉跎友誼長。 烽火連天生死共,...
    碧野牧歌閱讀 588評論 0 2
  • 親愛的各位教練團(tuán)助教以及講師訓(xùn)的各位戰(zhàn)友同學(xué)們!大家早上好!為什么說早上好,因為希望咱們早點坐上公司董事位置好不好...
    cindyzhao閱讀 1,831評論 0 0

友情鏈接更多精彩內(nèi)容