Algorithm Gossip: 約瑟夫問題(Josephus Problem)
2013-08-02 21:35
330 查看
說明
據說著名猶太歷史/數學家約瑟夫(Josephus)有過以下的故事:在羅馬人佔領喬塔帕特後,40個猶太士兵與約瑟夫躲到一個洞中,眼見脫逃無望,一群人決定集體自殺,然而私下約瑟夫與某個傢伙並不贊成,於是約瑟夫建議自殺方式,41個人排成圓圈,由第1個人開始報數,每報數到3的人就必須自殺,然後由下一個重新報數,直到所有人都自殺身亡為止。約瑟夫與不想自殺的那個人分別排在第16個與第31個位置,於是逃過了這場死亡遊戲。
解法
只要畫兩個圓圈就可以讓約瑟夫與不想死的傢伙免於死亡遊戲,這兩個圓圈內圈是排列順序,而外圈是自殺順序,如下圖所示:使用程式來求解的話,只要將陣列當作環狀來處理就可以了,在陣列中由計數1開始,每找到三個無資料區就填入一個計數,直而計數達41為止,然後將陣列由索引1開始列出,就可以得知每個位置的自殺順序,這就是約瑟夫排列,41個人而報數3的約琴夫排列如下所示:
14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23
由上可知,最後一個自殺的是在第31個位置,而倒數第二自殺的要排在第16個位置。
如果從另一角度來看,每次有人自殺後,剩下的人重新編號,那問題就可以分解為數個子問題。
如果使用動態規畫,有個遞迴推導公式可在 Josephus problem 找到:
g(1, k) = 0
g(n, k) = (g(n - 1, k) + k) % n
其 中k表示報數k的人就自殺,g(n, k)就是n人組成的圓最後存活者編號,編號是從0開始,由於1人時該人就是存活者,所以g(1, k)為0。簡單來說,如果可以求得n - 1的圓時最後存活者編號x,就可以用來求得n的圓時最後存活者編號(x + k) % n(如果要得到1開始的編號,就對結果加一即可)。
如果使用鏈結串列,問題會更簡單,A串列中是1到n的編號,逐一移出A串列中的編號至B串列,最後A串列為空,B串列就是自殺編號順序,1到n編號所在的索引加一就是約瑟夫排列。
#include <stdio.h> #include <stdlib.h> #define LEN 41 #define STEP 3 void josephus(int*, int, int); int next(int*, int, int, int); int winner(int, int); int main(void) { int man[LEN] = {0}; josephus(man, LEN, STEP); printf("約琴夫排列:"); int i; for(i = 0; i < LEN; i++) { printf("%d ", man[i]); } printf("\nWinner: %d", winner(LEN, STEP)); return 0; } void josephus(int* man, int len, int k) { int i, n; for(i = 1, n = -1; i <= len; i++) { n = next(man, len, k, n); man = i; } } int next(int* man, int len, int k, int n) { int count = 0; while(count < k) { n = (n + 1) % len; if(man == 0) { count++; } } return n; } int winner(int len, int k) { int g, n; for(g = 0, n = 1; n <= len; n++) { g = (g + k) % n; } return g + 1; }
#include<stdio.h> #include <stdlib.h> typedef struct node//节点存放一个数据和指向下一个节点的指针 { int data; struct node* pnext; } Node; Node *link_create(int n)//创建n个节点的循环链表 { //先创建第1个节点 Node *p,*q,*head; int i ; p = (Node *)malloc(sizeof(Node)); head = p; p->data = 1; for(i = 2;i<=n;i++)//再创建剩余节点 { q = (Node *)malloc(sizeof(Node)); q->data = i; p->pnext = q; p = q; } p->pnext = head;//最后一个节点指向头部,形成循环链表 return head; } void link_process(Node *head,int k,int m)//从编号为k(1<=k<=n)的人开始报数,数到m的那个人出列; { int i; Node *p = head,*tmp1; while(p->data != k)//p指向的节点data为k p = p->pnext; while(p->pnext != p) { for(i=1;i<m;i++) { tmp1 = p; p = p->pnext; } printf("%d ",p->data); tmp1->pnext= p->pnext; free(p); p = tmp1->pnext; } printf("%d ",p->data); free(p); } int main() { Node *head = link_create(15); link_process(head,1,2); system("pause"); return 0; }
相关文章推荐
- Algorithm Gossip: 约瑟夫问题(Josephus Problem)
- Geeks Ford-Fulkerson Algorithm for Maximum Flow Problem 最大网络流问题
- Algorithm Gossip: 超長整數運算(大數運算)
- 约瑟夫问题(Josephus problem)的一点思考
- Josephus Problem
- Algorithm Gossip: 河内塔
- lightoj 1179 - Josephus Problem 约瑟夫环
- Algorithm Gossip (9) 八枚银币
- Algorithm Gossip (24) 洗扑克牌(乱数排列)
- Objective-C实现Algorithm Gossip: 费式数列代码
- 递归算法计算八皇后问题(Eight Queen Problem with Recursive Algorithm)
- 利用Matlab以蚁群算法(Ant Colony Algorithm)求解不闭合的旅行商问题(Travelling Salesman Problem, TSP)并进行可视化
- 湘潭市赛 Josephus Problem 线段树
- josephus Problem 初级(使用数组)
- Josephus problem
- Algorithm Gossip: 费式数列
- Algorithm Gossip(10) 细胞生命游戏
- 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)
- (转)Maximum subarray problem--Kadane’s Algorithm
- josephus problem