美文网首页
Python约瑟夫圆环的解法

Python约瑟夫圆环的解法

作者: 吾星喵 | 来源:发表于2018-04-23 15:21 被阅读138次

现在有13个人围成一个环,从1开始报数,数到3的人离开,写出程序计算最后剩下的是谁。

使用while循环

def josephus1(num, k, m):
    """
    约瑟夫环(约瑟夫问题)是一个数学的应用问题:
    已知num个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。
    从编号为k的人开始报数,数到m的那个人出列;
    他的下一个人又从1开始报数,数到m的那个人又出列;
    依此规律重复下去,直到圆桌周围的人全部出列。
    :param num:总人数
    :param k:开始的编号
    :param m:数到m的出列
    :return:
    """
    alist = [x + 1 for x in range(num)]
    print(alist)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

    index, step = k-1, m  # 从1号开始报数,数到3的那个人出列
    while len(alist) > 1:
        index = (index + step - 1) % len(alist)  # 列表的索引
        print('出去的数:', alist[index])
        # del alist[index]
        alist.pop(index)
    return '最后的一个数:%s' % alist[0]


print(josephus1(13, 1, 3))  # 总共13个人,从第1个开始数,每数到3的出去
"""
出去的数: 3
出去的数: 6
出去的数: 9
出去的数: 12
出去的数: 2
出去的数: 7
出去的数: 11
出去的数: 4
出去的数: 10
出去的数: 5
出去的数: 1
出去的数: 8
最后的一个数:13
"""

使用for循环

def josephus2(num, k, m):
    alist = list(range(1, num + 1))
    index, step = k - 1, m  # 从1号开始报数,数到3的那个人出列
    for i in range(num-1):
        index = (index + step - 1) % len(alist)
        print('出去的数:', alist.pop(index))
    return '最后的一个数:%s' % alist[0]


print(josephus2(13, 1, 3))
"""
出去的数: 3
出去的数: 6
出去的数: 9
出去的数: 12
出去的数: 2
出去的数: 7
出去的数: 11
出去的数: 4
出去的数: 10
出去的数: 5
出去的数: 1
出去的数: 8
最后的一个数:13
"""

使用递归

alist = list(range(1, 13+1))


def josephus3(alist, k=1, m=3):
    """
    由于使用了递归,有一个隐患就是当传入的列表过大时,会造成
    RecursionError: maximum recursion depth exceeded while calling a Python object
    :param alist:传入的列表
    :param k:
    :param m:
    :return:
    """
    if len(alist) > 1:
        index, step = k - 1, m  # 从1号开始报数,索引为0,数到3的那个人出列
        index = (index + step - 1) % len(alist)
        print('出去的数: ', alist.pop(index))
        return josephus3(alist, index + 1, m)
    else:
        return '最后的一个数:%s' % alist[0]


print(josephus3(alist, 1, 3))
"""
出去的数:  3
出去的数:  6
出去的数:  9
出去的数:  12
出去的数:  2
出去的数:  7
出去的数:  11
出去的数:  4
出去的数:  10
出去的数:  5
出去的数:  1
出去的数:  8
最后的一个数:13
"""

摒弃递归,每次步长不为k时候都把当前元素弹出并放到队列尾部,从而模拟循环链表结构。进一步优化,由于列表弹出第一个元素的复杂度较高,可以使用双端队列来进行优化:

from collections import deque

alist = list(range(1, 13+1))


def josephus4(alist, k=1, m=3):
    """
    如果开始的位置不是1,则先进行队列变化,然后将3前面的数字append到队列末端,再pop掉3位置的数字
    :param alist: 传入的列表
    :param k:
    :param m:
    :return:
    """
    dq = deque(alist)
    tmp_k = k-1
    while tmp_k:
        dq.append(dq.popleft())
        tmp_k -= 1
    print('变换为新序列:', dq)
    k = 1  # 变换后将从第1个开始
    while len(dq) > 1:  # 当队列长度大于1时,进行队列的pop操作
        if k != m:
            dq.append(dq.popleft())
            k += 1
            print(dq)
        else:
            print('从左端移除:', dq.popleft())
            k = 1
    return '最后的一个数:%s' % dq[0]


print(josephus4(alist, 1, 3))
"""
变换为新序列: deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
deque([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1])
deque([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2])
从左端移除: 3
deque([5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 4])
deque([6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 4, 5])
从左端移除: 6
deque([8, 9, 10, 11, 12, 13, 1, 2, 4, 5, 7])
deque([9, 10, 11, 12, 13, 1, 2, 4, 5, 7, 8])
从左端移除: 9
deque([11, 12, 13, 1, 2, 4, 5, 7, 8, 10])
deque([12, 13, 1, 2, 4, 5, 7, 8, 10, 11])
从左端移除: 12
deque([1, 2, 4, 5, 7, 8, 10, 11, 13])
deque([2, 4, 5, 7, 8, 10, 11, 13, 1])
从左端移除: 2
deque([5, 7, 8, 10, 11, 13, 1, 4])
deque([7, 8, 10, 11, 13, 1, 4, 5])
从左端移除: 7
deque([10, 11, 13, 1, 4, 5, 8])
deque([11, 13, 1, 4, 5, 8, 10])
从左端移除: 11
deque([1, 4, 5, 8, 10, 13])
deque([4, 5, 8, 10, 13, 1])
从左端移除: 4
deque([8, 10, 13, 1, 5])
deque([10, 13, 1, 5, 8])
从左端移除: 10
deque([1, 5, 8, 13])
deque([5, 8, 13, 1])
从左端移除: 5
deque([13, 1, 8])
deque([1, 8, 13])
从左端移除: 1
deque([13, 8])
deque([8, 13])
从左端移除: 8
最后的一个数:13
"""

相关文章

网友评论

      本文标题:Python约瑟夫圆环的解法

      本文链接:https://www.haomeiwen.com/subject/pnaklftx.html