约瑟夫环问题

约瑟夫环的故事

公元66年,著名犹太历史学家约瑟夫( Josephus)在罗马人占领乔塔帕特后,在被迫不情愿的情况发发动起义失败,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,当时的罗马将军韦斯巴芗(Vespasian)派人来劝降,约瑟夫主张投降,但是除了他和他的朋友其他犹太人宁死也不愿意被敌人抓到。约瑟夫耍了个小技巧,他提出了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀为止。然而约瑟夫和他的朋友并不想遵从。陆续杀死了其余被困者,只有他和他的朋友出了山洞投降。

这个事件也反应了那个时代犹太人悲惨的状况。当他被带到韦斯巴芗面前时,因为当面预言韦斯巴芗会成为罗马皇帝,所以免于牢狱之刑。后来这个预言果真应验了,于是,他托庇于韦斯巴芗门下,并取得了罗马公民身份。在提图斯(韦斯巴芗的儿子)于公元70年摧毁耶路撒冷圣殿之后,他移居于罗马。因为这些经历,他常被指责为叛徒。

17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。

约瑟夫环问题

上面的只是约瑟夫环背后的小故事,将故事中的问题转化为数学问题
在一个圈之中有n个点,指定一个入口,给定一个k,从入口点开始报数,每报到k移除该点,再次从1开始报至k直至剩下最后一个点,该点的数即为约瑟夫环的解

图解约瑟夫环

欢迎访问jo_qzy的博客

编程实现约瑟夫环

可以说约瑟夫环非常适合用计算机的程序语言实现了,且该结构非常适合用链表实现,因为链表是可以连成环且删除数据的时间复杂度仅为O(1)
约瑟夫环得到解的判定条件也很简单,当这个圈内仅有一个节点即为解
下面用c语言简单实现

/
//本代码使用的结构体
typedef int DataType

typedef struct SListNode
{
    DataType _data;
    struct SListNode* _pNext;
}
/

SListNode* JosephCircle(SListNode* pHead, size_t circle_num)
{
    assert(pHead);//防御性编程,防止传入链表为空
    SListNode* cur = pHead;
    SListNode* next = NULL;//辅助删除用指针
    while (cur->_pNext)
    {
        cur = cur->_pNext;//找到给定链表的结尾
    }
    cur->_pNext = pHead;//将链表尾部和头相连构成环
    cur = cur->_pNext;//重新将cur指针指向头部
    //核心部分
    while (cur->_pNext != cur)//当cur的next指向cur,说明仅有一个节点,得到解
    {
        size_t count = circle_num;
        while (--count)//寻找需要删除的节点
        {
            cur = cur->_pNext;
        }
        next = cur->_pNext;//我在该处选择移数据的方式删除节点
        cur->_data = next->_data;//将后一个节点的数据移至要删除的节点,问题转化为删除目标节点后一个节点
        cur->_pNext = next->_pNext;//将要删的节点移出链表
        free(next);
    }
    return cur;
}

//测试用例
void test()
{
    SListNode* s = NULL;
    SListNode* ret = NULL;
    int i = 0;
    for (i = 1; i < 8; i++)
    {
        SListPushBack(&s, i);//该函数可以在我的链表博客找到,此处我直接应用
    }
    SListPrint(s);
    ret = JosephCircle(s, 4);
    printf("约瑟夫环的解为:%d\n", ret->_data);
    free(ret);
}

运行结果

欢迎访问jo_qzy的博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值