約瑟夫環問題

2021-08-24 20:58:38 字數 3861 閱讀 2146

按照如下規則去殺人:

所有人圍成一圈

順時針報數,每次報到q的人將被殺掉

被殺掉的人將從房間內被移走

然後從被殺掉的下乙個人重新報數,繼續報q,再清除,直到剩餘一人

你要做的是:當你在這一群人之間時,你必須選擇乙個位置以使得你變成那剩餘的最後一人,也就是活下來。

1.思路:注意這裡的前提是n = 2^k(也就是2的冪次個人,其他數另外討論)

如果只有2個人,顯然剩餘的為1號

如果有4個人,第一輪除掉2,4,剩下1,3,3死,留下1

如果是8個人,先除去2,4,6,8,之後3,7,剩下1,5,除去5,又剩下1了

定義j(n)為n個人構成的約瑟夫環最後結果,則有j(2^k) = 1

j(n) = 2^k

– 2^k-1= 2

^k-1n=2

^kj(n) =

2^k-1– 2

^k-2= 2

^k-2n=2

^k-1

………j(2^2

) = 2^2

– 2^1= 2^1

n=2^2

遞推得到如上結果,起始我們仔細分析也就是每次除去一半的元素,然後剩餘的一半繼續重複之前的策略,再除去一半。(可想到遞迴)

結合:j(2) = 1 我知道兩個數,從1開始,肯定是2先死,剩下1.

得到:j(2^k) = 1

2.but當n 不等於 2^k時候,比如9,11這種:

n 不等於 2^k時,就不存在這樣的easy的規律了,重新分析:

假設n = 9,這時候如圖下:

能看出來,我們乾掉第乙個人也就是2,之後就只剩下8個人了,又回到j(2^k)上了,這時候我們需要的是找到當前的1號元素。

見圖下:

這時候,我們從3號開始,就成了另外乙個規模小1的約瑟夫問題(恰好為2^k的特例)。

這時候,我們可以把3號看成新的約瑟夫問題中的1號位置:

j(8) = j(2^3) = 1,也就是說這裡的1代表的就是上乙個問題中的3號

so:j(9) = 3

答案為3號

同理可知所有的非2^k的數都是這樣:

假設n = 2^k + t,t可以隨意取,比如1,2,3…….

假設n = 11,這時候n = 2^3 + 3,也就是說t = 3,所以開始剔除元素直到其成為2^k問題的約瑟夫問題。

so,我們在剔除了t(3)個元素之後(分別是2,4,6),此時我們定格在2t+1(7)處,並且將2t+1(7)作為新的一號,而且這時候的約瑟夫環只剩下2^3,也就是j(2^3 + 3) = 2*3 + 1 = 7,答案為7

總結一下這個規律:

j(2^k + t) = 2t+1

3.說完了特例,現在說說q 不等於2的情況下:

當q ≠ 2:

我們假定:

- n — n人構成的約瑟夫環

- q — 每次移除第q個人

約定:

- jq(n)表示n人構成的約瑟夫環,每次移除第q個人的解

- n個人的編號從0開始至n-1

我們沿用之前特例的思想:能不能由jq(n+1)的問題縮小成為j(n)的問題(這裡的n是n+1規模的約瑟夫問題消除乙個元素之後的答案),jq(n)是在jq(n+1)基礎上移除乙個人之後的解。也就是說,我們能由jq(n)得到jq(n+1)。

規律:jq(n+1) = ( jq(n) + q ) / (n+1)

詳細推導過程見這篇博文

大致是如下這樣:

0 1 2 3 4 5 …… n-1 總共n人

設第q個人也就是下標為q-1的那位,殺死:

剩下n-1個人,如下:

q q+1 q+2 …… n-2 n-1 0 1 2 …… q-2 (這裡是除去q-1這位兄台的剩餘n-1人)

這時,又來重複我們的老套路:將新的被殺的後乙個人作為新的0號,於是新的如下:

0 1 2 …… ………. …….. n-2

1 2

3 4

5 6

7 8

9 其實就是從q開始,到之前最大數n-1,每個數都減去q,從0開始之後接著n-1這個新的值每次往後加1,直到加到n-1(這個下標)

舉個例子:

j4(9) :

0 1 2 3 4 5 6 7 8 消去3–> 0 1 2 4 5 6 7 8( 0 1 2)

對應的新值: 0 1 2 3 4 5 6 7

其中:q=4,從3之後第乙個數4開始:每個數5-q=1,6-q=2,7-q=3,8-q=4,因為是個環,0-q=-4,1-q=-3 ….直到加到n-1=7

這就相當於乙個限定範圍內的數的相對位置,-1代表的是最後乙個元素,也就是之前的8

(-2就代表7,-3代表6,-4代表5…..-9代表0,從後面開始數過來第9位)

1 2

3 4

5 6

7 8

9 大致過程如圖下:

這裡寫描述

那麼我們知道是這麼得到的新的佇列,那麼也很容易知道怎麼反推了:

反觀如上的變化情況,都是減去乙個q,所以:

變回去的公式如下:old = (new + q) % n

這裡的old和new指的是下標,n指的是總共有多少人

知道了怎麼推出之前的下標,那麼也就可以一步步遞推回去得到開始的佇列或者從小推到大得到最後剩餘的結果。

最後再做一道實際點的例子,求j2(4):

j2(1) = 0

j2(2) = (j2(1) + 2) % 2 = 0

j2(3) = (j2(2) + 2) % 3 = 2

j2(4) = (j2(3) + 2) % 4 = 0

……..

這樣一步步求就能得到所有的給出n和q條件的答案了。

#include

#include

using

namespace

std;

int yuesefu(int n,int m)

else

}int main(void)

cout

<

0;}

迴圈鍊錶模擬**

#include

#include

#include

#include

#include

#include

#include

using namespace std;

typedef struct node

node;

node* creatnode(int

x)//建立鍊錶節點的函式

node* creatjoseph(int n)

q->next=head;

return head;

}void runjoseph(int n,int

m)//模擬執行約瑟夫環,每數到乙個數,將它從環形鍊錶中摘除,並列印出來

q=p->next;

p->next=q->next;

p=p->next;

//printf("%d--",q->number);

free(q);

}printf("%d\n",p->number);

}int main()

約瑟夫問題 約瑟夫環

約瑟夫 問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死...

約瑟夫問題 約瑟夫環

約瑟夫問題 有時也稱為約瑟夫斯置換,是乙個出現在電腦科學和數學中的問題。在計算機程式設計的演算法中,類似問題又稱為約瑟夫環。又稱 丟手絹問題 據說著名猶太歷史學家 josephus有過以下的故事 在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也...

約瑟夫環問題

約瑟夫環問題 問題描述 編號是1,2,n的n個人按照順時針方向圍坐一圈,每個人持有乙個密碼 正整數 一開始任選乙個正整數作為報數上限值m,從第乙個人開始順時針方向自1開始順序報數,報到m時停止報數。報m的人出列,將他的密碼作為新的m值,從他在順時針方向的下乙個人開始重新從1報數,如此下去,直到所有人...