约瑟夫环问题

简介

约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。

编程解决思路

思路一 环形链表

很显然立马就会联想到用环形链表解决,很直观,编码也不会很难,控制好环形链表的头尾指针就好。(默认1号开始报数,可以添加参数K)

代码:


//链表解决,定义人结构体(链表节点)
struct people
{
    int id;
    struct people* next;
};

//n为人数,m为报号数字
void Joseph(int n,int m)
{
    int i ,j,no=0;
    struct people first ; //建立第一个人

    struct people* ring_head =&first;
    struct people* ring_tail =&first;
    struct people* pring =&first;
    struct people* p_new =NULL;
    first.id = 1;  //第一个人编号

    for (i=2;i<=n;i++) //再创建剩下的N-1个人
    {
        struct people* p_new = (struct people*)malloc(sizeof(struct people));
        p_new->id = i;
        pring->next =  p_new;
        pring = p_new;
    }
    //构成环状链表
    pring->next = ring_head;  //最后一个人的next 指向第一个人
    ring_tail = pring;  //尾指针指向最后一个人

    //当还剩一个人时结束循环,否则一直按规则报数
    while(ring_head!=ring_tail)
    {
        no++;//轮次
        for(i=1;i<m;i++)
        {
            ring_head = ring_head->next; //下一个人继续报数
            ring_tail = ring_tail->next;  //尾指针也要跟着移动
        }

        printf("现在是第%d次报数,我是第%d号同学,现在报到了%d号,退出游戏!",no,ring_head->id,m);
            printf("\n");

        //删除退出游戏的节点
        ring_head = ring_head->next; 
        ring_tail->next = ring_head;
    }

    printf("现在是第%d次报数,我编号为%d,是最后一个同学,游戏结束!",no+1,ring_head->id,m);
    printf("\n");
}

思路二 数学推导递归解决

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

现在假设m=10

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 Joseph_r(int n,int m,int i)
{
    if (i==1)
    {
        return (n+m-1)%n;
    }
    else
    {
        return (Joseph_r(n-1,m,i-1)+m)%n;
    }
}

测试代码 & 运行结果

//主函数中测试代码
    //递归
    for(i=1;i<=10;i++)
        printf("第%2d次出环:%2d\n",i,Joseph_r(10,3,i)+1);
    //环链
    Joseph(10,3);

运行结果:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值