程式設計之美---電梯排程演算法
在看linux 0.11版本的塊裝置驅動部分,裡面提到了電梯演算法,總結下幾種尋道的方式。
第一種:最為原始的先到先服務(first come first served)的演算法。假設此時我們正在第11道讀取資料,然後陸陸續續有其他程序來要求我們提供磁碟內容給他們。這裡我們把要讀取的柱面按照程序提出要求的順序記錄下來,比如1, 36, 16, 34, 9, 12,那麼嚴格按照先到先服務原則,我們下一個要去的柱面是1號,中間要經歷10個柱面,然後是36號....... 等全部讀下來,我們統計下,一共要"跑過"111個柱面,這個演算法效率太低。
第二種是最短尋道演算法(shortest seek first) 這種演算法有點類似貪心,即是每次我們選擇距離我們現在所處的點最近的一個點(柱面)。如下圖,若當前我們正好執行完對於11號塊的讀取,下一個最近的是12號塊,那麼我們讀取12號塊的資料,接著讀取16號塊...... 我們看到如果用這種演算法的話,我們經過的方塊號碼 12, 9, 16, 1, 34, 36 這樣我們總共的經歷的柱面數為 61塊,這樣我們大大節省了尋道時間。 這個演算法本來已經很好了,不過我們不得不面臨這樣一個問題: 現在我們正在讀取16號塊,馬上要讀取1號塊了,這是一個程序闖進來要求我們為他提供20號塊的資訊,20號距離16號比較近,那我們就去二十號吧,然後我們又接到通知要23號資料.....假如一直有更接近的請求過來那1號很可能需要很久才能讀取它內容!所以這裡我們需要一種演算法來平衡效率和公平性,避免上述過程中的飢餓,所以我們引進了電梯演算法。
第三種電梯演算法:我們需要做一個標記,標記現在是向數字大的方向讀,還是方向小的。如果現在是向前(數字大)讀,那麼我們就需要一直讀下去,一直到最尾一個。同理向後讀。這個演算法如下圖所示: 一種平衡效率和公平性的演算法(不要讓餓死)
總結:①先到先服務:保證了公平性,但效率低;②最短尋道演算法:效率高,但不能保證公平性(有可能餓死)③電梯演算法:平衡了效率和公平性
---------------------
出處:https://blog.csdn.net/heiworld/article/details/27978521
程式設計之美-電梯排程演算法
大部分參考文章http://www.cppblog.com/jake1036/archive/2011/06/29/149720.html(將它的c語言改寫成了java版)
一問題描述:
所有的員工均在1樓進電梯的時候,選擇所要到達的樓層。
然後計算出停靠的樓層i,當到達樓層i的時候,電梯停止。
所有人走出電梯,步行到所在的樓層中。
求所有人爬的樓層數目和的最小值。
二 問題解決方法:
解決方案:
(1)使用簡單的方法,直接將樓層從1到n開始遍歷
sum(person[i] * |i - j| ) 此表示式為一個雙重迴圈,i與j均為1-n的迴圈。
j下標表示電梯停靠的樓層。
person陣列表示,對應i層的下電梯的人數。此演算法負責度為o(n*n)
對應的j是上述和為最小的一層即為所求。 上面的演算法複雜度為o(n)
(2)下面考慮一個簡單的演算法,使其複雜度達到o(n)
考慮假如電梯停靠在某一樓層i處,假設在i處下樓的客人為N2,
在i以上樓層的客人數目為N3 ,在i一下樓層的客人數目為N1。
且將電梯在i層停止時,全部人員的路程之和記為T。
那麼加入電梯在i-1層停的話,則原來i層之上的人需要多爬一層,即增加了N3
第i層的人需要多爬一層,則結果增加了N2, i層之下的人則少爬了一層,結果減去N1
所以第i-1層的結果為 T - N1 + N2 + N3 。即結果可以即為 T -(N1 - N2 - N3)
下面考慮在i+1層的結果,若電梯在i+1層停止的話,原來i層之上的客戶都會少爬一層,
則結果減少N3 ,而i層之下的人員則都會多爬一層即增加了N1 ,第i層的人員都會多爬一層
即為增加了N2 。則結果為 T + N1 + N2 - N3
綜上我們得出,
(1)若N1 > N2 + N3的時候, 我們在第i-1層 選擇電梯停止最好。
(2)若N1 + N2 < N3的時候, 我們選擇在第i+1層停止電梯最好。
下面我們可以先計算出來當i=1時候的T ,然後判斷是否需要在i+1層停止,若是i+1層的花費
大於i層,則我們可以繼續計算,否則退出。
三 程式碼如下:
public class DianTi { static final int N = 10 ; static int person[] = {0, 2, 5 , 7 , 3 , 5 , 2 , 6, 2 , 6 , 3}; public static void main(String[] args) { System.out.print(compute(person)+" "+compute2(person)); } public static int compute(int[] person) { //先計算出在第一層停止的時候 所需要的花費 int T = 0; int N1 = 0 ; //在第一層以下下的人數 int N2 = person[1] ; //在第一層處下的人數 int N3 = 0 ; //在第一層之上下電梯的人數 int floor = 1 ; for(int i = 2 ; i <= N ;i++) //先計算出第1層停止需要爬取的樓層數目 { T += person[i] * (i - 1) ; N3 += person[i] ; } for(int i = 2 ; i <= N ;i++) { if(N1 + N2 <= N3) //說明第i+1層的結果會大於第i層 { T += N1 + N2 - N3 ; N1 += N2 ; N2 = person[i] ; N3 -= person[i] ; floor = i ; } else //否則第i層的結果已經最小,故不需要計算第i+1層 break ; } return floor ; } public static int compute2(int[] person) { int tempfloor = 0 ; int min = 6553 ;//儲存最小值 int floor = 1 ;//儲存停靠的樓層 int j; for(int i = 1 ; i <= N ;i++) //表示第i樓層電梯停靠 { tempfloor = 0 ; for(j = 1 ; j < N ;j++) tempfloor += Math.abs((i - j)) * person[j] ; if(min > tempfloor) { min = tempfloor ; floor = i ; } } return floor ; } }
結果如下:
5 5