每日一练 no.18 约瑟夫问题

问题:

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

解答:

模拟推导:

使用collections模块deque进行模拟:

import collections
def ysf(a, b):
    d = collections.deque(range(1, a+1)) # 将每个人依次编号,放入到队列中
    while d:
        d.rotate(-b) # 队列向左旋转b步
        print(d.pop()) # 将最右边的删除,即自杀的人

if __name__ == '__main__':
    ysf(41,3) # 输出的是自杀的顺序。最后两个是16和31,说明这两个位置可以保证他俩的安全。

数学推导:

n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始): k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2,并且从k开始报0。
假设x是最终的胜利者,逆推的话容易得到 x’=(x+k)%n ,可以以此逆推到最开始的位置。

# 递归直接求出
def ysf(m,k):   
    if m == 1:
        return 0
    else:
        return (ysf(m-1, k) +k) % m
    
ysf(41, 3) + 1

# 遍历求出结果
def ysf(m, k):
    s = 0
    for i in range(2, m+1):
        s = (s + k) % i
    return s

ysf(41, 3) + 1
点赞