约瑟夫环
【问题】n个小孩,编号1~n,站成一圈玩游戏。从编号1的小朋友开始,顺时针1,2,3报数,报到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))
拿大数试一下,这要快得多了。
本文探讨了约瑟夫环问题的解决方案,通过将游戏转化为等效的数组操作,然后使用递归思想改进算法。分析指出,从n-2开始游戏,可以第一次就删除新加入的编号为n的人。通过重新编号,可以找到在游戏结束时剩余的编号。最终给出的Python代码在处理大数据时表现出更高的效率。
7475

被折叠的 条评论
为什么被折叠?



