约瑟夫环问题

 
问题描述

历史典故:

据说著名犹太历史学家 Josephus有过以下的故事:

在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,

于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从。

首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

问题是,给定了和,一开始要站在什么地方才能避免被处决?

Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

问题描述

约瑟夫环:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。最优一个出列的为获胜者W

example:

N = [1,2,3,4,5,6]; M = 3;则出列顺序为:3,6,4,2,5,1;所以获胜者W=1

思路1

递归解法:

import copy

def rescue_jose(N,M):

    orders = []

    def rescue(N,M):  # 每一次出列,循环一次

        if not N:  # 递归终止条件

            return

        MM = M

        while MM > len(N):

            MM -= len(N)

    #    print(MM)

        orders.append(N[MM-1])

        rescue(N[MM:] + N[0:MM-1],M)

    rescue(N,M)

    return orders


orders = rescue_jose(N,M)

print(orders)

时间复杂度较高

思路2--一种特殊情况

固定M=2

通过代码生成大量case/data

分析规律

M = 2

for i in range(1,42):

    res[i] = rescue_jose(list(range(1,i+1)),M)[-1]

for k,v in res.items():

    print(k,v)

1 1

2 1

3 3

4 1

5 3

6 5

7 7

8 1

9 3

10 5

11 7

12 9

13 11

14 13

15 15

16 1

17 3

当M = 2

设n为所有人的人数

当n = 2^a 则获胜的肯定是1

n = 2^a + l 则获胜者是 2*l + 1

思路2参考:

[互砍惨案!约瑟夫斯问题 @圆桌字幕组](https://www.bilibili.com/video/av7885066)

思路3

[1, 2, 3, 4, 5, 6]

[4, 5, 6, 1, 2]

[1, 2, 4, 5]

[5, 1, 2]

[5, 1]

[1]

最终获胜者W的位置变化

0->3->0->1->1->0

数学可表示为:

(f(n)-M+N)%N

逆推:

(f(n)+M)%N

根据递推关系求解

#include/*

序列:0,1,2,3,4...n-1

n 表示当前序列长度

f[n] 胜利者的编号

f[1] = 0

(f[n-1]+m)%n

*/

void whoWin()

    // 求出最后的胜利者的序号

{

    int n, m, i, s = 0;

    printf ("N M = ");

    scanf("%d%d", &n, &m);

    for (i = 2; i <= n; i++)

    {

        s = (s + m) % i;

        printf("%d\n",s);

    }

    printf ("\nThe winner is %d\n", s+1);

}

int main()

{

    whoWin();

}

你可能感兴趣的:(约瑟夫环问题)