1. 程式人生 > >線性結構佇列以及應用(上)

線性結構佇列以及應用(上)

### 佇列Queue:什麼是佇列? 對列是一種有次序的資料集合,其特徵是新資料項的新增總髮生在一端(通常稱為“尾rear”端),而現存資料項的移除總髮生在另一端(通常稱為“首front”端) 當資料項加入佇列,首先出現在隊尾,隨著隊首資料項的移除,它逐漸接近隊首。新加入的資料項必須在資料集末尾等待,而等待時間最長的資料項則是隊首。這種次序安排的原則稱為:先進先出。 佇列的例子出現在我們日常生活的方方面面:水管,排隊;佇列僅有一個入口和一個出口。不允許資料項直接插入隊中,也不允許從中間移除資料項。 ### 抽象資料型別Queue 抽象資料型別Queue是一個有次序的資料集合,資料項僅新增到“尾”端,而且僅從”首“端移除,Queue具有先進先出的操作次序。 * 抽象資料型別Queue由如下操作定義: > Queue():建立一個空佇列物件,返回值為Queue物件; > > enqueue(item):將資料項item新增到隊尾,無返回值; > > dequeue():從隊首移除資料項,返回值為隊首資料項,佇列被修改; > > isEmpty():測試是否空佇列,返回值為bool; > > size():返回佇列中資料項的個數。 | 佇列操作 | 佇列內容 | 返回值 | | ---------------- | ------------------ | ------------ | | q=Queue() | [] | Queue object | | q.isEmpty() | [] | True | | q.enqueue(4) | [4] | | | q.enqueue("dog") | ["dog",4] | | | q.enqueue(True) | [True,"dog",4] | | | q.size() | [True,"dog",4] | 3 | | q.isEmpty() | [True,"dog",4] | False | | q.enqueue("dog") | [8.4,True,"dog",4] | | | q.dequeue() | [8.4,True,"dog"] | 4 | | q.dequeue() | [8.4,True] | "dog" | | q.size() | [8.4,True] | 2 | ### Python實現Queue 採用List來容納Queue的資料項,將List首端作為佇列尾端,List的末端作為佇列的首端,enqueue()複雜度為O(n),dequeue()複雜度為O(1) ```python 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) ``` 注意:首尾倒過來的實現,複雜度也倒過來。 ### 佇列的應用:約瑟夫問題 傳說猶太人反叛羅馬人,落到困境,約瑟夫和39人決定殉難,坐成一圈,報數1-7,報到7的人由旁邊殺死,結果略瑟夫給自己安排了個位置,最後活了下來…… 模擬程式採用佇列來存放所有參加遊戲的人名,按照報數的方向從隊首排到隊尾,模擬遊戲開始,只需要將隊首的人出隊,隨機再到對尾入隊,算是一次報數完成,反覆n次後,將隊首的人移除,不再入隊,如此反覆,知道佇列中剩下一人。 ```python def JosephQuestion(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() ``` ### 佇列的應用:列印任務 有如下場景:多人共享一臺印表機,採取“先到先服務”的策略來執行列印任務,在這種設定下,一個首要問題就是:這種列印作業系統的容量有多大?在能夠接受的等待時間內,系統能夠容納多少使用者以多高頻率提交到少列印任務? 一個具體的例項配置如下: 一個實驗室,在任意的一個小時內,大約有10名學生在場,這一個小時中,每個人發起2次左右的列印,每次1-20頁。印表機的效能是:以草稿模式列印的話,每分鐘10頁,以正常模式列印的話,列印質量好,但是速度下降,每分鐘只能列印五頁。 問題:怎麼設定印表機的模式,讓大家都不會等太久的前提下,儘量提高列印質量?這是一個典型的決策支援問題,但無法通過規則直接計算,我們要用一段程式來模擬這種列印任務場景,然後對程式執行結果進行分析,以支援對印表機模式設定的決策。 如何對問題建模: 首先對問題進行抽象,確定相關的物件和過程。拋棄那些對問題實質沒有關係的學生性別,年齡,印表機型號,列印內容,智障大小等等眾多細節。 物件:列印任務,列印佇列,印表機。 > 列印任務的屬性:提交事件,列印頁數 > > 列印佇列的屬性:具有“先進先出”性質的列印任務佇列 > > 印表機的屬性:列印速度,是否忙 過程:生成和提交列印任務 >確定生成概率:例項為每小時會有10個學生提交的20個作業,這樣概率是每180秒會有1個作業生成並提交,概率為每秒1/180。 > >確定列印頁數:例項是1-20頁,那麼就是1-20頁之間概率相同。 過程:實施列印 > 當前的列印作業:正在列印的作業 > > 列印結束倒計時:新作業開始列印時開始倒計時,返回0表示列印完畢,可以處理下一個作業。 模擬時間: > 統一的時間框架:以最小單位(秒)均勻流逝的時間,設定結束時間 > > 同步所有過程:在一個時間單位裡,對生成列印任務和實施列印兩個過程各處理一次 #### 列印任務問題:流程模擬 建立列印佇列物件 時間按照秒的單位流逝 > 按照概率生成列印作業,加入列印佇列 > > 如果印表機空閒,且佇列不空,則取出隊首作業列印,記錄此作業等待時間 > > 如果印表機忙,則按照列印速度進行1秒列印 > > 如果當前作業列印完成,則印表機進入空閒 時間用盡,開始統計平均等待時間 作業的等待時間 > 生成作業時,記錄生成的時間戳 > > 開始列印時,當前時間減去生成時間即可 作業的列印時間 > 生成作業時,記錄作業的頁數 > > 開始列印時,頁數除以列印速度即可 ```python 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(): num = random.randrange(1, 181) if num == 180: return True else: return False def simulation(numSeconds, pagesPerMinute): # 模擬 labprinter = Printer(pagesPerMinute) printQueue = Queue() waittingtimes = [] for currentSecond in range(numSeconds): if newPrintTask(): task = Task(currentSecond) printQueue.enqueue(task) if not labprinter.busy() and not printQueue.isEmpty(): nexttask = printQueue.dequeue() waittingtimes.append(nexttask.waitTime(currentSecond)) labprinter.startNext(nexttask) labprinter.tick() averageWait = sum(waittingtimes) / len(waittingtimes) print("Average Wait %6.2f secs %3d tasks remaining" %(averageWait,printQueue.size(