约瑟夫问题概述
来源:互联网 发布:源码包安装apache 编辑:程序博客网 时间:2024/06/03 20:46
我在很久之前就已经接触了约瑟夫问题了,但是最近终于找到了时间效率比较优秀的解决办法,写一篇博客以纪念一下。
什么是“约瑟夫问题”?
以下内容来自百度百科:(可以忽略以下三段内容)
约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)
关于它的故事:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39
个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus
和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
另一个故事:
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15
个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
下文中所介绍的问题与上面的两个类似,我们称它为“约瑟夫序列模型”。它的内容如下:
若干个人排成一圈,按照顺时针编号为1~n。从一号开始对环进行如下处理:1号人跳过,2号出环,3号跳过,4号出环,5号跳过(即每隔一个人,出圈一个人)…又因为所有的人站成一个环,最终所有的人一定都会出环(最后剩下的那个人被看成是最后一个出环的人)。用这种方法,我们把每个人按照出圈次序排序,就得到了一个“出圈序列”,下文中称其为“约瑟夫序列”。
如上图,
问题1:快速计算约瑟夫序列末尾元素
——每个人都想成为笑到最后的人,所以他总是想找到最后一个出圈的位置。
本题的一种暴力做法是用链表去模拟,但其时间效率比较差(如果只是求末尾元素的话。)
首先,无论n是奇数还是偶数,一个编号为偶数的位置一定不可能成为最后一个出圈的位置,因为在第一趟走的时候这些位置就会出圈,剩下的是所有的奇数。
此时我们不妨将所有的奇数重新编号为
我们不妨令
首先分析当n为偶数时的情况(令
第一步:所有的偶数位置被弹出。
第二步:将奇数序列
即:
然后再分析当n为奇数时的情况(令
第一步:将所有偶数位置弹出。
第二步:因为序列的总长度为奇数,所以下一个出圈的一定是编号为1的那个人。
第三步:把现在剩下的人的序列
即:
边界条件:
程序写出来近乎脑残:
int J(int n){ if(n==1)return 1; if(n%2==0) return 2*J(n/2)-1; return 2*J(n/2)+1;}
它的时间复杂度为
关于问题1的进一步探索
打一个表,看看函数
#include<cstdio>int J(int n){ if(n==1)return 1; if(n%2==0) return 2*J(n/2)-1; return 2*J(n/2)+1;}int main(){ printf(" i :"); for(int i=1;i<=15;i++) printf("%5d",i); printf("\n"); printf("J(i):"); for(int i=1;i<=15;i++) printf("%5d",J(i)); printf("\n"); return 0;}
可以打出来这样的一个表:
i : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15J(i): 1 1 3 1 3 5 7 1 3 5 7 9 11 13 15
不难发现一些神奇的规律:
序列J可以看成是若干个奇数等差数列的拼接
这个性质可用
这个性质可以用数学归纳法证明:
定义命题
1.
2.当该性质已经关于
(1)令
(2)令
p.s. 因为
因此得证。
问题2:快速计算某一个人的出圈时间
不妨令
当
当
1.n是偶数,令
同之前求最后一个出队人的方法一样,先去掉所有编号为偶数的人,然后给剩下的人重新编号为
即:
2.n是奇数,令
方法同上,先去掉所有的偶数以及1,将
即:
这样看可能有点复杂,我们不妨令
程序还是那么的水:
int f(int n,int k){ if(k%2==0)return k/2;//k=0 included if(n%2==0)return n/2+f(n/2,(k+1)/2); return (n+1)/2+f((n-1)/2,(k-1)/2);}
很显然的是时间复杂度仍然是
如有谬误,敬请指正!
- 约瑟夫问题概述
- 约瑟夫问题、约瑟夫环
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 约瑟夫问题
- 目标跟踪方法总结
- python学习过程
- 如何修改网页小图标,浏览器页面上的图标。
- ubuntu 安装redis两种方式 教程
- 知乎对「如何提问题?」的答案总结
- 约瑟夫问题概述
- IOS微信端返回重写
- ASCII、Unicode、GBK和UTF-8字符编码的区别联系
- Spring MVC访问页面直接显示源码
- C# 高级编程 对象和类型
- angularjs开发过程,错误集合
- 使用LinkedHashMap进行分数排序
- UE4 C++ Puzzle 模版学习和分析
- An Introduction to DNS Terminology, Components, and Concepts