Python的數(shù)據(jù)結(jié)構(gòu)與算法的隊列詳解(3)
模擬打印機(jī)任務(wù)隊列過程
計算機(jī)科學(xué)中也有眾多的隊列例子。比如計算機(jī)實驗室有10臺計算機(jī),它們都與同一臺打印機(jī)相連。當(dāng)學(xué)生需要打印的時候,他們的打印任務(wù)會進(jìn)入一個隊列。該隊列中的第一個任務(wù)就是即將執(zhí)行的打印任務(wù)。如果一個任務(wù)排在隊列的最后面,那么它必須等到所有前面的任務(wù)都執(zhí)行完畢后才能執(zhí)行。?
學(xué)生向共享打印機(jī)發(fā)送打印請求,這些打印任務(wù)被存在一個隊列中,并且按照先到先得的順序執(zhí)行。這樣的設(shè)定可能導(dǎo)致很多問題。其中最重要的是,打印機(jī)能否處理一定量的工作。如果不能,學(xué)生可能會由于等待過長時間而錯過要上的課。?
考慮計算機(jī)實驗室里的這樣一個場景:在任何給定的一小時內(nèi),實驗室里都有10個學(xué)生。他們在這 一小時內(nèi)最多打印2次,并且打印的頁數(shù)從1到20頁不等。實驗室的打印機(jī)比較老舊,每分鐘只能以低質(zhì)量打印10頁。也可以將打印質(zhì)量調(diào)高,但是這樣做會導(dǎo)致打印機(jī)每分鐘只能打印5頁。降低打印速度可能導(dǎo)致學(xué)生等待過長時間。那么,應(yīng)該如何設(shè)置打印速度呢??
可以通過構(gòu)建一個模型來解決該問題。我們需要為學(xué)生、打印任務(wù)和打印機(jī)構(gòu)建對象。當(dāng)學(xué)生提交打印任務(wù)時,我們需要將它們加入打印機(jī)的任務(wù)隊列中。當(dāng)打印機(jī)執(zhí)行完一個任務(wù)后,它會檢查該隊列,看看其中是否還有需要處理的任務(wù)。我們感興趣的是學(xué)生平均需要等待多久才能拿到打印好的文章。這個時間等于打印任務(wù)在隊列中的平均等待時間。?
在模擬時,需要應(yīng)用一些概率學(xué)知識。舉例來說,學(xué)生打印的文章可能有1~20頁。如果各頁數(shù)出現(xiàn)的概率相等,那么打印任務(wù)的實際時長可以通過模擬1~20的一個文章頁數(shù)隨機(jī)數(shù)來計算得出。?
如果實驗室里有10個學(xué)生,并且在一小時內(nèi)每個人都打印兩次,那么每小時平均就有20個打印任務(wù)。 在任意一秒,創(chuàng)建一個打印請求的概率是多少? 回答這個問題需要考慮任務(wù)與時間的比值。每小時20個任務(wù)相當(dāng)于每180秒1個任務(wù)。?
可以通過1~180的一個隨機(jī)數(shù)來模擬每秒內(nèi)產(chǎn)生打印請求的概率(1/180的概率)。如果隨機(jī)數(shù)正好是180,那么就認(rèn)為有 一個打印請求被創(chuàng)建。注意,可能會出現(xiàn)多個請求接連被創(chuàng)建的情況,也可能很長一段時間內(nèi)都沒有請求。這就是模擬的本質(zhì)。我們希望在常用參數(shù)已知的情況下盡可能準(zhǔn)確地模擬。?
主要模擬步驟:
1.創(chuàng)建一個打印任務(wù)隊列。每一個任務(wù)到來時都會有一個時間戳。一開始,隊列是空的。
2.針對每一秒(currentSecond),執(zhí)行以下操作。
- 是否有新創(chuàng)建的打印任務(wù)?如果是,以 currentSecond 作為其時間戳并將該任務(wù)加入到隊列中。
- 如果打印機(jī)空閑,并且有正在等待執(zhí)行的任務(wù),執(zhí)行以下操作:
- 從隊列中取出第一個任務(wù)并提交給打印機(jī);
- 用 currentSecond 減去該任務(wù)的時間戳,以此計算其等待時間;
- 將該任務(wù)的等待時間存入一個列表,用來作為計算平均等待時間的數(shù)據(jù);
- 根據(jù)該任務(wù)的頁數(shù),計算執(zhí)行時間。
- 打印機(jī)進(jìn)行一秒的打印,同時從該任務(wù)的執(zhí)行時間中減去一秒。
- 如果打印任務(wù)執(zhí)行完畢,即任務(wù)的執(zhí)行時間減為0,則說明打印機(jī)回到空閑狀態(tài)。
3.當(dāng)模擬完成之后,根據(jù)等待時間列表中的值計算平均等待時間。
?構(gòu)建隊列程序
class Queue: def __init__(self): self.items = [] # 構(gòu)建空隊列 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 # 模擬打印機(jī) class Printer: # 打印機(jī)初始化 def __init__(self, ppm): self.pagerate = ppm # 打印速度 頁/分鐘 self.currentTask = None # 現(xiàn)有任務(wù) self.timeRemain = 0 # 該任務(wù)所需時間 # 打印任務(wù)倒計時 0代表打印完成 def tick(self): # 如果打印機(jī)正在執(zhí)行任務(wù) if self.currentTask != None: # 該任務(wù)執(zhí)行時間 = 執(zhí)行時間 - 1(執(zhí)行時間倒計時) self.timeRemaining = self.timeRemaining - 1 if self.timeRemaining <= 0: # 該任務(wù)執(zhí)行時間 <= 0 self.currentTask = None # 該任務(wù)執(zhí)行完畢 # 判斷打印機(jī)是否空閑 def busy(self): if self.currentTask != None: return True else: return False # 打印機(jī)接受新任務(wù) def startNext(self, newtask): self.currentTask = newtask # 新打印任務(wù)需要時間 = 新任務(wù)頁數(shù) * (60 / 每分鐘打印多少頁的速度) # (60 / 每分鐘打印多少頁的速度) = 每打印一頁所需要的秒數(shù) self.timeRemaining = newtask.getPages() * (60 / self.pagerate) # 模擬單個任務(wù)的屬性 class Task: # 任務(wù)初始化 def __init__(self, time): self.timestamp = time # 創(chuàng)建任務(wù)的時間點 self.pages = random.randrange(1, 21) # 任務(wù)頁數(shù) 在 1~20 間隨機(jī)生成 def getStamp(self): return self.timestamp # 獲取任務(wù)創(chuàng)建的時間點 def getPages(self): return self.pages # 獲取任務(wù)的頁數(shù) def waitTime(self, currenttime): # 任務(wù)的等待時間 = 當(dāng)前時間 - 任務(wù)創(chuàng)建的時間點 return currenttime - self.timestamp # 模擬學(xué)生創(chuàng)建的新打印請求 def newPrintTask(): # 打印請求是一個隨機(jī)事件 # 通過1~180之間的一個隨機(jī)數(shù)來模擬每秒內(nèi)產(chǎn)生打印請求的概率 # 如果隨機(jī)數(shù)正好是180,那么就認(rèn)為有一個打印請求被創(chuàng)建。 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 個任務(wù)等待處理" % (averageWait, printQueue.size()))
模擬打印過程(有注釋)
def simulation(numSeconds, pagesPerMinute): # numSeconds-時間段 # pagesPerMinute-打印速度,頁/分鐘 labprinter = Printer(pagesPerMinute) # 創(chuàng)建打印機(jī) printQueue = Queue() # 創(chuàng)建打印機(jī)任務(wù)隊列 waitingtimes = [] # 創(chuàng)建等待時間數(shù)據(jù)樣本列表 for currentSecond in range(numSeconds): # 一次循環(huán)代表一秒 if newPrintTask(): # 如果 有打印請求創(chuàng)建 task = Task(currentSecond) # 創(chuàng)建打印任務(wù)并記錄當(dāng)前時間點 printQueue.enqueue(task) # 打印任務(wù)進(jìn)入打印機(jī)任務(wù)隊列 # 如果 打印機(jī)空閑 并且 打印機(jī)任務(wù)隊列有任務(wù) if(not labprinter.busy())and(not printQueue.isEmpty()): nexttask = printQueue.dequeue() # 從隊列取出新任務(wù) # 根據(jù)當(dāng)前時間點計算新任務(wù)在任務(wù)隊列里的等待時間 并將等待時間記錄進(jìn)樣本列表 waitingtimes.append(nexttask.waitTime(currentSecond)) labprinter.startNext(nexttask) # 開始執(zhí)行新任務(wù) 打印機(jī)進(jìn)入忙碌狀態(tài) labprinter.tick() # 每循環(huán)一次 當(dāng)前打印任務(wù)執(zhí)行倒計時減少一秒 averageWait = sum(waitingtimes)/len(waitingtimes) print("平均等待 %6.2f 秒,還有 %3d 個任務(wù)等待處理" % (averageWait, printQueue.size()))
需要注意的是,時間戳是我們根據(jù)循環(huán)模擬出來的,我們給定了 numSeconds 時間段后,每循環(huán)一次相當(dāng)于時間過了一秒。
雖然每次模擬的結(jié)果不一定相同。但對此我們不需要在意。這是由于隨機(jī)數(shù)的本質(zhì)導(dǎo)致的。我們感興趣的是當(dāng)參數(shù)改變時結(jié)果出現(xiàn)的趨勢。?
下面是一些結(jié)果:
我們根據(jù)模擬得到了打印機(jī)在兩種速度下,一小時內(nèi)的任務(wù)執(zhí)行情況的參考數(shù)據(jù)。可以很明顯的看到,當(dāng)打印質(zhì)量提升后,學(xué)生平均等待時間相比低質(zhì)量情況下顯著增加,并且任務(wù)處理未完成的次數(shù)也出現(xiàn)了增加,所以設(shè)置打印機(jī)為低質(zhì)量模式是最合適的。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
python使用xlrd實現(xiàn)檢索excel中某列含有指定字符串記錄的方法
這篇文章主要介紹了python使用xlrd實現(xiàn)檢索excel中某列含有指定字符串記錄的方法,涉及Python使用xlrd模塊檢索Excel的技巧,非常具有實用價值,需要的朋友可以參考下2015-05-05集調(diào)試共享及成本控制Prompt工具PromptLayer使用指南
這篇文章主要介紹了集調(diào)試共享及成本控制Prompt工具PromptLayer使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Python+OpenCV實現(xiàn)分水嶺分割算法的示例代碼
分水嶺算法是用于分割的經(jīng)典算法,在提取圖像中粘連或重疊的對象時特別有用。本文將用Python+OpenCV實現(xiàn)這一算法,需要的可以參考一下2022-08-08python抓取網(wǎng)頁時字符集轉(zhuǎn)換問題處理方案分享
python學(xué)習(xí)過程中發(fā)現(xiàn)英文不好學(xué)起來挺困難的,其中小弟就遇到一個十分蛋疼的問題,百度了半天就沒找到解決辦法~囧~摸索了半天自己解決了,記錄下來與君共勉。2014-06-06Python 普通最小二乘法(OLS)進(jìn)行多項式擬合的方法
今天小編就為大家分享一篇Python 普通最小二乘法(OLS)進(jìn)行多項式擬合的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12