Python的数据结构与算法的队列详解(3)

目录
  • 模拟打印机任务队列过程
  • 主要模拟步骤:
    • ​构建队列程序
    • 模拟打印程序
    • 模拟打印过程(有注释)
  • 总结

模拟打印机任务队列过程

计算机科学中也有众多的队列例子。比如计算机实验室有10台计算机,它们都与同一台打印机相连。当学生需要打印的时候,他们的打印任务会进入一个队列。该队列中的第一个任务就是即将执行的打印任务。如果一个任务排在队列的最后面,那么它必须等到所有前面的任务都执行完毕后才能执行。​

学生向共享打印机发送打印请求,这些打印任务被存在一个队列中,并且按照先到先得的顺序执行。这样的设定可能导致很多问题。其中最重要的是,打印机能否处理一定量的工作。如果不能,学生可能会由于等待过长时间而错过要上的课。​

考虑计算机实验室里的这样一个场景:在任何给定的一小时内,实验室里都有10个学生。他们在这 一小时内最多打印2次,并且打印的页数从1到20页不等。实验室的打印机比较老旧,每分钟只能以低质量打印10页。也可以将打印质量调高,但是这样做会导致打印机每分钟只能打印5页。降低打印速度可能导致学生等待过长时间。那么,应该如何设置打印速度呢?​

可以通过构建一个模型来解决该问题。我们需要为学生打印任务打印机构建对象。当学生提交打印任务时,我们需要将它们加入打印机的任务队列中。当打印机执行完一个任务后,它会检查该队列,看看其中是否还有需要处理的任务。我们感兴趣的是学生平均需要等待多久才能拿到打印好的文章。这个时间等于打印任务在队列中的平均等待时间。​

在模拟时,需要应用一些概率学知识。举例来说,学生打印的文章可能有1~20页。如果各页数出现的概率相等,那么打印任务的实际时长可以通过模拟1~20的一个文章页数随机数来计算得出。​

如果实验室里有10个学生,并且在一小时内每个人都打印次,那么每小时平均就有20个打印任务。 在任意一秒,创建一个打印请求的概率是多少? 回答这个问题需要考虑任务与时间的比值。每小时20个任务相当于每180秒1个任务。​

可以通过1~180的一个随机数来模拟每秒内产生打印请求的概率(1/180的概率)。如果随机数正好是180,那么就认为有 一个打印请求被创建。注意,可能会出现多个请求接连被创建的情况,也可能很长一段时间内都没有请求。这就是模拟的本质。我们希望在常用参数已知的情况下尽可能准确地模拟。​

主要模拟步骤:

1.创建一个打印任务队列。每一个任务到来时都会有一个时间戳。一开始,队列是空的。

2.针对每一秒(currentSecond),执行以下操作。

  • 是否有新创建的打印任务?如果是,以 currentSecond 作为其时间戳并将该任务加入到队列中。
  • 如果打印机空闲,并且有正在等待执行的任务,执行以下操作:
    • 队列中取出第一个任务并提交给打印机;
    • 用 currentSecond 减去该任务的时间戳,以此计算其等待时间
    • 将该任务的等待时间存入一个列表,用来作为计算平均等待时间的数据;
    • 根据该任务的页数,计算执行时间。
  • 打印机进行一秒的打印,同时从该任务的执行时间中减去一秒。
  • 如果打印任务执行完毕,即任务的执行时间减为0,则说明打印机回到空闲状态。

3.当模拟完成之后,根据等待时间列表中的值计算平均等待时间。

​构建队列程序

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)     # 队列(列表)长度

模拟打印程序

import random
# 模拟打印机
class Printer:
    # 打印机初始化
    def __init__(self, ppm):
        self.pagerate = ppm      # 打印速度 页/分钟
        self.currentTask = None  # 现有任务
        self.timeRemain = 0      # 该任务所需时间
    # 打印任务倒计时 0代表打印完成
    def tick(self):
        # 如果打印机正在执行任务
        if self.currentTask != None:
            # 该任务执行时间 = 执行时间 - 1(执行时间倒计时)
            self.timeRemaining = self.timeRemaining - 1
            if self.timeRemaining <= 0:     # 该任务执行时间 <= 0
                self.currentTask = None     # 该任务执行完毕
    # 判断打印机是否空闲
    def busy(self):
        if self.currentTask != None:
            return True
        else:
            return False
    # 打印机接受新任务
    def startNext(self, newtask):
        self.currentTask = newtask
        # 新打印任务需要时间 = 新任务页数 * (60 / 每分钟打印多少页的速度)
        # (60 / 每分钟打印多少页的速度) = 每打印一页所需要的秒数
        self.timeRemaining = newtask.getPages() * (60 / self.pagerate)

# 模拟单个任务的属性
class Task:
    # 任务初始化
    def __init__(self, time):
        self.timestamp = time                # 创建任务的时间点
        self.pages = random.randrange(1, 21) # 任务页数 在 1~20 间随机生成
    def getStamp(self):
        return self.timestamp  # 获取任务创建的时间点
    def getPages(self):
        return self.pages      # 获取任务的页数
    def waitTime(self, currenttime):
        # 任务的等待时间 = 当前时间 - 任务创建的时间点
        return currenttime - self.timestamp   

# 模拟学生创建的新打印请求
def newPrintTask():
    # 打印请求是一个随机事件
    # 通过1~180之间的一个随机数来模拟每秒内产生打印请求的概率
    # 如果随机数正好是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("平均等待 %6.2f 秒,还有 %3d 个任务等待处理"
          % (averageWait, printQueue.size()))

模拟打印过程(有注释)

def simulation(numSeconds, pagesPerMinute):
    # 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("平均等待 %6.2f 秒,还有 %3d 个任务等待处理" % (averageWait, printQueue.size()))

需要注意的是,时间戳是我们根据循环模拟出来的,我们给定了 numSeconds 时间段后,每循环一次相当于时间过了一秒。

虽然每次模拟的结果不一定相同。但对此我们不需要在意。这是由于随机数的本质导致的。我们感兴趣的是当参数改变时结果出现的趋势。​

下面是一些结果:


我们根据模拟得到了打印机在两种速度下,一小时内的任务执行情况的参考数据。可以很明显的看到,当打印质量提升后,学生平均等待时间相比低质量情况下显著增加,并且任务处理未完成的次数也出现了增加,所以设置打印机为低质量模式是最合适的。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 详解python数据结构之队列Queue

    一.前言 队列Queue是一种先进先出(FIFO,First In First Out)的线性表.允许一端进行插入(rear),对应的另一段进行删除(front). 本篇包含以下内容: (1)Queue的基本格式 (2)入队列en_queue (3)删除数据函数 de_queue 二.Queue的基本格式 class Queue(): def __init__(self,size): self.size = size self.front = -1 #设置front初始值,每出队列一个数据就加

  • Python数据结构与算法中的队列详解(1)

    目录 什么是队列? 构建一个队列 总结 什么是队列? 队列,与栈类似,是有序集合.添加操作发生在 “尾部”,移除操作只发生在 “头部”.新元素只从尾部进入队列,然后一直向前移动到头部,直到成为下一个被移除的元素.​ 最新添加的元素必须在队列的尾部等待,在队列中时间最长的元素则排在最前面.这种排序原则被称作FIFO(first-in first-out),即先进先出,也称先到先得.在日常生活中,我们经常排队,这便是最简单的队列例子.进电影院要排队,在超市结账要排队,买咖啡也要排队.好的队列只允许一

  • Python数据结构之队列详解

    目录 0. 学习目标 1. 队列的基本概念 1.1 队列的基本概念 1.2 队列抽象数据类型 1.3 队列的应用场景 2. 队列的实现 2.1 顺序队列的实现 2.2 链队列的实现 2.3 队列的不同实现对比 3. 队列应用 3.1 顺序队列的应用 3.2 链队列的应用 3.3 利用队列基本操作实现复杂算法 0. 学习目标 栈和队列是在程序设计中常见的数据类型,从数据结构的角度来讲,栈和队列也是线性表,是操作受限的线性表,它们的基本操作是线性表操作的子集,但从数据类型的角度来讲,它们与线性表又有

  • Python数据结构与算法中的队列详解(2)

    目录 传土豆 总结 传土豆 队列的一个典型方法是模拟需要以 FIFO 方式管理数据的真实场景.考虑这样一个游戏:传土豆.在这个游戏中,成员们围成一圈,并依次尽可能快地传递一个土豆.在某个时刻,大家停止传递,此时手里有土豆的成员就得退出游戏. 重复上述过程,直到只剩下一个成员. 我们将针对传土豆游戏实现通用的模拟程序.该程序接受一个名字列表和一个用于计数的常量 num ,并且返回最后剩下的那个人的名字. 我们使用队列来模拟一个环.即假设握着土豆的人位于队列的头部.在模拟传土豆的过程中,程序将这个人

  • Python 数据结构之队列的实现

    Python 队列 Queue 队列是一种先进先出(FIFO)的数据类型, 新的元素通过 入队 的方式添加进 Queue 的末尾, 出队 就是从 Queue 的头部删除元素. 用列表来做 Queue: queue = [] # 初始化一个列表数据类型对象, 作为一个队列 def enQ(): # 定义一个入栈方法 queue.append(raw_input('Enter New String: ').strip()) # 提示输入一个入队的 String 对象, 调用 Str.strip()

  • Python实现的数据结构与算法之队列详解

    本文实例讲述了Python实现的数据结构与算法之队列.分享给大家供大家参考.具体分析如下: 一.概述 队列(Queue)是一种先进先出(FIFO)的线性数据结构,插入操作在队尾(rear)进行,删除操作在队首(front)进行. 二.ADT 队列ADT(抽象数据类型)一般提供以下接口: ① Queue() 创建队列 ② enqueue(item) 向队尾插入项 ③ dequeue() 返回队首的项,并从队列中删除该项 ④ empty() 判断队列是否为空 ⑤ size() 返回队列中项的个数 队

  • Python的数据结构与算法之队列详解

    目录 模拟打印机任务队列过程 主要模拟步骤: ​构建队列程序 模拟打印程序 模拟打印过程(有注释) 总结 模拟打印机任务队列过程 计算机科学中也有众多的队列例子.比如计算机实验室有10台计算机,它们都与同一台打印机相连.当学生需要打印的时候,他们的打印任务会进入一个队列.该队列中的第一个任务就是即将执行的打印任务.如果一个任务排在队列的最后面,那么它必须等到所有前面的任务都执行完毕后才能执行.​ 学生向共享打印机发送打印请求,这些打印任务被存在一个队列中,并且按照先到先得的顺序执行.这样的设定可

  • Python的数据结构与算法的队列详解(3)

    目录 模拟打印机任务队列过程 主要模拟步骤: ​构建队列程序 模拟打印程序 模拟打印过程(有注释) 总结 模拟打印机任务队列过程 计算机科学中也有众多的队列例子.比如计算机实验室有10台计算机,它们都与同一台打印机相连.当学生需要打印的时候,他们的打印任务会进入一个队列.该队列中的第一个任务就是即将执行的打印任务.如果一个任务排在队列的最后面,那么它必须等到所有前面的任务都执行完毕后才能执行.​ 学生向共享打印机发送打印请求,这些打印任务被存在一个队列中,并且按照先到先得的顺序执行.这样的设定可

  • Python数据结构与算法之算法分析详解

    目录 0. 学习目标 1. 算法的设计要求 1.1 算法评价的标准 1.2 算法选择的原则 2. 算法效率分析 2.1 大O表示法 2.2 常见算法复杂度 2.3 复杂度对比 3. 算法的存储空间需求分析 4. Python内置数据结构性能分析 4.1 列表性能分析 4.2 字典性能分析 0. 学习目标 我们已经知道算法是具有有限步骤的过程,其最终的目的是为了解决问题,而根据我们的经验,同一个问题的解决方法通常并非唯一.这就产生一个有趣的问题:如何对比用于解决同一问题的不同算法?为了以合理的方式

  • c语言数据结构之栈和队列详解(Stack&Queue)

    目录 简介 栈 一.栈的基本概念 1.栈的定义 2.栈的常见基本操作 二.栈的顺序存储结构 1.栈的顺序存储 2.顺序栈的基本算法 3.共享栈(两栈共享空间) 三.栈的链式存储结构 1.链栈 2.链栈的基本算法 3.性能分析 四.栈的应用——递归 1.递归的定义 2.斐波那契数列 五.栈的应用——四则运算表达式求值 1.后缀表达式计算结果 2.中缀表达式转后缀表达式 队列 一.队列的基本概念 1.队列的定义 2.队列的常见基本操作 二.队列的顺序存储结构 1.顺序队列 2.循环队列 3.循环队列

  • C语言 数据结构与算法之字符串详解

    目录 串的定义 串的比较 串的抽象数据类型 串的初始化 相关定义初始化 定长类初始化 串的堆式顺序存储结构(Heap) 初始化堆字符串 赋值操作 比较两个堆字符串的大小 串的定义 零个或多个字符组成的有限序列 串的比较 串的比较实际上是在比较串中字符的编码 存在某个k < min(n,m),使得ai = bi (i = 1,2,3,4..k) 如果 ak < bk  -->  那么srt1 < srt2 (反之也成立) 除去相等的字符,在第一个不相等的字符位置以Ascii码进行比较

  • Python实现的数据结构与算法之快速排序详解

    本文实例讲述了Python实现的数据结构与算法之快速排序.分享给大家供大家参考.具体分析如下: 一.概述 快速排序(quick sort)是一种分治排序算法.该算法首先 选取 一个划分元素(partition element,有时又称为pivot):接着重排列表将其 划分 为三个部分:left(小于划分元素pivot的部分).划分元素pivot.right(大于划分元素pivot的部分),此时,划分元素pivot已经在列表的最终位置上:然后分别对left和right两个部分进行 递归排序. 其中

  • Python实现的数据结构与算法之链表详解

    本文实例讲述了Python实现的数据结构与算法之链表.分享给大家供大家参考.具体分析如下: 一.概述 链表(linked list)是一组数据项的集合,其中每个数据项都是一个节点的一部分,每个节点还包含指向下一个节点的链接. 根据结构的不同,链表可以分为单向链表.单向循环链表.双向链表.双向循环链表等.其中,单向链表和单向循环链表的结构如下图所示: 二.ADT 这里只考虑单向循环链表ADT,其他类型的链表ADT大同小异.单向循环链表ADT(抽象数据类型)一般提供以下接口: ① SinCycLin

  • Java数据结构与算法入门实例详解

    第一部分:Java数据结构 要理解Java数据结构,必须能清楚何为数据结构? 数据结构: Data_Structure,它是储存数据的一种结构体,在此结构中储存一些数据,而这些数据之间有一定的关系. 而各数据元素之间的相互关系,又包括三个组成成分,数据的逻辑结构,数据的存储结构和数据运算结构. 而一个数据结构的设计过程分成抽象层.数据结构层和实现层. 数据结构在Java的语言体系中按逻辑结构可以分为两大类:线性数据结构和非线性数据结构. 一.Java数据结构之:线性数据结构 线性数据结构:常见的

  • Javascript数据结构之栈和队列详解

    目录 前言 栈(stack) 栈实现 解决实际问题 栈的另外应用 简单队列(Queue) 队列实现 队列应用 - 树的广度优先搜索(breadth-first search,BFS) 优先队列 优先队列实现 线性数据结构实现优先队列 Heap(堆)数据结构实现优先队列 代码实现一个二叉堆 小顶堆在 React Scheduler 事务调度的包应用 最后 前言 我们实际开发中,比较熟悉的数据结构是数组.一般情况下够用了.但如果遇到复杂的问题,数组就捉襟见肘了.在解决一个复杂的实际问题的时候,选择一

随机推荐