線性結構佇列以及應用(上)
阿新 • • 發佈:2020-05-31
### 佇列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(