1. 程式人生 > >Joseph(約瑟夫環)問題分析

Joseph(約瑟夫環)問題分析

據說著名猶太曆史學家 Josephus有過以下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,直到所有人都自殺身亡為止。然而Josephus 和他的朋友並不想遵從。首先從一個人開始,越過k-2個人(因為第一個人已經被越過),並殺掉第k個人。接著,再越過k-1個人,並殺掉第k個人。這個過程沿著圓圈一直進行,直到最終只剩下一個人留下,這個人就可以繼續活著。問題是,給定了和,一開始要站在什麼地方才能避免被處決?Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲。

上面是對問題的由來描述,下面我們將問題進行分析,分析方法一採用遞迴的方式進行演算法設計。

遞迴演算法設計:

初始的編號 1  2  3  4  5 ......k-2  k-1  k  k+1  k+2  ...... n 

一輪後編號分析:首先是第k個元素要出圈,然後是第k+1個元素再次從1開始記數,具體編號分析如下:

原始編號             進行第二輪時的編號

k+1                    1

k+2                    2

k+3                    3

k+4                    4

 ............

  n                             n-k

  1                             n-k+1

  2                             n-k+2

  ...... ......

  k-1                         n-1

講到這裡,讀者不妨想想如果我們現在只是考慮第二輪的編號中的資料進行分析,這不就是原約瑟夫環的一個子問題而已,關鍵點在於如何將兩個問題進行關聯起來

如果我們能夠找到從第二輪編號到初始編號的關聯關係,問題便會迎刃而解。通過觀察分析,如果將k表示第二輪編號,t表示初始編號,那麼大家快就會發現其關係式為 

t = (k+m)%n,之後我們便只需解決一些遞迴的跳出條件細節問題。

下面附上java版原始碼:

public static int joseph(int n, int m)
{
	if (n == 2) return (m%2) ? 2 : 1;
	int v = (m+josephus0(n-1,m)) % n;
	if (v == 0) v = n;
	return v;
}

非遞迴演算法設計:

遞迴演算法分析簡單,容易程式設計,但是卻會花費較大的代價,執行效率並不高,因此必須考慮下約瑟夫環的非遞迴演算法,非遞迴演算法的設計思想是引入一維陣列表示該問題本體,設定另外陣列表示該位置的是否出列狀態,出列後下階段的計算將不會將其考慮在內,最後輸出唯一的狀態為未出列的位置,具體演算法實現如下:

import java.util.Scanner;
        public class JosephProblem {
	public static void main(String[] args)
	{
		//約瑟夫環非遞迴演算法
		Scanner sc = new Scanner(System.in);
		System.out.print("請輸入要報數的總人數:");
		int n = sc.nextInt();
		int leftnumber = n;
		System.out.print("請輸入隔幾報數:");
		int k = sc.nextInt();
		int countnumber = 0;
		int index = 0;
		int[] array = new int[n];
		for(int i = 0;i <= n-1;i++)
			array[i] = 1;
		while(leftnumber > 1)
		{
			if(array[index] == 1)
			{
				countnumber++;
				if(countnumber == k)
				{
					countnumber = 0;
					array[index] = 0;
					leftnumber--;
				}
			}
			index++;
			if(index == n)
				index = 0;
		}	
		for(int j = 0;j <= n-1;j++)
		{	
			if(array[j] == 1)
				System.out.print(j+1+" ");
		}
	}
}


本人演算法水平有限,如果本文有誤請不吝指出,我們共同進步
約瑟夫環問題是一個較為普遍的演算法問題,許多線上OJ中都會出現這種或那種的變體,例如九度OJ中劍指off專欄就有這樣一道程式設計題,連線如下

相關推薦

Joseph()問題分析

據說著名猶太曆史學家 Josephus有過以下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後

(Joseph)編程內容

Array#include <stdio.h> void main(){ int arr[100];int i=0,interval=0,qty=0,count=0,count1=0; //count數未出局的,count1數出局的printf("請輸入人數和間隔(+1): "

hdu 1443 Joseph

題目 題意:一共有2k個人,分別為k個好人和k個壞人,現在我們需要每隔m個人把壞人挑出來,但是條件是最後一個壞人挑出來前不能有好人被挑出來。。問最小的m是多少 約瑟夫環問題,通常解決這類問題時我們把編號設為從0~n-1。 求出每一輪出列的人:start = (start

Joseph POJ - 1012 遞推

  題意:約瑟夫環  初始前k個人後k個人  問m等於多少的時候 後k個先出去   題解:因為前k個位置是不動的,所以只要考慮每次遞推後的位置在不在前面k個就行   有遞推式 ans[i]=(ans[i-1]+m-1)%(n-i-1)  其中i是輪數  ans[i]

Joseph POJ - 1012 遞推

平移 bsp int std pre style tdi printf end   題意:約瑟夫環 初始前k個人後k個人 問m等於多少的時候 後k個先出去   題解:因為前k個位置是不動的,所以只要考慮每次遞推後的位置在不在前面k個就行   有遞推式 ans[i]=(a

poj 1012 & hdu 1443 Joseph變形)

題目連結: Description The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n

HDOJ 1443 Joseph(打表+數學—)

The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing

hdu 1443 Joseph

題意:約瑟夫環,一共2*k個人,每次報到m出局,前k個是好人,後k個是壞人,求最小的m使得所有的壞人先出局。 解題方案: 模擬,打表,可以維護一個[start,end]區間保護所有的好人,模擬k步。 設p=(m-1)%n,n為當前人數,即p為每次出局的位置(重新排列並對

華為筆試題目--Joseph)修改版

測試空間旗下大頭針出品 這個可是已經執行通過了的。大家可以看看,如果有什麼問題,及時交流。 #include<stdio.h>#include<stdlib.h>typedef struct Node *PNode; struct Node {       int num;     

poj 1012 Joseph求每次出圈人的序號)

有k個好人和k個壞人,前k個是好人後k個是壞人。模擬約瑟夫環,每次數到m的數要被殺死,然後他後面的人從1開始報數。重複這個過程。要求輸出最小的m,使得前k個被殺死的人都是壞人。 因為k比較小,我

HDU Joseph【數學&&

Joseph Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2118 Accepted Submission(

hdu 1443 joseph模擬)

proverb Do not spend all your time on training or studying – this way you will probably become very exhausted and unwilling to compete

用C++實現的問題

content 人在 -h tel padding next family bsp sun 約瑟夫問題是個有名的問題:N個人圍成一圈。從第一個開始報數,第M個將被殺掉,最後剩下一個,其余人都將被殺掉。比如N=6,M=5。被殺掉的人的序號為5,4,6。2。3。最後剩下1

[51nod1073]

成了 out namespace include 結果 div sin cin 比較 解題關鍵:此題不需要模擬,可以用數學方法解決。 無論是用鏈表實現還是用數組實現都有一個共同點:要模擬整個遊戲過程,不僅程序寫起來比較煩,而且時間復雜度高達O(nm),當n,m非常大(例如

51nod 1073 遞歸公式法

tar con for names 問題 print 第一個 描述 span 約瑟夫環問題的原來描述為,設有編號為1,2,……,n的n(n>0)個人圍成一個圈,從第1個人開始報數,報到m時停止報數,報m的人出圈,再從他的下一個人起重新報數,報到m時停止報數,報m的出圈

問題

clas 分享 技術 () trac == 約瑟夫環問題 avi str 約瑟夫環問題:50個人圍成一圈,數到3和3的倍數時出圈,問剩下的人是誰?在原來的位置是多少? 思路例如以下: 1)首先,把數據填充到數組或鏈表中。 2)用一個while循環進行出圈。直到

用循鏈表解決問題

循環 解決 使用 end head als list output 循環條件 約瑟夫環是一個數學的應用問題:已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規

猴子選大王()

ade col fcc closed open ons != head 計算 問題:   設編號為1,2,…,n的n個人圍坐一圈(每個人有一個密碼(正整數)),約定編號為k(1<=k<=n)的人從1開始報數,報到m的那個人出列,將他的密碼作為新的m值,他的下一位

鏈表下的

eno 鏈表 lis main brush return invalid lin java 首先把上面的雙向鏈表改為循環雙向鏈表 public class MyLinkedList2<E> { /** * 第一個 */ t

++ const ostream 題目 tex nod 一個 include null 約瑟夫環 一、心得 二、題目及分析 約瑟夫環 三、代碼及結果 1、 1 #include <iostream> 2 using namespace std; 3