python约瑟夫环

问题描述

        17世纪的法国数学家加斯帕在《数目的游戏问题》中讲到一个故事:15个教徒和15个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难。于是想了一个办法,将30个人围成一个圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅剩15个人为止。问怎样的排法才能使每次投入大海的都是非教徒?

        这个问题的本质就是,30个人排成一排不断的报数,数到9的人会被扔到海里,被扔到海里的人不能再参与到后面的报数中;后面一个人继续从1开始数,数到9的人又会被扔到海里;如此重复直到扔掉15个人后结束。实际操作过程如下图所示:

根据上图我们就可以设计出一个模拟上述操作的程序。

约瑟夫环

        我们可以先设计一个用来不断报数的生成器,它的作用就是不断的给我们输出下一个报数的位置。然后再设计一个用来执行约瑟夫环的函数,在函数中使用报数生成器,通过记录报数次数来决定把某个数去除。再把去除的数告诉报数生成器,让报数生成器不要再报已经去除的数。

模拟报数器

        首先制作一个模拟报数器,用来不断的从1数到30。我们选择生成器来实现,代码如下:

def cycle_iter(n):
    """
    模拟报数器

    :param n: 总人数
    :return: generator
    """
    while True:  # 死循环
        index = 0  # 每个循环开始给index赋初值为0
        while index < n:  # 循环n次后结束循环
            index += 1  # 每次循环先把index加一
            yield index  # 当模拟报数器被next函数调用时,返回index的值,并等待下一次被next函数调用

上面的代码就实现了一个模拟报数器,它能连续不断的生成从1到30的整数,直到我们不再需要它。但有一个问题是,当有人被扔下大海后,他所对应的数字就不存在了。所以这里我们需要一个公共变量来控制模拟报数器,让它不要报出已被扔下大海的人所对应的数字。更改后的代码如下:

result_list = []  # 用来存储被扔下大海的人所对应的数字



def cycle_iter(n):
    """
    模拟报数器

    :param n: 总人数
    :return: generator
    """
    while True:  # 死循环
        index = 0  # 每个循环开始给index赋初值为0
        while index < n:  # 循环n次后结束循环
            index += 1  # 每次循环先把index加一
            if index in result_list:  # 当result_list列表中存在index的值时
                continue  # 跳出本次循环
            yield index  # 当模拟报数器被next函数调用时,返回index的值,并等待下一次被next函数调用

我们在这里使用了一个result_list列表来存储被扔下大海的人所对应的数字,每当有人被扔下大海,我们就把他所对应的数字加入到result_list列表中。当模拟报数器数到result_list列表中存在的数字时就不向我们报数。

约瑟夫环执行器

        制作一个约瑟夫环执行器,来使用模拟报数器报数,并把每次被扔下大海的人所对应的序号存入result_list列表。代码如下:

def cycle(n, m, x):
    """
    约瑟夫环执行器

    :param n: 总人数
    :param m: 每次数的数
    :param x: 要扔下大海的人数
    :return: 被扔下大海的人所对应的序号列表
    """
    global result_list  # 声明公共变量result_list
    result_list = []  # 给result_list赋初值
    if x > n:  # 判断要扔下大海的人数是否大于总人数
        return  # 扔下大海的人数大于总人数,则不合理,直接结束程序
    my_iter = cycle_iter(n)  # 创建模拟报数器
    for j in range(x):  # 要扔x人下大海,则循环x次
        for i in range(m - 1):  # 让m - 1个人报数
            next(my_iter)  # 报数
        result_list.append(next(my_iter))  # 第m个人报数,把他扔下大海,并存储他的序号到列表
    my_iter.close()  # 关闭报数器
    result_list.sort()  # 给列表result_list排序
    return result_list  # 返回列表result_list

 我们调用cycle函数就可以推算出会被扔下大海的人的位置了。

完整代码

        把报数器和执行器结合在一起使用就可以解决约瑟夫环问题了,完整代码如下:

result_list = []  # 用来存储被扔下大海的人所对应的数字


def cycle_iter(n):
    """
    模拟报数器

    :param n: 总人数
    :return: generator
    """
    while True:  # 死循环
        index = 0  # 每个循环开始给index赋初值为0
        while index < n:  # 循环n次后结束循环
            index += 1  # 每次循环先把index加一
            if index in result_list:  # 当result_list列表中存在index的值时
                continue  # 跳出本次循环
            yield index  # 当模拟报数器被next函数调用时,返回index的值,并等待下一次被next函数调用


def cycle(n, m, x):
    """
    约瑟夫环执行器

    :param n: 总人数
    :param m: 每次数的数
    :param x: 要扔下大海的人数
    :return: 被扔下大海的人所对应的序号列表
    """
    global result_list  # 声明公共变量result_list
    result_list = []  # 给result_list赋初值
    if x > n:  # 判断要扔下大海的人数是否大于总人数
        return  # 扔下大海的人数大于总人数,则不合理,直接结束程序
    my_iter = cycle_iter(n)  # 创建模拟报数器
    for j in range(x):  # 要扔x人下大海,则循环x次
        for i in range(m - 1):  # 让m - 1个人报数
            next(my_iter)  # 报数
        result_list.append(next(my_iter))  # 第m个人报数,把他扔下大海,并存储他的序号到列表
    my_iter.close()  # 关闭报数器
    result_list.sort()  # 给列表result_list排序
    return result_list  # 返回列表result_list


print(cycle(30, 9, 15))  # [5, 6, 7, 8, 9, 12, 16, 18, 19, 22, 23, 24, 26, 27, 30]

执行结果如下:

从执行结果来看会被扔下大海的人所排的位置为[5, 6, 7, 8, 9, 12, 16, 18, 19, 22, 23, 24, 26, 27, 30]。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值