日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--我们来玩丢手绢(约瑟夫环问题)

發(fā)布時(shí)間:2023/12/4 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--我们来玩丢手绢(约瑟夫环问题) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們來玩丟手絹

  • 昨天我們打撲克,今天我們丟手絹
  • 丟手絹我們都知道這個(gè)游戲,他的由來由約瑟夫 (Josephus)提出來的
據(jù)說著名猶太歷史學(xué)家Josephus有過以下的故事:在羅馬人占領(lǐng)喬塔帕特后,39 個(gè)猶太人與Josephus及他的朋友躲到一個(gè)洞中, 39個(gè)猶太人決定寧愿死也不要被敵人抓到,于是決定了一個(gè)自殺方式,41個(gè)人排成一個(gè)圓圈,由第1個(gè)人開始報(bào)數(shù),每報(bào)數(shù)到第3人 該人就必須自殺,然后再由下一個(gè)重新報(bào)數(shù),直到所有人都自殺身亡為止。然而Josephus 和他的朋友并不想遵從。首先從一個(gè)人開始, 越過k-2個(gè)人(因?yàn)榈谝粋€(gè)人已經(jīng)被越過),并殺掉第k個(gè)人。接著,再越過k-1個(gè)人,并殺掉第k個(gè)人。這個(gè)過程沿著圓圈一直進(jìn)行, 直到最終只剩下一個(gè)人留下,這個(gè)人就可以繼續(xù)活著。問題是,給定了和,一開始要站在什么地方才能避免被處決。 Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個(gè)與第31個(gè)位置,于是逃過了這場死亡游戲。
  • 問題來了,我們就不殺呀殺了,還是丟手絹吧,讓n個(gè)人圍成一個(gè)環(huán),編號(hào)從1 ~n,從1 開始數(shù)當(dāng)數(shù)到第m個(gè)人時(shí)候,他從圈中離開,接著從m+1 個(gè)人數(shù),繼續(xù)m個(gè),在出局,依次復(fù)制這個(gè)過程,求最后一個(gè)出局的人是幾號(hào)?

方案一,環(huán)形鏈表

  • 題目明顯提到了數(shù)字的圓圈,自然我們可以構(gòu)造一個(gè)這樣類似的數(shù)據(jù)結(jié)構(gòu)去模擬這種過程。在常用數(shù)據(jù)結(jié)構(gòu)中,n個(gè)階段的環(huán)形鏈表很好的重現(xiàn)了這種模式
  • 我們先構(gòu)建n個(gè)節(jié)點(diǎn)的鏈表,每次刪除第m個(gè)元素,當(dāng)環(huán)形鏈表中元素只剩下一個(gè)時(shí)候得到解
  • 算法分析:
    • 鏈表頭開始,指定位置position,鏈表尾指定位置before
    • 循環(huán)m次,分別將position與before都前移m-1次,得到指定位置
    • 刪除psition位置節(jié)點(diǎn),讓position指向position.next,統(tǒng)計(jì)個(gè)數(shù)count = n-1
    • 繼續(xù)以上步驟
  • 以上步驟中循環(huán)次數(shù) (n-1)*m次因此時(shí)間復(fù)雜度應(yīng)該是O(nm),當(dāng)m特別大40000 ,時(shí)間復(fù)雜度會(huì)異常的大,非常慢
  • 優(yōu)化:可以通過取余操作,直接計(jì)算出m次循環(huán)在 鏈表中實(shí)際的步驟,將 m % count -1得到實(shí)際步驟,當(dāng)m是大數(shù)時(shí)候,優(yōu)化效果明顯
  • 如上分析有如下代碼:
/*** 約瑟夫環(huán)問題: 將0,1,2,3,4.....n 這n個(gè)數(shù)字排列成一個(gè)圈圈,從數(shù)字0 開始數(shù)m個(gè)數(shù),刪掉他,* 接著從m+1 個(gè)開始數(shù)在來m個(gè)繼續(xù)刪除,一次類推,求出剩下的最后一個(gè)數(shù)據(jù)* @author liaojiamin* @Date:Created in 14:30 2021/7/7*/ public class JosephusForList {public static void main(String[] args) {System.out.println(delNodeForJosephus(40000,997));}/*** 環(huán)形鏈表方法* */public static Integer delNodeForJosephus(Integer n, Integer m){if(n <= 0|| m<=0){return null;}//構(gòu)造環(huán)形隊(duì)列ListNode head = new ListNode(0);ListNode last = null;ListNode position = head;for (Integer i = 1; i < n; i++) {last = new ListNode(i);position.setNext(last);position = position.getNext();}last.setNext(head);ListNode before = position;position = position.getNext();//當(dāng)需要移動(dòng)的位置小于當(dāng)前數(shù)據(jù)個(gè)數(shù),直接移動(dòng)指針Integer count = n;while (m <= count){for (Integer i = 0; i < m-1; i++) {before = position;position = position.getNext();}before.setNext(position.getNext());position = position.getNext();count--;}//當(dāng)m 大于當(dāng)前需要移動(dòng)位置個(gè)數(shù),取余計(jì)算最小移動(dòng)值while (m > count && count > 1){int move = Math.abs(m%count - 1);for (Integer i = 0; i< move;i++){before = position;position = position.getNext();}before.setNext(position.getNext());position = position.getNext();count--;}position.setNext(null);return position.getValue();}}
  • 以上算法時(shí)間復(fù)雜度O(nm), 空間復(fù)雜度O(n)

約瑟夫環(huán)問題

  • 約瑟夫環(huán)問題最終還是一個(gè)數(shù)學(xué)問題(數(shù)學(xué)能救命),我們大概來推導(dǎo)一下

  • 首先定義一個(gè)關(guān)于n 和 m的方法記為f(n, m),表示每次在n個(gè)數(shù)字0~n-1中每次刪除第m個(gè)數(shù)字,最后剩下的數(shù)字

  • 第一個(gè)刪除的數(shù)字用n,m表示:(m-1)% n , 當(dāng)且僅當(dāng) m>0 n>1的時(shí)候,我們記為k =(m-1)% n 如下圖

  • 接著下一次遍歷是從k+1 開始的現(xiàn)有數(shù)字總共 n-2個(gè),k+1排第一,也就有如下圖所示的順序

  • 我們記錄第一個(gè)刪除后的值是 d(n-1, m),與之前的 f(n, m)是同一個(gè)規(guī)則刪除,那么他們最終的結(jié)果肯定是一樣的,那么就有 d(n-1, m) = f(n, m)

  • 也就是我們將k+1當(dāng)成是當(dāng)前的第0 位置,k-1是當(dāng)前最后的位置,也就是 n-2,那么我們依次推導(dǎo)出各個(gè)的位置,k+2是第一個(gè)位置

    • n-2之后有n-1,在加上k個(gè)數(shù) (n-2)-(k+2)+1=n-k-3
    • n-2 之后有k1個(gè)數(shù),(n-2)-(k+1)+1 = n-k+2
    • 同理 0 則是 n-k+1
    • 1 是 n-k
    • k-1則是n-2
    • 用下圖對(duì)應(yīng)關(guān)系展示
  • 如上圖,上部分是數(shù)據(jù)值,下部分是對(duì)應(yīng)的位置,我們可以定義兩個(gè)集合:

    • A集合是上部分?jǐn)?shù)據(jù)值
    • B集合是下部分位置值
    • A,B為非空集, 若存在對(duì)應(yīng)法則f(), 使得對(duì)每個(gè) x∈y 都有唯一確 y∈B與之對(duì)應(yīng), 則稱對(duì)應(yīng)法則f()為A到B的映射
  • 如果我們找到了數(shù)據(jù)值與 位置值的映射關(guān)系函數(shù),是不是可以直接通過計(jì)算得到下一步中需要?jiǎng)h除的數(shù)據(jù)

  • 此處,我們定義映射為p,推導(dǎo)出p(x) = (x-k-1)%n,映射的逆映射就是p(x) = (x+k+1)%n

  • 根據(jù)以上映射規(guī)則,映射之前的寫中最后剩下的數(shù)字 d(n-1, m) = p[f(n-1)+m] = [f(n-1,m) +k +1]%n,將k=(m-1)%n 得到f(n,m) = d(n-1,m)= [f(n-1,m) +k +1]%n

  • 那么我們得到一個(gè)遞推公式

    • n =1 f(n, m) = 0
    • n>1 f(n, m) = [f(n-1, m)+m ]%n
  • 有了如上公式我們很自然直接遞歸就能得到第n次的結(jié)果

  • 如上分析有如下代碼:

/*** 約瑟夫環(huán)問題: 將0,1,2,3,4.....n 這n個(gè)數(shù)字排列成一個(gè)圈圈,從數(shù)字0 開始數(shù)m個(gè)數(shù),刪掉他,* 接著從m+1 個(gè)開始數(shù)在來m個(gè)繼續(xù)刪除,一次類推,求出剩下的最后一個(gè)數(shù)據(jù)* @author liaojiamin* @Date:Created in 14:30 2021/7/7*/ public class JosephusForList {public static void main(String[] args) {System.out.println(fixJosephusForMath(40000, 997));}/*** 數(shù)學(xué)方案:通過計(jì)算得出發(fā)f(n,m)* n=0 f(n,m) = 1* n>1 f(n,m) = [f(n-1,m)+m]%n** */public static Integer fixJosephusForMath(Integer n, Integer m){if(n <= 0|| m<=0){return null;}if(n == 1){return 0;}if(n > 1){return (fixJosephusForMath(n-1, m)+m)%n;}return null;} }
  • 以上遞歸實(shí)現(xiàn)當(dāng)n, m值非常大時(shí)候幾乎不可用,情況通之前文章斐波那契數(shù)量原因一樣

優(yōu)化方案三

  • 動(dòng)態(tài)規(guī)劃解法:在以上推導(dǎo)基礎(chǔ)上用一個(gè)循環(huán)解決
package com.ljm.resource.math.myList;/*** 約瑟夫環(huán)問題: 將0,1,2,3,4.....n 這n個(gè)數(shù)字排列成一個(gè)圈圈,從數(shù)字0 開始數(shù)m個(gè)數(shù),刪掉他,* 接著從m+1 個(gè)開始數(shù)在來m個(gè)繼續(xù)刪除,一次類推,求出剩下的最后一個(gè)數(shù)據(jù)* @author liaojiamin* @Date:Created in 14:30 2021/7/7*/ public class JosephusForList {public static void main(String[] args) {System.out.println(fixJosephusForMath2(40000,997));}/*** 動(dòng)態(tài)規(guī)劃實(shí)現(xiàn)* 數(shù)學(xué)方案:通過計(jì)算得出發(fā)f(n,m)* n=0 f(n,m) = 1* n>1 f(n,m) = [f(n-1,m)+m]%n** */public static Integer fixJosephusForMath2(Integer n, Integer m){if(n <= 0|| m<=0){return null;}int last = 0;for(int i=2;i<=n;i++){last = (last+m)%i;}return last;}}
  • 可以看出,以上實(shí)現(xiàn)思路分析非常復(fù)雜,但是代碼簡單時(shí)間復(fù)雜度O(n),空間復(fù)雜度O(1),遠(yuǎn)優(yōu)于第一,第二解法

上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–判斷撲克牌是否順子
下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–這個(gè)需求很簡單怎么實(shí)現(xiàn)我不管(發(fā)散思維)

總結(jié)

以上是生活随笔為你收集整理的数据结构与算法--我们来玩丢手绢(约瑟夫环问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。