约瑟夫环的故事
公元66年,著名犹太历史学家约瑟夫( Josephus)在罗马人占领乔塔帕特后,在被迫不情愿的情况发发动起义失败,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,当时的罗马将军韦斯巴芗(Vespasian)派人来劝降,约瑟夫主张投降,但是除了他和他的朋友其他犹太人宁死也不愿意被敌人抓到。约瑟夫耍了个小技巧,他提出了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀为止。然而约瑟夫和他的朋友并不想遵从。陆续杀死了其余被困者,只有他和他的朋友出了山洞投降。
这个事件也反应了那个时代犹太人悲惨的状况。当他被带到韦斯巴芗面前时,因为当面预言韦斯巴芗会成为罗马皇帝,所以免于牢狱之刑。后来这个预言果真应验了,于是,他托庇于韦斯巴芗门下,并取得了罗马公民身份。在提图斯(韦斯巴芗的儿子)于公元70年摧毁耶路撒冷圣殿之后,他移居于罗马。因为这些经历,他常被指责为叛徒。
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
约瑟夫环问题
上面的只是约瑟夫环背后的小故事,将故事中的问题转化为数学问题
在一个圈之中有n个点,指定一个入口,给定一个k,从入口点开始报数,每报到k移除该点,再次从1开始报至k直至剩下最后一个点,该点的数即为约瑟夫环的解
图解约瑟夫环
编程实现约瑟夫环
可以说约瑟夫环非常适合用计算机的程序语言实现了,且该结构非常适合用链表实现,因为链表是可以连成环且删除数据的时间复杂度仅为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);
}