首页 技术 正文
技术 2022年11月20日
0 收藏 300 点赞 3,831 浏览 2395 个字

  笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争——马萨达战争,深为震撼,有兴趣的同学可以移步: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);
}}

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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,082
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,557
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,406
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,179
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,815
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,898