约瑟夫环问题

约瑟夫环问题

@(算法)


问题描述与解决

Josephus的故事:39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓。于是决定了自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀。然后下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

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

下面使用数组遍历计数的方法解决约瑟夫问题:

public class JosephProblem {

    public static void main(String[] args){
        josephusStory();
        System.out.println();
        System.out.println();
        System.out.println("------------------------------------");
        System.out.println();
        gaspardStory();
    }

    public static void josephusStory(){
        // true:自杀了
        // false:或者
        boolean[] menInCave = new boolean[41];

        // 自杀计数指针,只数活人
        int suicidePointer = 0;
        // 留下的人数计数
        int menAlive = menInCave.length;
        // 步数计数
        int count = 0;

        while(true){
            if(count == menInCave.length){
                count = 0;
            }
            if(!menInCave[count]){
                suicidePointer++;
            }
            if(suicidePointer == 3){
                menInCave[count] = true;
                menAlive--;
                suicidePointer = 0;
            }
            if(menAlive == 2){
                break;
            }
            count++;
        }

        // 打印时位置编号
        int num = 1;
        for (boolean man : menInCave) {
            System.out.print(num+":"+ man+" ");
            num++;
        }
    }

    public static void gaspardStory(){
        // true:被扔下海
        // false:活着
        boolean[] menOnBoat = new boolean[30];

        // 杀人计数指针,只数活人
        int killPointer = 0;
        // 留下的人数计数
        int menAlive = menOnBoat.length;
        // 步数计数
        int count = 0;
        while(true){
            if(count == menOnBoat.length){
                count = 0;
            }
            if(!menOnBoat[count]){
                killPointer++;
            }
            if(killPointer == 9){
                menOnBoat[count] = true;
                menAlive--;
                killPointer = 0;
            }
            if(menAlive == 15){
                break;
            }
            count++;
        }

        // 打印时位置编号
        int num = 1;
        for (boolean man : menOnBoat) {
            System.out.print(num+":"+ man+" ");
            num++;
        }
    }
}

约瑟夫问题背后的故事

引用的原文出处

计算机专业的学生大都知道约瑟夫环问题。

n个人(编号1…n)围成一圈,选择一个正整数k,从第一个人开始从1报数,报到k的人被拉出圈外,在剩余的人中,从被拉出去那人下一个人开始,继续从1开始报数,报到k的人再被拉出圈外。如此循环,直至剩下最后一人。所以,对于每给定一组n和k,可以算得一个编号序列,序列中编号的位置表示这个人被拉出圈外的顺序。

这是一个数学问题,非常适合用计算机编程去解决。所以,它经常作为一道编程练习题,出现在各种计算机编程语言教材当中。之所以叫约瑟夫环,因为相传是由犹太历史学家约瑟夫(公元37-100)最先提出来的。

公元66年,约瑟夫不情愿地参与领导了犹太同胞反抗罗马统治的起义,后来起义失败,他和一些宁死不降的起义者被困于一个山洞之中。罗马将军韦斯巴芗(Vespasian)派人来劝降,他主张投降,其余的人不答应,并以死相逼。最后,约瑟夫提议,与其死在自己的手上,不如死在彼此的手上。据说,他提出用约瑟夫环方法决定生死(实际上是所有人抽签,每一轮抽到1的人把抽到2的人杀死),陆续杀死了其余被困者,只有他和另外一人出了山洞投降。这个事件也反应了那个时代犹太人悲惨的状况。当他被带到韦斯巴芗面前时,因为当面预言韦斯巴芗会成为罗马皇帝,所以免于牢狱之刑。后来这个预言果真应验了,于是,他托庇于韦斯巴芗门下,并取得了罗马公民身份。在提图斯(韦斯巴芗的儿子)于公元70年摧毁耶路撒冷圣殿之后,他移居于罗马。因为这些经历,他常被指责为叛徒。

但是,约瑟夫是公元一世纪重要的历史学家,主要著作有《犹太古史》和《犹太战记》。再由于他生活于1世纪(和使徒时代相当),是距离耶稣基督时代最近的历史学家。所以,他关于犹太历史的描述常作为《圣经》里记述史实的重要佐证和参考。《犹太古史》里关于希律家族、施洗约翰、大祭司、犹大巡抚(彼拉多、腓力斯、非斯都)等记述都与《圣经》<福音书>和<使徒行传>相印证,有些内容互相补充。例如,关于施洗约翰的被捕入狱和《圣经·马太福音》一致,并且约瑟夫给出了施洗约翰被囚禁和杀害的地点。再如,关于希律亚基帕演讲时暴毙的史实,《圣经·使徒行传》12章记载,希律面对听众谄媚,没有归荣耀给神,被主的使者所罚,以至于被虫所咬而亡。而《犹太古史》里面也有相关记载,并且给出了详细的过程描述。《犹太古史》从另一个视角给《圣经》部分内容提供了证据,证实了《圣经》记载的正确性。

耐人寻味的是,在《犹太古史》里面也有关于耶稣基督的记述,里面这样记述:此时,有一位智者,名叫耶稣,他有佳美的言行,并因为自己的德行而著称。在犹太及其他民族中,有许多人跟从耶稣,作了他的门徒。彼拉多将他定罪,并把他钉死在十字架上。然而,那些已经成为耶稣门徒的人,并没有因此背弃自己门徒的身份。他们宣称说,耶稣在被钉死在十字架上的第三天向他们显现了,耶稣已经复活了。因此,耶稣可能就是那位弥赛亚。众先知已经就他做出了奇妙的预言。而且,那些因为耶稣基督得名的基督徒们,迄今为止仍然没有消失。引自《约瑟夫著作精选》,北京大学出版社。

从这份描述可见,在使徒时期,耶稣基督的名声并没有因为被钉十字架而削弱。相反地,在耶稣的感召下在,经过使徒们的不懈努力,基督复活的福音正迅速传播。从犹太地,到撒玛利亚,再到亚细亚,非洲,欧洲等等。约瑟夫是一位犹太人,能做出如此评价,可见耶稣对那个时代影响之深。这种影响不仅是因为对耶稣所行诸多神迹的耳闻目睹,还包括众门徒所做的基督复活见证。耶稣基督复活之后曾多次向多人显现,最多一次是同时向500人显现(哥林多前书15:6)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值