python3 题解(21)-约瑟夫环

约瑟夫环

【问题】n个小孩,编号1~n,站成一圈玩游戏。从编号1的小朋友开始,顺时针1,2,3报数,报到3的小朋友出列。他的顺时针方向下一个小朋友重新开始1,2,3报数。
游戏一直这样进行下去,直到队列里只剩下一个小朋友。
求最后剩下的小朋友的编号。

分析:
为了用数组操作方便,可以把游戏改为如下等价效果的玩法:

  1. 一个小朋友从队头跑到队尾,
  2. 再一个小朋友从队头跑到队尾,
  3. 从队头删去一个小朋友。
    … 再重复这个过程,直到队里只有一个人。

它对应的python 解法:

def josephus(n):
	a = []
	for i in range(1,n+1): a.append(i)

	while len(a) > 1:
		a.append(a.pop(0))
		a.append(a.pop(0))
		a.pop(0)

	return a[0]

if __name__ == '__main__':
	for i in range(10,20):
		print(i, '==>', josephus(i))

对 n 在 10, 20 之间的数字实验,结果如下:

10 ==> 4
11 ==> 7
12 ==> 10
13 ==> 13
14 ==> 2
15 ==> 5
16 ==> 8
17 ==> 11
18 ==> 14
19 ==> 17

这个解法虽然可行,然而效率不高,数一大,就很慢了。
能不能用递归的想法呢?

当 n-1 个人玩此游戏,第 x 人为所剩
现在编号为 n 的人加入队尾。
集中脑力考虑:从哪个号开始游戏,才能第一次就删去新加入的这个第 n号呢?
显然,从 n-2 开始就行。最后所剩,就是编号为 x 的人

按游戏规则,从 n-2 开始,n-2 就应该编号为 1
现在的问题变成了:如何重新编号
如果重新如此编号,原来编号 x 的人的新编号是多少呢?
大约就是:(x + 3) % n

思路已定,开始编码:

def josephus(n):
	if n == 1: return 1
	k = (josephus(n-1) + 3) % n
	return k if k > 0 else n

if __name__ == '__main__':
	for i in range(10,20):
		print(i, '==>', josephus(i))

拿大数试一下,这要快得多了。


版权声明:本文为ggyhang原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。