前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >囚徒问题解答

囚徒问题解答

作者头像
Crossin先生
发布2018-04-17 10:16:14
5770
发布2018-04-17 10:16:14
举报

前天提出了一个关于囚犯排队报数,谁能留到最后的问题:

一道囚徒问题

有人看出来,这是“约瑟夫环”问题的改编版,在网上可以搜到原版的问题,和很多种解法。

这里说一下我的解法:

大体思路就是,用一个列表表示所有囚犯,用循环去模拟报数的过程,如果报到奇数,就把当前值从列表中移除。循环一次之后,如果剩下的人超过 1 个,就对剩下的列表再进行循环。如此反复,直到剩下 1 个为止。代码如下:

代码语言:javascript
复制
def lucky(n):
    lst = range(1, n + 1)
    count = 0
    while len(lst) > 1:
        lst2 = lst[:]
        for i in lst:
            count += 1
            if count % 2 != 0:
                lst2.remove(i)
        lst = lst2
    return lst[0]

lst 是表示从 1 开始所有位置的列表。

count 表示报数,每次加 1。

count % 2 != 0 就是报到奇数。

lst2.remove(i) 移除对应元素。

最后剩下 1 个元素时,lst[0]就是最终结果。

这里有一个特别提出的地方,就是每次循环中,我都创建了一个新列表 lst2,作为 lst 的备份。删除元素时,是从 lst2 中删除。到循环结束后,再将 lst2 赋值给 lst。

思考题:

1. 如果直接在 lst 中删除行不行?

2. 如果写成 lst2 = lst 行不行?

测试几个 lucky 的输出结果,然后自己在纸上验证一下是否正确。

有同学留言说,最终结果就是队列中最大的 2 的次方数。如果是所有人站成一排,报完一轮之后,从第一个重新报数,的确是这个结果。但站成一圈循环报数就不对了。

上面我的方法只是一种可行解法。在留言中,有人给出了更好的解答,比如:

代码语言:javascript
复制
def lucky(n):
    lst = range(1,n + 1)
    while len(lst) > 1: 
        lst.append(lst[1]) 
        del lst[0:2] 
    return lst[0] 

解释一下:每次把队列中的第 2 个元素加到队尾,然后把前两个元素都删掉。一直循环,直到剩下最后一个。

关于思考题:

1.

试下这段代码:

代码语言:javascript
复制
lst = [1, 2, 3, 4, 5, 6]
for i in lst:
    if i < 5:
        lst.remove(i)
print lst

结果似乎应该是 [5, 6]?但实际输出却是 [2, 4, 5, 6]。这是因为 for 循环中每一次执行完毕后,都会去找下一个元素,进行下一次循环。但如果删除当前元素,当前元素位置的下一个元素就变成了原本的下下一个元素,因而跳过了一个元素。这是在遍历列表删除元素时常会遇到的问题。在遇到这种情况时,切记不要直接删除正在遍历的列表,而且通过其他方式来处理,比如另开一个列表,或者把待删除的元素记录下来,遍历完之后再统一删除。

2.

lst2 = lst,并没有产生一个新列表,只是相当于给 lst 起了个别名叫 lst2,所以结果会和直接操作 lst 一样。当你想对一个列表进行拷贝时,需要用 [:]。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Crossin的编程教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档