Josephus问题的不同实现方法与总结
/************************************************************************/
/* Josephus问题——数组实现 */
/************************************************************************/
#include <stdio.h>
#include <malloc.h> int Josephus(int times, int number, int id){
int *a;
int i, count = , t = ;
a = (int *)malloc(sizeof(int) * number); for(i = ; i < number; i++)
a[i] = i + ; // 数组a用于储存每个元素的编号
i = id - ; while(count < number - ){
if(a[i] != )
t++;
if(t == times){
t = ;
count++;
printf("%4d", a[i]);
a[i] = ; // 当该元素被剔除时,该数组元素置为0
}
i++;
if(i == number)
i = ;
}
for(i=;i<number;i++)
if(a[i]!=)
{
printf("\n最后剩余的结点是:%4d\n",a[i]);
return;
} } int main(){
int times, number, id;
printf("请输入总人数:");
scanf("%d", &number);
printf("请输入报数周期:");
scanf("%d", ×);
printf("请输入开始报数的编号:");
scanf("%d", &id);
Josephus(times, number, id); return ;
} /************************************************************************/
/* 总结:
优点为可以得出每次被剔除的元素编号
缺点为内存空间占用较大,没有数学归纳法快速 */
/************************************************************************/ /************************************************************************/
/* Josephus问题——循环链表实现 */
/************************************************************************/
#include <stdio.h>
#include <malloc.h> typedef struct LNode
{
int data;
struct LNode *next;
}LNode,*Linkhead;
void Josephus(int m,int n,int k)
{
Linkhead p,r,head = NULL;
int i;
for(i = ;i <= n;i++)
{
p = (Linkhead)malloc(sizeof(LNode));//申请一个新的链结点
p->data = i;//存放第i个结点的编号
if(head == NULL)
head = p;
else
r->next = p; // 因为Insert和Del操作都需要之前一个节点的地址,故用r来存储。其作用类似栈的top
r = p;
}
p->next = head;//至此,建立一个循环链表 p = head;
for(i = ;i < k;i++)
{
r=p;
/*请注意,此行不是多余的,因为当k!=1,但m=1时如果没有这条语句,此时删除动作无法完成*/
p=p->next;
} //此时p指向第1个出发结点 while(p->next != p)
{
for(i = ;i < m;i++)
{
r = p;
p = p->next;
} //p指向第m个结点,r指向第m-1个结点
r->next = p->next; //删除第m个结点
printf("%4d",p->data); //依次输出删除结点的编号
free(p); //释放被删除结点的空间
p = r->next; //p指向新的出发结点
}
printf("\n最后剩余的结点是:%4d\n",p->data);//输出最后一个结点的编号
} int main(){
int times, number, id;
printf("请输入总人数:");
scanf("%d", &number);
printf("请输入报数周期:");
scanf("%d", ×);
printf("请输入开始报数的编号:");
scanf("%d", &id);
Josephus(times, number, id); return ;
} /************************************************************************/
/* 总结:
优点为可以得出每次被剔除的元素编号
缺点为相较数组方法需要更多的计算量
总体而言与数组方法相差无几 */
/************************************************************************/ /************************************************************************/
/* Josephus问题——数学归纳法直接计算 */
/************************************************************************/
#include <stdio.h>
int main() {
int answer = ;
int times, number, i, id; // number为环内总元素个数,times为报数周期, id为从第几个元素开始报数
printf("请分别输入总人数和循环次数:");
scanf("%d %d", &number, ×);
printf("起始报号者的编号:");
scanf("%d", &id);
for(i = ; i <= number; i++) {
answer = (answer + times) % i; // 核心算法,利用数学归纳法得出
}
if(answer + id == number)
printf("Survial: %d\n", number); // 防止当幸存者为最后一个编号时输出0的情况
else
printf("Survival: %d\n",(answer + id) % number);
// 这边利用number对answer进行取余操作以防止编号数值超过最大编号(溢出) return ;
}
对于Josephus问题有两个地方是可以进行优化的。 (总人数为N,编号为从0~N-1;经过M次报数去除一个成员,剩余成员个数为numleft, 记M%numleft为mPrime)
1、被移除的成员离上一个成员之间的距离是M%numleft-1(报数次为M%numleft).当M大于N时,该计算方式将节省大量时间
2、当mPrime大于numleft的时候可以反向遍历该表来查找要去除的成员。这样可以节省时间。同样这也就要求了该表必须是一个双向表才行。(即含有Previous方法)
该算法实现原理即为:
第一轮,必定为编号M%N-1的成员被去除,第二轮为在第一轮的基础上即从编号为M%N的成员开始正移mPrime-1个单位(或者反移numleft-mPrime-1个单位)。若将M%N即为编号0,开始重新编号,那么第二轮被删除的成员编号便是M%(numleft)-1,由此可得该轮要被删除的成员与上一轮去除成员之间的距离为M%numleft,这里可利用迭代器来实现。
这里我们便可以得到成员编号与该轮成员数目的关系是:(n表示该轮所剩余的成员数目,Index(n)表示该轮成员的编号(从0开始))
Index(n) = (Index(n - 1) + m) % n。
那么按照这个过程,我们这样一直移除元素下去,肯定能够找到最后一个被移除的元素。
这个元素则对应只有一个元素的环,很显然,它的值为0。也就是Index(1) = 0。
对于这个元素的索引,它对应两个元素的索引是多少呢?
按照前面的过程,我们倒推回去就是了。Index(2) = (Index(1) + m) % 2。
那么对应3个,4个元素的呢?我们这样一路继续下去就可以找到对应到n个元素的索引了。
所以,我们发现了一个有意思的数学归纳关系:
f(1) = 0, f(n) = (f(n - 1) + m) % n。
按照这个关系,我们可以得到最后一个被取出来的元素对应到n个元素的环里的索引值。 至此,我们可以发现,利用count计数从而删除成员的方法与此相比起来逊色不少,故之后我们将采用此方法来解决问题。
该问题的最终解决程序可参见另一篇文章:
Josephus问题的java实现
Josephus问题的不同实现方法与总结的更多相关文章
- Josephus问题的java实现
import java.util.ArrayList; import java.util.ListIterator; public class Josephus { public static voi ...
- javaSE27天复习总结
JAVA学习总结 2 第一天 2 1:计算机概述(了解) 2 (1)计算机 2 (2)计算机硬件 2 (3)计算机软件 2 (4)软件开发(理解) 2 (5) ...
- Josephus环问题
约瑟夫环问题 问题描述: Josephus问题可以描述为如下的一个游戏:N个人编号从1到N,围坐成一个圆圈,从1号开始传递一个热土豆,经过M次传递后拿着土豆的人离开圈子,由坐在离开的人的后面的人拿起热 ...
- josephus问题
问题描述 n个人围成一圈,号码为1-n,从1开始报数,报到2的退出,剩下的继续从1开始报数,求最后一个人的号码. 算法分析 最直观的算法是用循环链表模拟.从首节点开始,不断删除第二个节点,直到只剩一个 ...
- 约瑟夫问题(Josephus Problem)的两种快速递归算法
博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/ 对,我是来骗访问量的!O(∩_∩)O~~ 约瑟夫问题(Josephus Problem)也称 ...
- Josephus Problem的详细算法及其Python、Java实现
笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争--马萨达战争,深为震撼,有兴趣的同学可以移步:http://finance.ifeng.com/a/20170627/15491157_0. ...
- 约瑟夫(Josephus)问题~转
本文都是转的,一个是转博客,一个是转贴吧,前者详细,后者"强,无敌"! 博客转: 以前就知道约瑟夫问题是模拟,今天我才发现一些约瑟夫问题可以使用数学解法得出!真是强悍啊!约瑟夫问题 ...
- 谁能笑到最后,约瑟夫环-Josephus问题求解
一. 简述Josephus问题 N个人站成一环,从1号开始,用刀将环中后面一个人“消灭“”掉,之后再将刀递给下一个人,这样依次处理,最后留下一个幸存者. 二. 求解方法 1. 约瑟夫问题如果使用 ...
- mapreduce多文件输出的两方法
mapreduce多文件输出的两方法 package duogemap; import java.io.IOException; import org.apache.hadoop.conf ...
随机推荐
- 完整的堆栈JavaScript路(十五)HTML5 focus 扩大 (扩展点)
HTML5 加入辅助管理 focus 产品特点, 有办法获得焦点文档; 页面加载,用户输入(通常由tab债券).和 调用代码focus()功能. HTML5新的属性和方法,辅助管理的重点: docu ...
- Mac OS X下环境搭建 Sublime Text 2 环境变量配置 开发工具配置Golang (Go语言)
Golang (Go语言) Mac OS X下环境搭建 环境变量配置 开发工具配置 Sublime Text 2 一.安装Golang的SDK 在官网http://golang.org/ 直接下载安装 ...
- C#动态表达式计算
C#动态表达式计算 应该有不少人开发过程中遇到过这样的需求,我们直接看图说话: 如上图所示,其中Entity为实体类,其中包括五个属性,该五个属性的值分别来自于数据库查询结果: 用户通过可视化界面进行 ...
- Node填坑教程——HelloWorld
环境安装(极简): Node需要的环境可以说及其简单,也可以说及其复杂.为什么这么说呢? 如果里只需要运行环境那么到Node官网下载一个包就行了.里面自带npm管理工具,这是包管理工具,以后会频繁的使 ...
- 高并发非自增ID如何设计?
博友们一起来讨论下高并发非自增ID如何设计? 底层是很重要的,我最近设计底层,通用底层. 我想跟大家谈论下这个话题: 如何在高并发环境下设计出一套好用的非自增ID的添加操作的解决方案?更新的操作我随机 ...
- Asp.net MVC集成Google Calendar API(附Demo源码)
Asp.net MVC集成Google Calendar API(附Demo源码) Google Calendar是非常方便的日程管理应用,很多人都非常熟悉.Google的应用在国内不稳定,但是在国外 ...
- api的安全问题
在给第三方系统提供api时,我们需要注意下安全问题. 比较常见的接口有http接口.以http接口为例.我们需要注意的几点: 1.只有被允许的系统才可以调用api 2.如果http请求被截获.也不 ...
- nginx 重定向到index.php
location /keywords { index index.php; try_files $uri $uri/ /keywords/i ...
- C语言中数据类型的长度
面试中C里面int长度经常会被问到,下面总结一下作为资料: 首先看看一般规定: 标准c规定,int长度等于机器字长,short的表示范围不能大于int的表示范围,long的表示范围不能小于int的表示 ...
- (翻译) Android Accounts Api使用指南
本文翻译自Udinic的文章Write your own Android Authenticator,可能需要FQ才能阅读.这是译者目前能找到的介绍如何使用Android的Accounts Api最好 ...