python解决约瑟夫环(杀人游戏)

约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从第s个人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后 [1] 结果+1即为原问题的解。
在这里插入图片描述
此类问题在很多地方也被加工成杀人游戏,猴子当大王问题什么的,其原型就是约瑟夫环。
分析:python中没有指针概念,所以很难形象的表示计算过程,但是高级语言也有高级语言的好处,使用一些函数调用同样可以达到目的。因为约瑟夫环的规则,所以在进行人员出列的时候,剩下的人员的排序是个问题:1、如果是静态的重排,那么就需要列尾最后一个人报完后从列头从新报数,是否可以将报完数的人排到列尾。2、如果不进行重新排序,是否可以通过计数来标记出列的人(或存活的人)。

算法设计:若有n个人,报到m的人出列,最后留下x人,那么算法结束的标志应该是人数<=x,我们假设n=20,m=3,x=5,那么
①对于第一种想法可以用以下算法:

a=[x for x in range(1,21)]   #取1-20个数,要让他从1开始,方便与1-3报数相同
b=[]			#创建空列表来记出列的人

for i in range(15*3): 	#每3次下一个人,最少要数15*3次才能数够15名下船人数
    if (i+1)%3==0:      #range(45)是从0-44,前闭后开,+1后与报数的序数一致
        b.append(a[i-len(b)])  #把a里面出列的人加入到b列表中
        y=len(b)-1 #列表b里面每增加一个元素,a里面对应的人员就会自动往前面进一位后面前进的位数刚好是列表b的长度-1
        a.remove(a[i-y]) #同时册除列表a里面被点中的人
    else:
        a.append(a[i-len(b)]) #把没有被点中的人重新加入到列表a 的后面
b.sort()   #对b排序,更直观清晰
print("被淘汰的人为:{}".format(b))

c=[x for x in range(1,21)]    #因为a列表已经之前改变表长,用c代替原来的a
for i in b:
    c.remove(i)
print("剩下的人为:{}".format(c))

输出结果为:

被淘汰的人为:[1, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 17, 18, 19]
剩下的人为:[2, 8, 13, 16, 20]

这种算法思想就是创建一个足够长的列表来计数,长度至少为(n-x)*m,然后将没有点到的人不断的再加入到列表后面(用append),将出列的人删掉(remove和pop都可以,pop会返回这个数,remove不返回),最后用新的列表来记录出列的人和剩下的人。有些麻烦,但是很好理解。

②对于第二种想法可以用以下算法:

people={}    #创建个字典,后面解释用处
for i in range(1,21):     #还是1-20个数,对应1-20个人
    people[i]=1				
    #每个号码对应的初始值为1,这点就是创建字典的用处
    #通过字典的key:value,这样就可以对valu来标记,进而来记录谁出局。
num=0           #报数置零
i=1               #报数人的位置
count=0      #记录淘汰的人数
while i<=21:
    if i == 21:   #当i循环到最后的时候,自动回到1的位置
        i=1
    elif count == 15:
        break      #当淘汰人数到15个人时,退出循环
    else:
        if people[i] == 0:       
        # 如果这个人已经被淘汰了,num不计数,此时跳过他,第i+1个人接着报数
            i+=1
            continue
        else:
            num+=1   
            #如果这个人没有被淘汰还,那么num+1,此时进行进一步判断
            if num == 3:
            #如果num到3了,那么将他的value设置为0,并print淘汰信息,num归0,count加1
                people[i]=0
                num = 0
                print("{}号淘汰了".format(i))
                count+=1
            else:
            #若不为3,则进行下一个人报数
                i+=1
                continue

输出结果为:

3号淘汰了
6号淘汰了
9号淘汰了
12号淘汰了
15号淘汰了
18号淘汰了
1号淘汰了
5号淘汰了
10号淘汰了
14号淘汰了
19号淘汰了
4号淘汰了
11号淘汰了
17号淘汰了
7号淘汰了

这种算法就是通过字典的key:value,来记录每个key的value,根据value来判断是否淘汰,控制循环,可能没有第一种清晰,但是时间和空间复杂度上要比第一种好点

③、第三种算法,先上代码

people=list(range(1,21))
while len(people)>5:
    i=1             #每次while循环时初始化i
    while i<3:
        people.append(people.pop(0))
        i+=1
    print('{}号淘汰了'.format(people.pop(0)))

结果为:

3号淘汰了
6号淘汰了
9号淘汰了
12号淘汰了
15号淘汰了
18号淘汰了
1号淘汰了
5号淘汰了
10号淘汰了
14号淘汰了
19号淘汰了
4号淘汰了
11号淘汰了
17号淘汰了
7号淘汰了

和第②种方法结果完全一样,这个算法是这样的:前面都相同,主要这句代码

while i<3:
        people.append(people.pop(0))
        i+=1

这句代码的意思是,如果i<3,也就是只要没报到3的人,用.append(.pop())函数,pop()会返回这个元素的值,然后再用append接受并加到列表最后,完成动态的“环”。举个例子:
在python3中输入

>>> a=[1,2,3,4,5]
>>> a.pop(0)

结果会返回a[0]的值,也就是1

1

此时,再用append函数接受这个值加到最后,输出列表a

>>> a.append(1)
>>> a
[2, 3, 4, 5, 1]

然后通过i的计数,完成删除

print('{}号淘汰了'.format(people.pop(0))) 
 #这句当while为FALSE,即i=3时执行此句代码

第三种算法的代码无疑是最简洁,这也是python的一大特点,当然还有其他解法,比如递归调用等等,因为本人能力有限,水平不高,无法用递归来简单清楚的解答约瑟夫环这个问题。不足之处,还请大神多多指点,O(∩_∩)O哈哈~

  • 10
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
问题是一个经典的数学问题,可以用循链表来解决。下面是一个使用Python实现的问题的代码示例: ```python class Node: def __init__(self, value=None, next=None): self.value = value self.next = next class CircularLinkedList: def __init__(self): self.head = None def add_node(self, value): new_node = Node(value) if not self.head: self.head = new_node new_node.next = self.head else: current_node = self.head while current_node.next != self.head: current_node = current_node.next current_node.next = new_node new_node.next = self.head def remove_node(self, node): if self.head == node: current_node = self.head while current_node.next != self.head: current_node = current_node.next current_node.next = self.head.next self.head = self.head.next else: current_node = self.head while current_node.next != self.head: if current_node.next == node: current_node.next = node.next break current_node = current_node.next def get_winner(self, k): current_node = self.head while len(self) > 1: for i in range(k - 1): current_node = current_node.next next_node = current_node.next self.remove_node(current_node) current_node = next_node return self.head.value def __len__(self): length = 0 current_node = self.head while current_node: length += 1 current_node = current_node.next if current_node == self.head: break return length ``` 上面的代码中,使用了循链表来表示所有的人围成的圆,然后用一个`get_winner`方法来求出最后胜利的人的编号。这个方法的具体实现是:从链表头开始遍历,每次找到第k个节点,然后把它从链表中删除,继续从下一个节点开始遍历,直到链表中只剩下一个节点为止。最后返回这个节点的值,也就是最后胜利的人的编号。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值