java约瑟夫环迭代器_Josephus约瑟夫环问题的不同实现方法与总结

/************************************************************************/

/*                  Josephus问题——数组实现                          */

/************************************************************************/

#include

#include

int Josephus(int times, int number, int id){

int *a;

int i, count = 0, t = 0;

a = (int *)malloc(sizeof(int) * number);

for(i = 0; i < number; i++)

a[i] = i + 1;            // 数组a用于储存每个元素的编号

i = id - 1;

while(count < number - 1){

if(a[i] != 0)

t++;

if(t == times){

t = 0;

count++;

printf("%4d", a[i]);

a[i] = 0;                // 当该元素被剔除时,该数组元素置为0

}

i++;

if(i == number)

i = 0;

}

for(i=0;i

if(a[i]!=0)

{

printf("\n最后剩余的结点是:%4d\n",a[i]);

return;

}

}

int main(){

int times, number, id;

printf("请输入总人数:");

scanf("%d", &number);

printf("请输入报数周期:");

scanf("%d", &times);

printf("请输入开始报数的编号:");

scanf("%d", &id);

Josephus(times, number, id);

return 0;

}

/************************************************************************/

/* 总结:

优点为可以得出每次被剔除的元素编号

缺点为内存空间占用较大,没有数学归纳法快速                        */

/************************************************************************/

/************************************************************************/

/*                  Josephus问题——循环链表实现                      */

/************************************************************************/

#include

#include

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 = 1;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 = 1;i < k;i++)

{

r=p;

/*请注意,此行不是多余的,因为当k!=1,但m=1时如果没有这条语句,此时删除动作无法完成*/

p=p->next;

}        //此时p指向第1个出发结点

while(p->next != p)

{

for(i = 1;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", &times);

printf("请输入开始报数的编号:");

scanf("%d", &id);

Josephus(times, number, id);

return 0;

}

/************************************************************************/

/* 总结:

优点为可以得出每次被剔除的元素编号

缺点为相较数组方法需要更多的计算量

总体而言与数组方法相差无几                                        */

/************************************************************************/

/************************************************************************/

/*            Josephus问题——数学归纳法直接计算                      */

/************************************************************************/

#include

int main() {

int answer = 0;

int times, number, i, id;    // number为环内总元素个数,times为报数周期, id为从第几个元素开始报数

printf("请分别输入总人数和循环次数:");

scanf("%d %d", &number, &times);

printf("起始报号者的编号:");

scanf("%d", &id);

for(i = 1; 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 0;

}

对于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计数从而删除成员的方法与此相比起来逊色不少,故之后我们将采用此方法来解决问题。

该问题的最终解决程序可参见另一篇文章: Java实现 Josephus约瑟夫环问题  http://www.linuxidc.com/Linux/2017-05/144055.htm

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值