笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争——马萨达战争,深为震撼,有兴趣的同学可以移步:http://finance.ifeng.com/a/20170627/15491157_0.shtml .

  这不仅让笔者想起以前在学数据结构时碰到的Josephus问题:

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

  以前我们都是用链表的方法编程来解决这个问题的,这次笔者将会讲述两个不同的方法,一个是笔者自己的朴素想法,一个是数学方法。

  • 朴素方法
  • 数学方法

  首先我们先将Josephus问题描述出来,即: 共有N个人围成一圈,编号分别为1,2,...,N,从第一个人开始从1到m报数,报到m的人退出,如此循环下去,直至最后一个人。问最后一个人的最开始的编号是几?

  先是笔者的朴素想法。

  将N个人储存在列表(list)中,每次报到m的元素剔除,并记录下最后一个人报的数i,然后将缩短后的数组从i+1报数,如此循环下去,直至列表的长度为1,这样剩下来的元素就是我们要求的答案。

  这种想法虽然素朴,比较容易实现,但是时间复杂度为O(Nm).

  接着是数学方法。

  假设一开始的Josephus环编号为0,1,2,...,N-1.我们知道第一个人(编号一定是m%N-1) 出列之后,剩下的N-1个人组成了一个新的Josephus环(以编号为k=m%n的人开始):

\[k, k+1, k+2,......, n-2, n-1, 0, 1, 2, ... k-2
\]

并且从k开始报0.

  现在我们把他们的编号做一下转换:

\[k --> 0\\
k+1 --> 1\\
k+2 --> 2\\
...\\
k-2 --> n-2\\
k-1 --> n-1\]

变换后就成为了(N-1)个人报数的子问题,这启示我们可以用归纳法来解决这个问题。假如我们知道这个子问题的解为\(x\),原来问题的答案为\(x^{'}\),则\(x^{'}=(x+k)\%n.\)因此,递推公式就有了!令\(f(i)\)表示\(i\)个人玩游戏报\(m\)退出最后胜利者的编号,我们要求的结果是\(f(N)\),其递推公式如下:

\[f(1)=0\\
f(1)=(f(i-1)+m)\% i \qquad (i>1)\]

  数学方法简单明了,计算效率高,但是推导比较困难。

  最后,我们给出以下两种方法的Python代码和朴素方法的Java代码,希望能给大家一点思考。

  完整的Python代码如下:

# -*- coding: utf-8 -*-

# This code is devoted to solve the Josephus Problem by Python.

# N: numper of people
# m: cycle number
def solve1(N, m):
a = list(range(1, N+1)) # sequence end = 0 # the number of last man in sequence
while len(a) > 1:
b = []
for i in a:
if not (end+a.index(i)+1)%m:
b.append(i)
# print(i, end=' ') # print the order of removing
if a.index(i) == len(a)-1: # last man of sequence
end = (end+a.index(i)+1)%m # remove elements in b from a
for i in b:
a.remove(i) return a[0] # solve the problem by math method
def solve2(N, m):
return 0 if N == 1 else (solve2(N-1, m)+m)%N # main function for execution
def main():
N, m = 41, 3
left1 = solve1(N, m)
print('\nThe man left: %d' %left1) left2 = solve2(N, m)+1
print('\nThe man left: %d' % left2) main()

  完整的Java代码如下:

import java.util.ArrayList;

public class Josephus {

	public static void main(String[] args) {
int N = 41;
int m = 3;
int left = solve(N, m);
System.out.println("\nThe man left is "+left+"."); } public static int solve(int N, int m) {
ArrayList<Integer> a = new ArrayList<Integer>();
int end = 0; for(int i=0; i < N; i++)
a.add(i+1); while(a.size() > 1) {
ArrayList<Integer> b = new ArrayList<Integer>(); for(int i: a) {
if ((end+a.indexOf(i)+1)%m == 0)
b.add(i);
// System.out.print(i+"-->"); if(a.indexOf(i) == a.size()-1)
end = (end+a.indexOf(i)+1)%m;
} for(Object i: b) {
a.remove(i);
}
} return a.get(0);
} }

  本次分享到此结束,欢迎大家交流~~

Josephus Problem的详细算法及其Python、Java实现的更多相关文章

  1. 算法Sedgewick第四版-第1章基础-017一约瑟夫问题(Josephus Problem)

    /************************************************************************* * * Josephus problem * * ...

  2. 十大经典排序算法(Python,Java实现)

    参照:https://www.cnblogs.com/wuxinyan/p/8615127.html https://www.cnblogs.com/onepixel/articles/7674659 ...

  3. 冒泡排序算法的C++,Java和Python实现和冒泡排序算法三种语言效率的比较

    冒泡排序原理: 这一篇百度经验讲得很好,我不多说了 https://jingyan.baidu.com/article/6525d4b13f920bac7d2e9484.html 他讲的是C语言,没有 ...

  4. 八大排序算法总结与java实现(转)

    八大排序算法总结与Java实现 原文链接: 八大排序算法总结与java实现 - iTimeTraveler 概述 直接插入排序 希尔排序 简单选择排序 堆排序 冒泡排序 快速排序 归并排序 基数排序 ...

  5. josephus Problem 中级(使用数组模拟链表,提升效率)

    问题描写叙述: 在<josephus Problem 0基础(使用数组)>中.我们提出了一种最简单直接的解决方式. 可是,细致审视代码之后.发现此种方案的效率并不高,详细体如今.当有人出局 ...

  6. 机器学习算法与Python实践之(四)支持向量机(SVM)实现

    机器学习算法与Python实践之(四)支持向量机(SVM)实现 机器学习算法与Python实践之(四)支持向量机(SVM)实现 zouxy09@qq.com http://blog.csdn.net/ ...

  7. 机器学习算法与Python实践之(二)支持向量机(SVM)初级

    机器学习算法与Python实践之(二)支持向量机(SVM)初级 机器学习算法与Python实践之(二)支持向量机(SVM)初级 zouxy09@qq.com http://blog.csdn.net/ ...

  8. 约瑟夫问题(Josephus Problem)的两种快速递归算法

    博文链接:http://haoyuanliu.github.io/2016/04/18/Josephus/ 对,我是来骗访问量的!O(∩_∩)O~~ 约瑟夫问题(Josephus Problem)也称 ...

  9. 机器学习算法与Python实践之(五)k均值聚类(k-means)

    机器学习算法与Python实践这个系列主要是参考<机器学习实战>这本书.因为自己想学习Python,然后也想对一些机器学习算法加深下了解,所以就想通过Python来实现几个比较常用的机器学 ...

随机推荐

  1. 【转】《深入理解C# 3.x的新特性》博文系列汇总

    [转]<深入理解C# 3.x的新特性>博文系列汇总 较之C# 2.0, C# 3.x引入了一系列新的特性,为我们编程带来很大的便利,通过有效地利用这些新特性,我们可以编写出更加简洁.优雅的 ...

  2. 卓豪ManageEngine参加2018企业数字化转型与CIO职业发展高峰论坛

    卓豪ManageEngine参加2018企业数字化转型与CIO职业发展高峰论坛 2018年10月20日,78CIO APP在北京龙城温德姆酒店主办了主题为“新模式.新动能.新发展”的<2018企 ...

  3. s11 Docker+DevOps实战--过程和工具

    开发人员本地提交代码,本地使用容器模拟生产环境测试,测试通过提交到git master 分支,就会触发pipeline执行集成构建.集成工具: gitlab-vi,travis,或Jenkins.自动 ...

  4. C# 从后台代码同步或异步注册Javascript到页面之RegisterStartupScript和RegisterClientScriptBlock的区别

    下面来讲讲同步注册JS和异步注册JS的区别 同步注册JS:RegisterClientScriptBlock,相当于在 form开始处(紧接 <form runat="server&q ...

  5. 2019swpuj2ee作业3

    静态页面: 在网站设计中,纯粹html格式的网页通常被称为“静态网页”,早期的网站一般都是由静态网页制作的.静态网页是相对于动态网页而言,是指没有后台数据库.不含程序和不可交互的网页.你编的是什么它显 ...

  6. uasrt配置

    Universal Synchronous/Asynchronous Receiver/Transmitter 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式.同步要有一个 ...

  7. RabbitMQ与消息总线

    Windows环境安装RabbitMQ,https://www.cnblogs.com/xibei666/p/5931267.html 1.消息发送流程 using System; using Sys ...

  8. Buffer cache hit ratio性能计数器真的可以作为SQL Server 内存瓶颈的判断指标吗?

    SQL Server中对于Buffer cache hit ratio的理解: Buffer cache hit ratio官方是这么解释的:“指示在缓冲区高速缓存中找到而不需要从磁盘中读取的页的百分 ...

  9. WPF 通过线程使用ProcessBar

    WPF下使用进度条也是非常方便的,如果直接采用循环然后给ProcessBar赋值,理论上是没有问题的,不过这样会卡主主UI线程,我们看到的效果等全部都结束循环后才出现最后的值. 所以需要采用线程或者后 ...

  10. Linux pwn入门教程(1)——栈溢出基础

    作者:Tangerine@SAINTSEC 原文来自:https://bbs.ichunqiu.com/thread-42241-1-1.html 0×00 函数的进入与返回 要想理解栈溢出,首先必须 ...