1.问题描述
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲到一个故事:
15个教徒和15个非教徒在深海上遇险,必须将一半的人投入海中,其余的
人才能幸免于难。于是想了一个办法,将30个人围成一个圆圈,从第一个
人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅
剩15个人为止。问怎样的排法才能使每次投入大海的都是非教徒?
2.问题分析
本题明显是一个约瑟夫问题。约瑟夫问题的求解方法很多,这里仅给
出一种实现方法。
问题描述中说“将30个人围成一个圆圈”,据此可以考虑使用一个循环
的链来表示。
3.算法设计
我们使用一个二维数组array[flag][next]来存放30个人,其中flag用来表
示是否被扔下海的标记。为1表示没有被扔下海,为0表示已被扔下海。next
表示指向下一个人的数组下标。
程序从第一人开始对还未扔下海的人进行计数,每数到9时,就将数组
中的标记改为0,表示该人已经被扔下海了,如此循环计数直到有15个人被
扔下海为止。
4.确定程序框架
1)定义二维数组。定义一个二维数组用来存放30个人,代码如下:

array = [[0] * 2 for i in range(31)] # 30个人,0号元素没有使用

上面的代码定义了二维数组array,该数组有31个元素,数组中的0号元
素未使用,1~30号元素分别表示题中所述的30个人。
2)初始化二维数组。二维数组的初始化代码如下:

for i in range(1, 31): # 初始化结构数组
array[i][0] = 1 # flag标志置为1,表示人在船上
array[i][1] = i + 1 # next的值为数组中下一个元素的下标,即指向下一个人
array[30][1] = 1 # 第30个人的数组下标指向第一个人以构成环

 上面的代码使用for循环完成对二维数组的初始化工作。初始情况下,
由于30个人都在船上,因此数组中每个元素的flag成员的值都置为1,next成
员的值设置为数组中下一个元素的下标,即指向下一个人。需要注意的
是,array数组中第30个元素的next成员值需要重新指定为1,用于指向第一
个人以构成环。
3)计数15次,每到第九个人就将其扔入大海。实现核心功能的代码如
下:

j = 30 # 变量j指向已经处理完毕的数组元素,从array[i]指向的人开始计数
for i in range(15): # 循环变量i作为计数器,记录已扔下海的人数,共15个人
k = 0
while True: # 循环变量k作为计数器,决定哪个人被扔下海,计数到9为止
if k < 9:
j = array[j][1] # 修改数组下标,取下一个人
k += array[j][0] # 进行计数,已扔下海的人标记为0
else:
break # 计数到9则停止计数
array[j][0] = 0 # 将标记置0,表示该人已被扔下海

上面的程序中循环变量i作为计数器,用于记录已扔下海的人数。循环
变量k作为计数器,用于找到每次被扔下海的那个人,计数到9为止。每次
循环找到被扔下海的那个人后就将其flag标记置0。
程序流程图如图12.1所示。
5.完整的程序
根据上面的分析,编写程序如下:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @author : liuhefei
# @desc: 约瑟夫环
if __name__ == '__main__':
array = [[0] * 2 for i in range(31)] # 30个人,0号元素没有使用
print('最终结果为(s:被扔下海,b:在船上):')
for i in range(1, 31): # 初始化结构数组
array[i][0] = 1 # flag标志置为1,表示人在船上
array[i][1] = i + 1 # next的值为数组中下一个元素的下标,即指向下一个人
array[30][1] = 1 # 第30个人的数组下标指向第一个人以构成环
j = 30 # 变量j指向已经处理完毕的数组元素,从array[i]指向的人开始计数
for i in range(15): # 循环变量i作为计数器,记录已扔下海的人数,共15个人
k = 0
while True: # 循环变量k作为计数器,决定哪个人被扔下海,计数到9为止
if k < 9:
j = array[j][1] # 修改数组下标,取下一个人
k += array[j][0] # 进行计数,已扔下海的人标记为0
else:
break # 计数到9则停止计数
array[j][0] = 0 # 将标记置0,表示该人已被扔下海
for i in range(1, 31): # 输出结果
if array[i][0]:
print('b', end='')
else:
print('s', end='')
print()

python有n个人围成一圈代码及测试结果 python有30人围成一圈_二维数组

6.运行结果
在PyCharm下运行程序,结果如图12.2所示。其中,s表示数组中该位
置代表的人将会被扔进海里,b表示数组中该位置代表的人会待在船上。

python有n个人围成一圈代码及测试结果 python有30人围成一圈_数组_02

 

 7.拓展训练
思考题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3
报数),凡是报到3的人就退出圈子,问最后留下的人是原来的第几号。