队列
我们为了实现队列抽象数据类型创建一个新类。和前面一样,我们将使用列表集合来作为构建队列的内部表示。
我们需要确定列表的哪一端作为队首,哪一端作为队尾。
如下所示的实现假定队尾在列表中的位置为 0。
这允许我们使用列表上的插入函数向队尾添加新元素。弹出操作可用于删除队首的元素(列表的最后一个元素)。回想一下,这也意味着入队为 O(n),出队为 O(1)。
1 2 3 4 5 6 7 8 9 10 11
| class Queue(): def __init__(self): self.items=[] def isEmpty(self): return self.items == [] def enqueue(self,item): self.items.insert(0,item) def dequeue(self): return self.items.pop() def size(self): return len(self.items)
|
初始化队列以及基础应用
1 2
| q.enqueue(1) q.dequeue()
|
1 2
| for i in range(10): q.enqueue(i)
|
1 2
| while not q.isEmpty(): print(q.dequeue())
|
队列的应用
烫手山芋
队列的典型应用之一是模拟需要以 FIFO 方式管理数据的真实场景。首先,让我们看看孩子们的游戏烫手山芋,在这个游戏中,孩子们围成一个圈,并尽可能快的将一个山芋递给旁边的孩子。在某一个时间,动作结束,有山芋的孩子从圈中移除。游戏继续开始直到剩下最后一个孩子。
为了模拟这个圈,我们使用队列。假设拿着山芋的孩子在队列的前面。当拿到山芋的时候,这个孩子将先出列再入队列,把他放在队列的最后。经过 num 次的出队入队后,前面的孩子将被永久移除队列。并且另一个周期开始,继续此过程,直到只剩下一个名字(队列的大小为 1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def hotPotato(namelist, num): simqueue = Queue() for name in namelist: simqueue.enqueue(name)
while simqueue.size() > 1: for i in range(num): simqueue.enqueue(simqueue.dequeue())
simqueue.dequeue()
return simqueue.dequeue()
print(hotPotato(["Bill","David","Susan","Jane","Kent","Brad"],7))
|
请注意,在此示例中,计数常数的值大于列表中的名称数。这不是一个问题,因为队列像一个圈,计数会重新回到开始,直到达到计数值。另外,请注意,列表加载到队列中以使列表上的名字位于队列的前面。在这种情况下,Bill 是列表中的第一个项,因此他在队列的前面。
打印机
主要模拟步骤
- 创建打印任务的队列,每个任务都有个时间戳。队列启动的时候为空。
- 每秒(currentSecond):
- 是否创建新的打印任务?如果是,将 currentSecond 作为时间戳添加到队列。
- 如果打印机不忙并且有任务在等待
- 从打印机队列中删除一个任务并将其分配给打印机
- 从 currentSecond 中减去时间戳,以计算该任务的等待时间。
- 将该任务的等待时间附件到列表中稍后处理。
- 根据打印任务的页数,确定需要多少时间。
- 打印机需要一秒打印,所以得从该任务的所需的等待时间减去一秒。
- 如果任务已经完成,换句话说,所需的时间已经达到零,打印机空闲。
- 模拟完成后,从生成的等待时间列表中计算平均等待时间。
为了设计此模拟,我们将为上述三个真实世界对象创建类:Printer, Task, PrintQueue
Printer 类需要跟踪它当前是否有任务。
如果有,则它处于忙碌状态(13-17 行),并且可以从任务的页数计算所需的时间。
构造函数允许初始化每分钟页面的配置,tick 方法将内部定时器递减直到打印机设置为空闲(11 行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Printer: def __init__(self, ppm): self.pagerate = ppm self.currentTask = None self.timeRemaining = 0
def tick(self): if self.currentTask != None: self.timeRemaining = self.timeRemaining - 1 if self.timeRemaining <= 0: self.currentTask = None
def busy(self): if self.currentTask != None: return True else: return False
def startNext(self,newtask): self.currentTask = newtask self.timeRemaining = newtask.getPages() * 60/self.pagerate
|
Task 类表示单个打印任务。创建任务时,随机数生成器将提供 1 到 20 页的长度。我们选择使用随机模块中的 randrange 函数
每个任务还需要保存一个时间戳用于计算等待时间。此时间戳将表示任务被创建并放置到打印机队列中的时间。可以使用 waitTime 方法来检索在打印开始之前队列中花费的时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import random
class Task: def __init__(self,time): self.timestamp = time self.pages = random.randrange(1,21)
def getStamp(self): return self.timestamp
def getPages(self): return self.pages
def waitTime(self, currenttime): return currenttime - self.timestamp
|
以下代码实现了上述算法。PrintQueue 对象是我们现有队列 ADT 的一个实例。
newPrintTask 决定是否创建一个新的打印任务。我们再次选择使用随机模块的 randrange 函数返回 1 到 180 之间的随机整数。
打印任务每 180 秒到达一次。通过从随机整数(32 行)的范围中任意选择,我们可以模拟这个随机事件。
模拟功能允许我们设置打印机的总时间和每分钟的页数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| def simulation(numSeconds, pagesPerMinute):
labprinter = Printer(pagesPerMinute) printQueue = Queue() waitingtimes = []
for currentSecond in range(numSeconds):
if newPrintTask(): task = Task(currentSecond) printQueue.enqueue(task)
if (not labprinter.busy()) and (not printQueue.isEmpty()): nexttask = printQueue.dequeue() waitingtimes.append(nexttask.waitTime(currentSecond)) labprinter.startNext(nexttask)
labprinter.tick()
averageWait=sum(waitingtimes)/len(waitingtimes) print("Average Wait %6.2f secs %3d tasks remaining."%(averageWait,printQueue.size()))
def newPrintTask(): num = random.randrange(1,181) if num == 180: return True else: return False
|
当我们运行模拟时,我们不应该担心每次的结果不同。这是由于随机数的概率性质决定的。 因为模拟的参数可以被调整,我们对调整后可能发生的趋势感兴趣。 这里有一些结果。
首先,我们将使用每分钟五页的页面速率运行模拟 60 分钟(3,600秒)。 此外,我们将进行 10 次独立试验。记住,因为模拟使用随机数,每次运行将返回不同的结果。
1 2
| for i in range(10): simulation(3600,5)
|
1 2 3 4 5 6 7 8 9 10
| Average Wait 133.21 secs 0 tasks remaining. Average Wait 69.95 secs 1 tasks remaining. Average Wait 18.21 secs 0 tasks remaining. Average Wait 156.15 secs 1 tasks remaining. Average Wait 124.05 secs 1 tasks remaining. Average Wait 194.70 secs 4 tasks remaining. Average Wait 48.24 secs 1 tasks remaining. Average Wait 80.65 secs 0 tasks remaining. Average Wait 62.31 secs 0 tasks remaining. Average Wait 85.43 secs 2 tasks remaining.
|
你的支持是我写作的莫大鼓励