约瑟夫(环)问题(Josephus problem)
问题描述:皇帝决定找出全国中最幸运的一个人,于是从全国选拔出 n 个很幸运的人,让这 n 个人围着圆桌进餐,可是怎么选择出其中最幸运的一个人呢?皇帝决定:从其中一个人从 1 开始报数,按顺序数到第 k 个数的人自动出局,然后下一个人从 1 开始报数,数到 k 的人出局……。如此直到最后只剩下约瑟夫一人,然后他就成为全国最幸运的人。请问约瑟夫最初的位置?(注:原问题略显暴力,故自创此趣味题目)
分析:把第一个开始报 1 的人标定为 1,然后按报数顺序依次标定其余的人为:2,3,……,n - 1,n。按规则进行淘汰,直到最后剩一个数字,这个数字就是约瑟夫的位置。
解决方案:
1. 模拟法(simulation)
数组模拟,时间复杂度最高为 O(n3) (当k≈n时 ) ,空间复杂度O(n)
/********** 用数组模拟 *************/
void findNext(bool *out, int n, int &curPosition){
if(!out[curPosition]) {
int pNext = (curPosition + ) % n;
if(!out[pNext])
curPosition = pNext;
else{
curPosition = pNext;
while(out[curPosition])
curPosition = (curPosition + ) % n;
}
}else
{
while(out[curPosition])
curPosition = (curPosition + ) % n;
}
}
int josephus(int n, int k)
{
if(n < || k < ) return -;
if(n == ) return n;
bool *out = new bool[n]; /********* 记录是否出局 *********/
for(int i = ; i < n; ++i)
out[i] = false;
int current = ;
int n2 = n;
while(n2 != )
{
int cnt = k;
while(--cnt)
findNext(out, n, current);
out[current] = true;
findNext(out, n, current);
--n2;
}
delete[] out;
out = NULL;
return (current + );
}
Code
循环链表模拟:时间复杂度O(n),空间复杂度O(n)
/********** 循环链表 *************/
struct ListNode{
int val;
ListNode * next;
ListNode(int x):val(x), next(NULL) {}
}; int josephus(int n, int k)
{
if(n < 1 || k < 1)
return -1;
if(n == 1) return n;
ListNode *head = new ListNode(1);
ListNode *prior = head;
for(int i = 2; i <= n; ++i)
{
ListNode *tem= new ListNode(i);
prior->next = tem;
prior = prior->next;
}
prior->next = head;
while(head->next != head)
{
int cnt = k;
while(--cnt)
{
head = head->next;
prior = prior->next;
}
prior->next = prior->next->next;
ListNode *current = head;
head = head->next;
delete current; /*** 只释放堆内存空间,局部指针自动回收 ***/
}
return head->val;
}
2.建模法(modeling)
使用队列建模。
/********** 用队列(注:使用STL可简单化) *************/
bool ERROR = false;
typedef int ELEM;
struct Node{
ELEM val;
Node *next;
Node(ELEM e):val(e), next(NULL){}
};
struct queue{
queue():front(NULL), tail(NULL) {}
ELEM pop();
void push(ELEM val);
bool empty();
private:
Node *front;
Node *tail; };
ELEM queue::pop(){
if(front == NULL){
ERROR = true;
return -;
}else{
ELEM v = front->val;
front = front->next;
return v;
}
}
void queue::push(ELEM val){
Node *p = new Node(val);
if(front == NULL)
front = tail = p;
else
{
tail->next = p;
tail = tail->next;
}
}
bool queue::empty(){
if(front == NULL)
return true;
else
return false;
} int josephus(int n, int k)
{
if(n < || k < ) return -;
if(n == ) return ;
queue qu;
for(int i = ; i <= n; ++i)
qu.push(i);
int result = ;
while(!qu.empty())
{
for(int i = ; i <= k-; ++i)
qu.push(qu.pop());
result = qu.pop();
}
return result;
}
Code
3. 数学推理 && 动态规划
初始:0 1 ... (k-2) (k-1) k ... (N-1)
K 出局: 新的顺序:
k 0
... p ...
N-1 映 N - k - 1 P(x) = (x - k + N) mod N
0 射 N - k 令:y = P(x) = (x - k + N) mod N
1 N - k + 1 则,x = (y + k - N) mod N
... ... = (y + k) mod N
k-2 N - 2 P-1(x) = (x + k) mod N
设 f(N,k) 为最后所得的数字,则:
f(N,k) = P-1( f(N-1,k) ) = (f(N-1,k) + k) mod N
所以有如下递推公式:
int josephus(int n, int k)
{
if(n < 1 || k < 1) return -1;
if(n == 1) return 1;
int result = 0;
for(int i = 2; i <= n; ++i)
result = (result + k) % i;
return result+1;
}
另外,简洁的递归:(不推荐,递归栈太小,容易溢出)
int josephus(int n, int k)
{
if(n < 1 || k < 1) return -1;
if(n == 1) return 1;
else
return ((josephus(n - 1, k) + k - 1) % n + 1);
}
最后,当 k = 2 时,如下公式可直接求出:
代码为:
int n = 1000;
cout<< 2*(n - pow(2.0, int(log((float)n) / log((float)2))))+1 <<endl;
约瑟夫(环)问题(Josephus problem)的更多相关文章
- 约瑟夫环问题(Josephus)
约瑟夫环:用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至最后一个元素并输出该元素的值. 一.循环链表:建立一个有N个元素的循环链表,然后从链表头开始遍历并记数,如果计数值为M,则 ...
- 组合数学--约瑟夫环问题 Josephus
约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环. 有n个囚犯站成一个圆圈,准备处决.首先从一个人开始,越过k-2个人(因为第 ...
- 约瑟夫问题(Josephus Problem)的两种快速递归算法
博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/ 对,我是来骗访问量的!O(∩_∩)O~~ 约瑟夫问题(Josephus Problem)也称 ...
- LightOJ - 1179 Josephus Problem(约瑟夫环)
题目链接:https://vjudge.net/contest/28079#problem/G 题目大意:约瑟夫环问题,给你n和k(分别代表总人数和每次要数到k),求最后一个人的位置. 解题思路:因为 ...
- 谁能笑到最后,约瑟夫环-Josephus问题求解
一. 简述Josephus问题 N个人站成一环,从1号开始,用刀将环中后面一个人“消灭“”掉,之后再将刀递给下一个人,这样依次处理,最后留下一个幸存者. 二. 求解方法 1. 约瑟夫问题如果使用 ...
- 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)
/************************************************************************* * * Josephus problem * * ...
- Josephus环的四种解法(约瑟夫环)
约瑟夫环 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个 ...
- Josephus problem(约瑟夫问题,丢手绢问题)
约瑟夫问题 约瑟夫环问题是一个数学应用题:已知n个人(以编号1,2,3.....,n)围坐在一张圆桌的周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列 ...
- 单向环形链表解决约瑟夫环(Josephus)问题
一.约瑟夫环问题 Josephu 问题为:设编号为1,2,- n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那 ...
- Roman Roulette(约瑟夫环模拟)
Roman Roulette Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)To ...
随机推荐
- hadoop运行原理之Job运行(三) TaskTracker的启动及初始化
与JobTracker一样,TaskTracker也有main()方法,然后以线程的方式启动(继承了Runnable接口).main()方法中主要包含两步:一是创建一个TaskTracker对象:二是 ...
- hdu 2095
ps:真是日了狗...英语渣渣理解题目不行,开了个100W数组来算,还优化了下时间,还是超时了,看了题解才知道用异或. N个数异或,会得出其中是奇数的一个.比如 1^1^3^2^2 = 3. 1^ ...
- Android 学习第3课,小例子
package temperature.convert; import java.util.Scanner; public class Converter { public static void m ...
- Android 学习第2课,下载 eclipse 工具
可以到http://www.ddooo.com/softdown/61745.htm 下载下来是32位与64位都有的 而且是汉化的,经测试成功,还可以,不错!
- js数组常用方法汇总
判断某个对象是否是数组: instanceof.Array.isArray() 对于一个网页或者一个全局作用域可以使用instanceof操作符. if(value instanceof Array) ...
- python数据结构与算法——快速排序
快速排序通过不断将数列分段,使得较小的数在左边的序列,较大的数在右边的序列,不断重复此过程实现排序效果.通过设置两个哨兵不断的找两个序列的较小数,较大数,并把左右的数据互换,实现对数据从粗到细的排序. ...
- android 镜像源
Android SDK在线更新镜像服务器 中国科学院开源协会镜像站地址: IPV4/IPV6: mirrors.opencas.cn 端口:80 IPV4/IPV6: mirrors.opencas. ...
- jquery 获取浏览器可视窗口大小,滚动条高度
alert($(window).height()); //浏览器时下窗口可视区域高度 alert($(document).height()); //浏览器时下窗口文档的高度 alert($(docum ...
- find 命令的参数详解
使用name选项 文件名选项是find命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用. 可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来.不管当前路径是什么,如果想要在自 ...
- cocos2d 保存最近登陆多个账号最多一个月
用的是一个单例来管理 ,数据是存在本地的xml文件里的格式如下 <?xml version="1.0" encoding = "utf-8" ?> ...