约瑟夫问题(Josephus Problem)的两种快速递归算法
- 博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/
- 对,我是来骗访问量的!O(∩_∩)O~~
约瑟夫问题(Josephus Problem)也称“丢手绢问题”,是一道非常经典的算法问题,其解法涉及了链表、递归等算法和数据结构,本文主要分为如下三个内容:
- 使用C语言定义循环链表,通过遍历链表模拟事件处理过程;
- 使用数学方法,找出第
n - 1
步与第n
步的关系,通过递归解决问题;- 对第二种方法进行优化,加速递归过程,提高算法效率
循环链表(C语言)
代码
#include <stdio.h>
#include <stdlib.h>
//定义循环链表
typedef struct node//定义node结构体
{
int data;
struct node* next;
}cLinkList;//typedef struct node* cLinkList;定义一个struct node类型的循环链表
//主函数
int main()
{
cLinkList *head, *p, *s, *temp;
int n, k;
int i = 1;
printf("Please enter the total number n:\n");
scanf("%d", &n);
printf("Please enter the key value:\n");
scanf("%d", &k);
k %= n;
head = (cLinkList *)malloc(sizeof(cLinkList));
p = head;
p->next = p;//这里要赋值为p,不能赋值为head,要保持head的位置不变
p->data = i;
for(i = 2; i <= n; i++)
{
s = (cLinkList *)malloc(sizeof(cLinkList));
s->data = i;
s->next = p->next;
p->next = s;
p = s;
}
p = head;
int total = n;
while(n--)
{
for(i = 1; i < k - 1; i++)
{
p = p->next;
}
printf("%d->", p->next->data);
temp = p->next;//temp为要删除的元素
p->next = temp->next;//链表中跳过temp
free(temp);//释放temp
p = p->next;//p向前移动继续寻找
}
printf("Done!\n");
return 0;
}
运行过程如下:
程序分析
这段代码主要使用了循环链表的数据特性和结构特性,非常适合用来进行Josephus问题的模拟,但是相对来说处理问题的复杂度较高,下面将介绍两种更加高效的算法。
第一种递归
原理
令f[n]表示当有n个候选人时,最后当选者的编号。则:
f[1] = 0
f[n] = (f[n - 1] + K) mod n
方法证明
上述公式可以用数据归纳法简单证明其正确性:
f[1] = 0
当只有一个候选人的时候,显然结果应该是0
f[n] = (f[n - 1] + K) mod n
f[n - 1]
为第n - 1
次数到的id序列,则第n
次就是再往下数k
个,最后进行取模运算即可得到结果序列
这种算法的时间复杂度为O(N),空间复杂度为O(1),效率有所提高!
代码
#include <iostream>
using namespace std;
int main()
{
int num, n, k;
cin >> num;
while(num--)
{
int ret = 0;
cin >> n >> k;
for(int i = 2; i <= n; ++i)
{
ret = (ret + k) % i;//ret记录每一次数到的序列号
}
cout << ret << endl;//输出最终序列结果
}
return 0;
}
第二种递归
原理
- 在每一轮报数过程中,都有
N/K
个人退出了队伍,比如N = 10, K = 3
,第一轮有N / K = 3
三个人退出;- 上述第一种方法每次递归的步长为
1
,这里我们利用上述关系,建立一个步长为N / K
的递归过程;- 需要注意的是,当
N
减少到N = K
的时候就需要使用第一种递归进行计算;N > K
时的递归公式为:
ret < N mod K: ret = ret - (N mod K) + N
ret >= N mod K: ret = ret - (N mod K) + (ret - N mod K) / (K - 1)
代码
#include <iostream>
using namespace std;
int josephus(int n, int k)
{
int ret;
if(n == 1)
return 0;
//n < k的时候使用第一种递归算法
if(n < k)
{
int ret = 0;
for(int i = 2; i <= n; ++i)
ret = (ret + k) % i;
return ret;
}
//执行递归过程
ret = josephus(n-n/k,k);
if(ret < n % k)
{
ret = ret - n % k + n;
}
else
{
ret = ret - n % k + (ret - n % k ) / (k - 1);
}
return ret;
}
int main()
{
int num;
cin >> num;
while(num--)
{
int n, k;
cin >> n >> k;
cout << josephus(n, k) << endl;
}
return 0;
}
代码分析
这个算法加快了递归算法的迭代速度,当所求N
比较大K
比较小的时候比较适用,能够以更快的速度进行求解。
Github: https://github.com/haoyuanliu
个人博客: http://haoyuanliu.github.io/
个人站点,欢迎访问,欢迎评论!
约瑟夫问题(Josephus Problem)的两种快速递归算法的更多相关文章
- 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)
/************************************************************************* * * Josephus problem * * ...
- 文件批量上传-统一附件管理器-在线预览文件(有互联网和没有两种)--SNF快速开发平台3.0
实际上在SNF里使用附件管理是非常简单的事情,一句代码就可以搞定.但我也要在这里记录一下统一附件管理器能满足的需求. 通用的附件管理,不要重复开发,调用尽量简洁. 批量文件上传,并对每个文件大小限制, ...
- Android 利用an框架快速实现夜间模式的两种套路
作者:Bgwan链接:https://zhuanlan.zhihu.com/p/22520818来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 网上看到过大多实现夜间模 ...
- 使用Idea当中的快捷键快速查看继承关系或其图表的两种方法
一.Idea当中有两种方法可以查看继承关系 在Idea当中选中一个类,然后按Ctrl+H,可以快速查看当前所选类的继承关系,如下图: 同样选中一个类,按CTRL+ALT+U,即可生成当前类的继承关 ...
- iOS同一项目多个Target的快速实现方法 - 两种使用场景详解
我们项目中,默认建好是只有一个target的,但是,一些场景中,多target能帮助我们更好的使用项目. 场景1: 同一项目,一般会分不同环境:开发环境.测试环境.正式(生产)环境. 这就涉及到一个请 ...
- 消息队列:快速上手ActiveMQ消息队列的JMS方式使用(两种模式:Topic和Queue的消息推送和订阅)
1.实现功能 希望使用一套API,实现两种模式下的消息发送和接收功能,方便业务程序调用 1.发送Topic 2.发送Queue 3.接收Topic 4.接收Queue 2.接口设计 根据功能设计公共调 ...
- 快速掌握RabbitMQ(四)——两种消费模式和QOS的C#实现
本篇介绍一下RabbitMQ中的消费模式,在前边的所有栗子中我们采用的消费者都是EventingBasicConsumer,其实RabbitMQ中还有其他两种消费模式:BasicGet和QueueBa ...
- 排产的两种方式(前推式与后拉式)在Optaplanner上的体现
生产计划的约束 在制定生产计划过程中,必然是存在某些制约因素,满足某些需求才能进行的,或是交期保证.或是产能限制.或是关键工序制约.即TOC理论 - 任何系统至少存在着一个制约因素/瓶颈:否则它就可能 ...
- Josephus Problem的详细算法及其Python、Java实现
笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争--马萨达战争,深为震撼,有兴趣的同学可以移步:http://finance.ifeng.com/a/20170627/15491157_0. ...
随机推荐
- String 方法
import java.lang.*; /** * 1.查找"asdfjvjadsffvaadfkfasaffdsasdffadsafafsafdadsfaafd" * 该字符串中 ...
- Java线程(学习整理)--1--守护线程
1.什么是守护线程? 今天老师讲解我才知道有守护线程这回事!原来守护线程经常存在于我们的身边,比如:一个免费的网页游戏,里面都会或多或少有些插入性的广告!! 一般情况下,我们不会去点击这些广告的,但是 ...
- Spring在代码中获取bean的几种方式(转:http://www.dexcoder.com/selfly/article/326)
方法一:在初始化时保存ApplicationContext对象 方法二:通过Spring提供的utils类获取ApplicationContext对象 方法三:继承自抽象类ApplicationObj ...
- python中如何使用help命令?
python下 help()使用方法 查看python所有的modules:help("modules") 单看python所有的modules中包含指定字符串的modules ...
- cocos2dx ease 公式
所有ease计算公式都在CCTweenFunction.cpp里.
- WHU 1568 Product (DP、逆元)
题意: 定义f(x) 为数x的所有数字的乘积. 求满足f(k)=f(x)的不同的不含数字1的k的个数. x的长度小于50. 不超过1000组数据. Solution: 由于函数是乘积的形式,可以由质因 ...
- jQuery选择器种类整理
选择器概念 jQuery选择器是通过标签.属性或者内容对HTML内容进行选择,选择器运行对HTML元素组或者单个元素进行操作. jQuery选择器使用$符号,等同于jquery,例如: $(“li”) ...
- 分数拆分( Fractions Again, UVA 10976)-ACM
It is easy to see that for every fraction in the form (k > 0), we can always find two positive i ...
- 一次性安装src.rpm编译所依赖的软件包
yum-builddep SRPMS/fcitx-4.2.8.4-4.1.cgdl21.src.rpm NAME yum-builddep - install missing depend ...
- 练习2 B题 - 求绝对值
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Description 求实数 ...