獨立任務最優調度問題

來源:互聯網
上載者:User

問題:獨立任務最優調度,又稱雙機調度問題:用兩台處理機A和B處理n個作業。設第i個作業交給機器A處理時所需要的時間是a[i],若由機器B來處理,則所需要的時間是b[i]。現在要求每個作業只能由一台機器處理,每台機器都不能同時處理兩個作業。設計一個動態規划算法,使得這兩台機器處理完這n個作業的時間最短(從任何一台機器開工到最後一台機器停工的總的時間)。研究一個執行個體:n=6, a = {2, 5, 7, 10, 5, 2}, b = {3, 8, 4, 11, 3, 4}.

分析:當完成k個作業,設機器A花費了x時間,機器B所花費時間的最小值肯定是x的一個函數,設F[k][x]表示機器B所花費時間的最小值。則F[k][x]=Min{ F[k-1][x]+b[k], F[k-1][x-a[k]] };其中F[k-1][x]+b[k]表示第k個作業由機器B來處理(完成k-1個作業時機器A花費的時間仍是x),F[k-1][x-a[k]]表示第k個作業由機器A處理(完成k-1個作業時機器A花費的時間是x-a[k])。

      那麼單個點對較大值Max(x, F[k][x]),表示此時(即機器A花費x時間的情況下)所需要的總時間。而機器A花費的時間x是變化的,即x=0,1,2……x(max),(理論上x的取值是離散的,但為編程方便,設為整數連續的)由此構成了點對較大值序列。要求整體時間最短,取這些點對較大值序列中最小的即是。

    理解痛點在於B是A的函數運算式,也即動態規劃所在。花點時間,看懂運算式,加上思考,理解了這點一切OK,後面的編程實現完全依據這個思想。先用前兩個任務的枚舉樣本來協助理解。

樣本:前兩個作業樣本足矣。

初始化第一個作業:下標以0開始。

首先,機器A所花費時間的所有可能值範圍:0 <= x <= a[0].
設x<0時,設F[0][x]= ∞,則max(x, ∞)= ∞;記法意義見下。
x=0時,F[0][0]=3,則Max(0,3)=3,機器A花費0時間,機器B花費3時間,而此時兩個機器所需時間為3; 
x=1時,F[0][1]=3,Max(1,3)=3;
x=2時,F[0][2]=0,則Max(2,0)=2;

那麼上面的點對序列中,可以看出當x=2時,完成第一個作業兩台機器花費最少的時間為2,此時機器A花費2時間,機器B花費0時間。

來看第二個作業

       首先,x的取值範圍是:0 <= x <= (a[0] + a[1]).
       當x<0時,記F[1][x] = ∞;這個記法編程使用,因為數組下標不能小於0。在這裡的實際含義是:x是代表完成前兩個作業機器A的時間,a[1]是機器A完成第2個作業的時間,若x<a[1],則勢必第2個作業由機器B來處理,即在Min()中取前者。

x=0,則F[1][0]= Min{ F[0][0]+b[2], F[0][0-a[1]] }= Min{3+8,∞}=11,進而Max(0,11)=11;
x=1,則F[1][1]= Min{ F[0][1]+b[2], F[0][1-a[1]] }= Min{3+8,∞}=11,進而Max(11)=11;
x=2,則F[1][2]= Min{ F[0][2]+b[2], F[0][2-a[1]] }= Min{0+8,∞}=8,進而Max(2,8)=8;
x=3,則F[1][3]= Min{ F[0][3]+b[2], F[0][3-a[1]] }= Min{0+8,∞}=8,進而Max(3,8)=8;
x=4,則F[1][4]= Min{ F[0][4]+b[2], F[0][4-a[1]] }= Min{0+8,∞}=8,進而Max(4,8)=8;
x=5,則F[1][5]= Min{ F[0][5]+b[2], F[0][5-a[1]] }= Min{0+8,3}=3,進而Max(5,3)=5;
x=6,則F[1][6]= Min{ F[0][6]+b[2], F[0][6-a[1]] }= Min{0+8,3}=3,進而Max(6,3)=6;
x=7,則F[1][7]= Min{ F[0][7]+b[2], F[0][7-a[1]] }= Min{0+8,0}=0,進而Max(7,0)=7;

那麼上面的點對序列中,可以看出當x=5時,完成兩個作業兩台機器花費最少的時間為5,此時機器A花費5時間,機器B花費3時間。

演算法時間複雜度:按照上述思想,編程實現,結果如,演算法時間複雜度為O(n*Sum),其中Sum為a中所有元素之和與b中所有元素之和的最小值。王曉東《演算法設計與實驗題解》中提供的方法時間複雜度是O(m*m*n*n*n),其中m為a、b所有元素的最大值。
      比較:Sum必然遠遠小於n*m;所以n*Sum<n*n*m,相比之下差了O(n*m)都不止,看來這個思想還是蠻優秀的,竊喜……

      這個是去年演算法課上的一道作業,當時絞盡腦汁想了好幾天,中間不乏出現過投機的方法,因為這道題的資料設計比較巧合,貌似也得到了正確結果,但隨便時間序列換位一下即可發現錯誤。最搞笑的是助教在課堂上給出的答案正是投機取巧的錯誤答案。錯誤的解答如下,錯誤的原因對比下自己去想想吧。
     T=max(T,min(tai-1+ai,tbi-1+bi))其中tai表示做完第i個任務a的等待時間,同理tbi。
      Initial: T=tai=tbi=0;  
      i=1 ,T=max(0,min(0+2,0+3))=2;
      i=2 ,T=max(2,min(2+5,0+8))=7;
      i=3 ,T=max(7,min(7+7,0+4))=7;
      i=4 ,T=max(7,min(7+10,4+11))=15;
      i=5 ,T=max(15,min(7+5,15+3))=15;
      i=6 ,T=max(15,min(12+2,15+4))=15。

以下是我用C#編寫的程式碼,儲存了完成前K項任務的所有最優值,並記錄了完成機器的順序。編程思想完全基於前述分析,程式碼中並附有說明。用C++的同學把二維數組的寫法還有輸出等變換一下即可。為說明問題,並沒有進行過多代碼上的最佳化,還有改進之處。附送一張圖,輸出了一些數組的值(美觀起見,沒有輸出完),方便理解。


using System;
namespace zydd
{
    class Program
    {
        //獨立任務最優調度函數,參數為兩組任務和任務個數,最優時間和順序結果。
        static void DlrwZydd(int[] a, int[] b, int n, int[] least, string[] result)
        {
            //首先給一個大值。
            for (int i = 0; i < n; i++)
            {
                least[i] = 99;
            }

            //若任務只有一台機器完成,求得兩個時間。
            int aSum = 0, bSum = 0;
            for (int i = 0; i < n; i++)
            {
                aSum += a[i];
                bSum += b[i];
            }
            //小值加1作為數組的列數,減少儲存空間。
            int Sum = 1 + Math.Min(aSum, bSum);

            //建立四個行數列數相同的數組,timeA儲存機器A可能用的時間,timeB儲存對應機器B用的時間,
            //timeMax記錄兩者共需的時間,即較大的那個;who則標識完成該任務的機器是A還是B。
            int[,] timeA = new int[n, Sum];
            int[,] timeB = new int[n, Sum];
            int[,] timeMax = new int[n, Sum];
            char[,] who = new char[n, Sum];
            char[] tempRlt = new char[n];//tempRlt記錄機器完成任務的機器順序,並逐一賦值給result

            //先計算第1個任務相關值,記錄在四個數組的第0行。
            for (int i = 0; i <= a[0]; i++)
            {
                timeA[0, i] = i;
                if (i < a[0])
                {
                    timeB[0, i] = b[0];
                    who[0, i] = 'b';
                }
                else
                {
                    timeB[0, i] = 0;
                    who[0, i] = 'a';
                }
                timeMax[0, i] = Math.Max(timeA[0, i], timeB[0, i]);
            }

            //儘管像下面一樣,直接比較即可得出完成第1項任務的最優時間,但由於使用動態規劃,計算上述值是必需的。
            if (a[0] <= b[0])
            {
                least[0] = a[0];
                tempRlt[0] = 'a';
            }
            else
            {
                least[0] = b[0];
                tempRlt[0] = 'b';
            }
            result[0] = new String(tempRlt);         

            //計算第2個至第n個任務,分別記錄在四個數組相應行。
            for (int k = 1; k < n; k++)
            {

                //tempSum記錄完成前k項任務機器A最多需要的時間,即全部由A完成需要的時間,亦即機器A所有可能的取值範圍。
                int tempSum = 0;
                for (int temp = 0; temp <= k; temp++)
                {
                    tempSum += a[temp];
                }
                //計算出所有可能的點對(timeA,timeB),並取值timeMax。
                for (int i = 0; i <= tempSum; i++)
                {
                    //機器A在完成前k項任務時所花費的時間為i。
                    timeA[k, i] = i;

                    //i即timeA[k, i],若機器A完成前k項任務的時間 小於 它完成第k項的時間,可能嗎?不可能,所以第k項任務肯定由機器B做。
                    if (i < a[k])
                    {
                        timeB[k, i] = timeB[k - 1, i] + b[k];
                        who[k, i] = 'b';
                    }
                    //按照前述動態規劃方式的思想,確定機器A在花費i時間時,機器B花費的最優時間。
                    else
                    {
                        if ((timeB[k - 1, i] + b[k]) <= timeB[k - 1, i - a[k]])
                        {
                            timeB[k, i] = timeB[k - 1, i] + b[k];
                            who[k, i] = 'b';
                        }
                        else
                        {
                            timeB[k, i] = timeB[k - 1, i - a[k]];
                            who[k, i] = 'a';
                        }
                    }
                    //兩台機器花費時間較大的那個為總花費時間。
                    timeMax[k, i] = Math.Max(timeA[k, i], timeB[k, i]);
                }

                //處理數組tempSum後面的值。機器A時間全部設為最大,此時機器B則無需花費時間。
                for (int i = tempSum + 1; i < aSum; i++)
                {
                    timeA[k, i] = tempSum;
                    timeB[k, i] = 0;
                }

                //完成第k項任務後,在timeMax所有可能值中,選取最小值即最優值。
                int flag = 0;//記錄最優值所在的位置i值,同時也是機器A所花費的時間。
                for (int i = 0; i <= tempSum; i++)
                {
                    if (timeMax[k, i] > 0 && timeMax[k, i] < least[k])
                    {
                        least[k] = timeMax[k, i];
                        flag = i;
                    }
                }

                //用來回溯所用機器的順序,需要注意的是機器A所花費的時間和數組的下標一樣,因此可用做標記。
                //最後一項任務的回溯路徑已在中標出,便於理解。
                tempRlt[k] = who[k, flag];
                for (int i = k; i > 0 && flag > 0; i--)
                {
                    //如果是機器A完成第i項任務,則機器A花費的時間減去a[i]就是完成前i-1項任務的時間。
                    //同時在who中可以確定前一項任務的機器,因為機器A花費的時間可用做位置標記。
                    if (tempRlt[i] == 'a')
                    {
                        flag -= a[i];
                        tempRlt[i - 1] = who[i - 1, flag];

                    }
                    if (tempRlt[i] == 'b')
                    {
                        tempRlt[i - 1] = who[i - 1, flag];
                    }
                }

                result[k] = new String(tempRlt);
            }    
        }

        static void Main(string[] args)
        {
            const int N = 6;
            int[] a = new int[N] { 2, 5, 7, 10, 5, 2 };
            int[] b = new int[N] { 3, 8, 4, 11, 3, 4 };
            int[] least = new int[N];   //記錄完成任務的最優時間
            string[] result = new string[N];    //記錄完成任務機器的順序

            DlrwZydd(a, b, N, least, result);
            Console.WriteLine();
            for (int i = 0; i < N; i++)
            {
                Console.WriteLine(" 按要求完成前{0}項任務的機器順序為:" + result[i] + " 時間為:{1}" ,i+1,least[i]);
            }
        }
    }
}

轉自http://hi.baidu.com/liongg/item/03938f0fcd75f58a03ce1b6d

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.