1. 程式人生 > >程式設計之美---電梯排程演算法

程式設計之美---電梯排程演算法

在看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