对约瑟夫环的理解

约瑟夫环是一个数学的应用问题:已知n个人(以编号123…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

 

例题:假设下标从0开始,012 .. m-1m个人,从1开始报数,报到k则此人从环出退出,问最后剩下的一个人的编号是多少?

现在假设m=10

1次出环: 2

2次出环: 5

3次出环: 8

4次出环: 1

5次出环: 6

6次出环: 0

7次出环: 7

8次出环: 4

9次出环: 9

10次出环: 3

 

0 1 2 3  4 5 6 7 8 9    k=3

第一个人出列后的序列为:

0 1 3 4 5 6 7 8 9

:

3 4 5 6 7 8 9 0 1*

我们把该式转化为:

0 1 2 3 4 5 6 7 8 (**)

则你会发现: (**)+3%10则转化为(*)式了

也就是说,我们求出9个人中第9次出环的编号,最后进行上面的转换就能得到10个人第10次出环的编号了 

f(m,k,i)m个人的环,报数为k,第i个人出环的编号,则f(10,3,10)是我们要的结果

i=1时,  f(m,k,i) = (m+k-1)%m

i!=1时,  f(m,k,i)= ( f(m-1,k,i-1)+k )%m

所以程序如下:

int fun(int m,int k,int i){

    if(i==1)

        return (m+k-1)%m;

    else

        return (fun(m-1,k,i-1)+k)%m;

}

int main(int argc, char* argv[])

{

    for(int i=1;i<=10;i++)

        printf("第%2d次出环:%2d\n",i,fun(10,3,i));

    return 0;

}

 

另外解决方案:

1、最简单明了的办法就是用循环链表存储这n个人。数到m的人出列,只需将该结点从链表里剔除即可,然后继续往后报数,直到链表只剩下一个结点时结束。

具体代码如下:

typedef struct People

{

int number;

People *next;

}people;//结点的存储结构

 

void List_kind(People* L,int n,int m)

{//L为n个人构成的链表,报数报到m的人出列

People *q,*p=L;//p指向当前结点,q指向p的前驱

while(p->next != p)//当链表只剩下一个结点,即报数剩下一个人的时候结束。

{

for(int i=1;i<m;i++)

{//for循环实现功能:使p指向要出列的结点,q指向p的前驱

q=p;

p=p->next;

}

printf("%d\n",p->number);

q->next=p->next;//将要出列的结点从链表“剔除”

free(p);

p=q->next;//p指向出列的下一个结点,重新开始报数

}

printf("%d\n",q->number);///输出最后一个人

free(q);}

  

 

2、第二种办法就是用数组存储,难度将大大增加。但思路还是跟链表有点相似的。首先将要出列的元素输出,然后将它后面的所有元素都向前移一位,将已出列的元素填充掉。然后再向后找下一个要出列的元素。

注意:当搜索到最后一个元素a[i]以后,用求余“%”功能,让数组和循环链表一样实现循环。另外,每次输出一个元素并把它填充掉以后,数组的总元素个数i会减一。

具体代码如下:

void Array_kind(int n,int m)

{//n个人进行报数,报到m的出列

int *a,i,j,k;

a=(int*)malloc(sizeof(int));//为数组a动态分配内存

for(i=1;i<=n;i++)

a[i]=i;//对每个元素进行编号,代表每个人,注意编号从a[1]开始

for(i=n,k=1;i>=1;i--)//i为数组当前元素总个数

{

k=(k+m-1)%i;//令k指向要出列的元素

if(k==0)//从上式知,当k+m-1=i时才会(k+m-1)%i=0。因为数组是从a[1]开始的,k=0时出列的元素应该是a[i],而不是a[0]。

k=i;

printf("%d\n",a[k]);//输出要出列的元素

for(j=k;j<i;j++)//k之后的元素都向前移一位,填充出列元素的位置。

a[j]=a[j+1];

}

}

  

理解代码最好的方式就是举具体的例子,我们假设n=7,m=3,求出列顺序,代码的实现如下:

(下图表示第二个for循环的执行过程,每一行表示执行一次循环)

 《对约瑟夫环的理解》

 

 

原文链接:

http://www.cnblogs.com/yangyh/archive/2011/10/30/2229517.html

http://blog.csdn.net/bone_ace/article/details/41213215

    原文作者:HiJuly
    原文地址: https://www.cnblogs.com/junyan/p/5330755.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞