【数据结构与算法Python版学习笔记】基本数据结构——队列 Queue

定义

  • 一系列有序的元素的集合,新元素的加入在队列的一端,这一端叫做“队尾”(rear),已有元素的移除发生在队列的另一端,叫做“队首”(front)。

  • 当一个元素被加入到队列之后,它就从队尾开始向队首前进,直到它成为下一个即将被移出队列的元素。

  • “先进先出”(FIFO, first-in first-out)

  • 只有一个入口和一个出口

举例

  • 排队看电影
  • 在杂货店里排队等着付钱
  • 在自助餐厅里我们也排队
  • 打印
  • 计算机科学中队列的例子
    • 进程调度
    • 键盘缓冲

抽象数据类型Queue

  • Queue() 创建一个空队列对象,无需参数,返回空的队列;

  • enqueue(item) 将数据项添加到队尾,无返回值;

  • dequeue() 从队首移除数据项,无需参数,返回值为队首数据项;

  • isEmpty() 测试是否为空队列,无需参数,返回值为布尔值;

  • size() 返回队列中的数据项的个数,无需参数。

enqueue 的复杂度是 O(n),而 dequeue 的复杂度是 O(1)

采用List容纳Queue的数据项

class Queue:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        return self.items == []

    def enqueue(self, item):
        return self.items.insert(0, item)

    def dequeue(self):
        return self.items.pop()

    def size(self):
        return len(self.items)

应用

热土豆(约瑟夫)问题

算法

  • 模拟程序采用队列来存放所有参加游戏的人名,按照传递土豆方向从队首排到队尾
  • 模拟游戏开始,只需要将队首的人出队,随即再到队尾入队,算是土豆的一次传递

代码

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()

if __name__ == "__main__":
    print(hotPotato(["Bill","David","Susan","Jane","Kent","Brad"],7))

打印任务

建模

  • 对象
    • 打印任务属性
      • 提交时间
      • 打印页数
    • 打印队列属性
      • 具有FIFO性质的打印任务队列
    • 打印机属性
      • 打印速度
      • 是否忙
  • 过程
    • 生成和提交打印任务

      • 确定生产概率
        每小时会有10个学生提交20个作业
      • 确定打印页数
        草稿10页/min,高质量 5页/min

      image

    • 实施打印

      • 当前的打印作业
      • 打印结果倒计时
    • 模拟时间

      • 统一时间框架
        以最小单位(秒)均匀流逝的时间,设定结束时间
      • 同步所有过程
        在一个时间单位里,对生成打印任务和实施打印两个过程个处理一次

模拟流程

  1. 创建一个打印任务队列。每个任务在生成时被赋予一个“时间邮戳”。队列在开始时是空的。
  2. 对于每一秒(打印过程中的当前秒(currentSecond):
    1. 是否有新的打印任务生成?如果有,把它加入打印队列,并把当前秒作为其“时间邮戳”。
    2. 如果打印机空闲并且有任务正在等待队列中:
      1. 从打印队列中移除下一个打印任务并且将其提交给打印机;
      2. 从当前秒中减去“时间邮戳”值,计算得到该任务的等待时间;
      3. 将该任务的等待时间添加到一个列表中,以用于后续操作;
      4. 基于打印任务的页数,求出需要多长的打印时间。
    3. 如果此时打印机在工作中,那对于打印机而言,就工作了一秒钟;对于打印任务而言,它离打印结束又近了一秒钟(剩余打印时间减 1)。
    4. 如果此时打印任务已经完成,也即是剩余打印时间为 0 时,打印机就进入空闲状态。
  3. 在整个模拟算法完成后,依据生成的等待时间列表中的数据,计算平均打印时间。

代码

import random
class Printer():
    """打印机类"""
    def __init__(self,ppm):
        self.pagerate=ppm # 打印速度
        self.currentTask=None # 打印任务
        self.timeRemaining=0
    
    def tick(self): 
        """打印1秒"""
        if self.currentTask!=None:
            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

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
        
def newPrintTask():
    """1/180 概率生成作业"""
    num = random.randrange(1,181)
    if num == 180:
        return True
    else:
        return False

# 
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()))

if __name__ == "__main__":
    print("-------------ppm=5-----------")
    for i in range(10):
        simulation(3600,5)
    print()
    print("-------------ppm=10-----------")
    for i in range(10):
        simulation(3600,10)

>>>
-------------ppm=5-----------
Average Wait 107.60 secs   0 tasks remaining
Average Wait 101.47 secs   3 tasks remaining
Average Wait  72.62 secs   0 tasks remaining
Average Wait 100.61 secs   3 tasks remaining
Average Wait 115.65 secs   0 tasks remaining
Average Wait 144.67 secs   0 tasks remaining
Average Wait  36.50 secs   0 tasks remaining
Average Wait 125.72 secs   5 tasks remaining
Average Wait  62.21 secs   0 tasks remaining
Average Wait  15.42 secs   0 tasks remaining

-------------ppm=10-----------
Average Wait   7.29 secs   0 tasks remaining
Average Wait  29.45 secs   0 tasks remaining
Average Wait  26.29 secs   0 tasks remaining
Average Wait  18.06 secs   1 tasks remaining
Average Wait  27.65 secs   0 tasks remaining
Average Wait   7.94 secs   0 tasks remaining
Average Wait   7.25 secs   0 tasks remaining
Average Wait  39.70 secs   0 tasks remaining
Average Wait  31.45 secs   0 tasks remaining
Average Wait  22.76 secs   0 tasks remaining

讨论

  • 为了对打印模式设置进行决策,我们用模拟程序来评估任务等待时间
  • 模拟系统对现实的仿真
    • 不耗费现实资源的情况下(有时真实的实验无法进行),可以以不同设定,反复多次模拟来帮助我们进行决策
posted @ 2021-04-22 13:11  砥才人  阅读(211)  评论(0编辑  收藏  举报