这是崔斯特的第三十四篇原创文章
从约瑟夫问题
开始说起 (๑• . •๑)
约瑟夫问题
有 n 个囚犯站成一个圆圈,准备处决。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过 k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。
问题是,给定了n和k,一开始要站在什么地方才能避免被处决?
这个问题是以弗拉维奥·约瑟夫命名的,它是1世纪的一名犹太历史学家。他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人,他们将向罗马军队投降,不再自杀。约瑟夫斯把他的存活归因于运气或天意,他不知道是哪一个。
队列是什么
这道题有多种解法,这里先不说别的,要引出今天的主角——队列。队列的定义很好理解:
队列是项的有序结合,其中添加新项的一端称为队尾,移除项的一端称为队首。当一个元素从队尾进入队列时,一直向队首移动,直到它成为下一个需要移除的元素为止。
队列抽象数据类型由以下结构和操作定义。如上所述,队列被构造为在队尾添加项的有序集合,并且从队首移除。队列保持 FIFO 排序属性。 队列操作如下。
- Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。
- enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
- dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。
- isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。
- size() 返回队列中的项数。它不需要参数,并返回一个整数。
队列的Python算法实现
为了实现队列抽象数据类型创建一个新类
|
|
想明白了其实就是对 list 的简单操作
如何活到最后
那我们回到上面的问题,如果是你,你要如何选择并活到最后呢?
我们的程序将输入名称列表和一个称为 num
常量用于报数。它将返回以 num
为单位重复报数后剩余的最后一个人的姓名。
假设第一个人是a
。从他开始计数,a
将先出列再入队列,把他放在队列的最后。经过 num
次的出队入队后,前面的人将被永久移除队列。并且另一个周期开始,继续此过程,直到只剩下一个名字(队列的大小为 1)。
|
|
其他解法
比较简单的做法是用循环单链表模拟整个过程,时间复杂度是O(n*m)。如果只是想求得最后剩下的人,则可以用数学推导的方式得出公式。先看看模拟过程的解法。
|
|
作业
假设实验室里有一台打印机供学生共性。当学生向共享打印机发送打印任务时,任务被放置在队列中以便以先来先服务的方式被处理。如何才能通过python程序模拟的方式得到每次提交任务的平均等待时间呢?(平均等待时间不包括打印本身的时间,仅指在队列中排队的时间。)
我们假定:
- 学生们每次打印的页数在1到20页之间。
- 打印机平均每小时会收到20个打印请求,即平均每180秒1个请求。
- 每秒新增任务的可能性相等,即任务的产生为独立同分布
- 打印机的打印速度恒定。
挖坑,要一起来填吗?