此演算法將一個 O( N2 ) 的 Scheduling 問題縮減到了 O( NlogN )
問題描述
有 N 個 Jobs,每個 Job 耗時 p 且期限 (due) 為 d,求最少逾期工作數量 ( Minimum number of late jobs )的排程方式
也就是說,要找出一個排程方式是能夠讓在期限之前完成的工作數量最大化
Moore-Hodgson 演算法
- 設 J = ∅ ( J 是一個 subschedule 且為空集合 )
- 將 Jobs 按照期限遞增排序 ( d1 < d2 < …… < dn ),也就是依照 Earliest Due Date ( EDD ) 的方式,並放入 J 中
- 令 k = subschedule J 中的第一個逾期工作的 index,如果沒有找到,代表已完成排程,跳到步驟 7
- 找出 pm = max{ pi, for i = 1 ~ k } ( 找出 index <= k 中最耗時的工作)
- 將 Job m 移出 subschedule J,放入集合 S 中
- 重複步驟 3
- 最後,將集合 S 以任意順序加到 J 的後面,此 Schedule J 即為最少逾期工作數量的排程
例子一
Job | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
pi ( 所需時間 ) | 10 | 3 | 4 | 8 | 10 | 6 |
di ( 期限 ) | 15 | 6 | 9 | 23 | 20 | 30 |
第一步,先根據 Due Time 去做排序
再新增加一個 si 去紀錄剛好執行完 Job[i] 的時間
Job | 2 | 3 | 1 | 5 | 4 | 6 |
---|---|---|---|---|---|---|
pi ( 所需時間 ) | 3 | 4 | 10 | 10 | 8 | 6 |
di ( 期限 ) | 6 | 9 | 15 | 20 | 23 | 30 |
si ( 完成工作的時間 ) | 3 | 7 | 17 | 27 | 35 | 41 |
我們可以輕易的從 si 與 di 去找到第一個逾期的工作是 Job[1]
然後再找一下在 Job[1] 之前的工作中,最耗時的工作為 Job[1]
所以我們把 Job[1] 刪除,並更新一下在 Job[1] 之後的 si
Job | 2 | 3 | 5 | 4 | 6 |
---|---|---|---|---|---|
pi | 3 | 4 | 10 | 8 | 6 |
di | 6 | 9 | 20 | 23 | 30 |
si | 3 | 7 | 17 | 25 | 31 |
找到第一個逾期的工作是 Job[4]
在 Job[4] 之前最耗時的工作為 Job[5]
剔除他並更新
Job | 2 | 3 | 4 | 6 |
---|---|---|---|---|
pi | 3 | 4 | 8 | 6 |
di | 6 | 9 | 23 | 30 |
si | 3 | 7 | 15 | 21 |
到這邊就沒有逾期的工作了
接下來就是把剛剛剔除的工作以任意順序加到後面去
所以可以為 : 2 -> 3 -> 4 -> 6 -> 1 -> 5
也可以是為 : 2 -> 3 -> 4 -> 6 -> 5 -> 1
例子二
Job | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
pi | 4 | 3 | 2 | 5 | 6 |
di | 6 | 7 | 8 | 9 | 11 |
由於剛好順序已經是按照 Due Time 排序過後的了
所以直接加上 si 來輔助過程
Job | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
pi | 4 | 3 | 2 | 5 | 6 |
di | 6 | 7 | 8 | 9 | 11 |
si | 4 | 7 | 9 | 14 | 20 |
找到第一個逾期的工作是 Job[3]
在 Job[3] 之前最耗時的工作為 Job[1]
剔除他並更新
Job | 2 | 3 | 4 | 5 |
---|---|---|---|---|
pi | 3 | 2 | 5 | 6 |
di | 7 | 8 | 9 | 11 |
si | 3 | 5 | 10 | 16 |
找到第一個逾期的工作是 Job[4]
在 Job[4] 之前最耗時的工作為 Job[4]
剔除他並更新
Job | 2 | 3 | 5 |
---|---|---|---|
pi | 3 | 2 | 6 |
di | 7 | 8 | 11 |
si | 3 | 5 | 11 |
到這邊就沒有逾期的工作了
接下來就是把剛剛剔除的工作以任意順序加到後面去
所以可以為 : 2 -> 3 -> 5 -> 1 -> 4
也可以是為 : 2 -> 3 -> 5 -> 4 -> 1
暴力程式碼 O( N2 )
此版本程式碼耗時 O( N2 )
他只是演示上述演算法的過程
並非真正的最佳 present algorithm 的方式
真正優化的程式碼請往下看或按側邊攔跳到 優化程式碼
首先我們先定義一下工作的結構與比較大小的方法
1 | struct Job |
再把 Moore-Hodgson’s Algorithm 封裝成 class
1 | class Moore_Hodgson |
然後在主程式這樣呼叫
1 | int main(void) |
得到輸出1
2Schedule 1 : 2 3 4 6 1 5
Schedule 2 : 2 3 5 1 4
優化程式碼 O( NlogN )
上述的暴力法中有許多搜尋與計算 job end time 是重複的
我們可以使用 Priority Queue 與依序加入工作的方式來加快演算法
演算法
- 一樣先依照 EDD 次序將 Jobs 排序
- 遍歷 Jobs,將 Job[i] 加入到 priority queue 中
- 將目前 end time 加上 Job[i] 的耗時
- 檢查此工作是否有超時 ( end time 是否大於 the due of Job[i] )
- 若超時,則從 priority queue 中去除最大者,將 end time 扣掉他。
若有需要輸出最後結果的話,也在 overdue 上記錄該工作為超時 - 若沒有,則重複步驟 2,直到遍歷完 Jobs
- 輸出時,先依照排序過後的 Jobs 次序將未逾期的工作印出
再以任意順序輸出已逾期工作
例子
用上面的例子一來做說明
Job | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
pi ( 所需時間 ) | 10 | 3 | 4 | 8 | 10 | 6 |
di ( 期限 ) | 15 | 6 | 9 | 23 | 20 | 30 |
先將 Jobs 依照期限排序
Job | 2 | 3 | 1 | 5 | 4 | 6 |
---|---|---|---|---|---|---|
pi ( 所需時間 ) | 3 | 4 | 10 | 10 | 8 | 6 |
di ( 期限 ) | 6 | 9 | 15 | 20 | 23 | 30 |
現在就開始來排程
首先依序遍歷 Jobs
(一) 加入 Job[1]
Job | 2 | |||
---|---|---|---|---|
pi | 3 | |||
di | 6 |
end time = 3,沒有超時
(二) 加入 Job[2]
Job | 2 | 3 | ||
---|---|---|---|---|
pi | 3 | 4 | ||
di | 6 | 9 |
end time = 7,沒有超時
(三) 加入 Job[3]
Job | 2 | 3 | 1 | |
---|---|---|---|---|
pi | 3 | 4 | 10 | |
di | 6 | 9 | 15 |
end time = 17,超時
利用 priority queue 去除最大的,也就是將 J1 刪除
並減去他的工作時間,end time = 17 - 10 = 7
(四) 加入 Job[4]
Job | 2 | 3 | 5 | |
---|---|---|---|---|
pi | 3 | 4 | 10 | |
di | 6 | 9 | 20 |
end time = 17,沒有超時
(五) 加入 Job[5]
Job | 2 | 3 | 5 | 4 |
---|---|---|---|---|
pi | 3 | 4 | 10 | 8 |
di | 6 | 9 | 20 | 23 |
end time = 25,超時
利用 priority queue 去除最大的,也就是將 Job5 刪除
並減去他的工作時間,end time = 25 - 10 = 15
(六) 加入 Job[6]
Job | 2 | 3 | 4 | 6 |
---|---|---|---|---|
pi | 3 | 4 | 8 | 6 |
di | 6 | 9 | 23 | 30 |
end time = 21,沒有超時
最後輸出答案為 : 2 -> 3 -> 4 -> 6 -> 1 -> 4
程式碼
首先定義 Job 結構和比較大小的方法
由於我們不需要再記錄個別點的結束時間,因此就不需要 end_time 欄位了
1 | struct Job2 |
再來我們還需要定義 Priority Queue 中紀錄資訊的結構
1 | struct Record |
主要演算法部分
要注意因為 Job 的 Index 是從 1 開始,所以要把 overdue 多開一個空間才可以用 Index 直接存取
1 | class Moore_Hodgson_optimal |
主程式
1 | int main(void) |
輸出
1 | Schedule 1 : 2 3 4 6 1 5 |
思考
此演算法的目的是要讓未超時的工作數量最大化,也就是要讓逾期的工作量最小
整體的核心就是 Due
所以先用 Due 去由小到大排序,再依序把一個一個工作加到排程中
若發現加入當前工作後此工作會超時,就從以加入的工作中選一個最耗費時間的把他刪除掉
可以想成是讓整體的彈性時間更大 ( 也就是完成目前排程工作的時間到排程最後一個工作期限的這段緩衝 )
重複此動作到所以工作皆加入,就會是一個 Minimum number of late jobs 的 Schedule
證明演算法
我這邊就沒有證明了,因為我也沒有搞很懂QQ
若是有大大有興趣,可以看一下參考
我有放一些 Reference
記得搞懂了要來留言教我呀
參考網站
- Handbook of Scheduling: Algorithms, Models, and Performance Analysis
- 讓我發現這個演算法的網站
- Operations Scheduling
- 論文 : Knapsack-like scheduling problems, the Moore-Hodgson algorithm and the ‘tower of sets’ property
- 論文 : An n Job, One Machine Sequencing Algorithm for Minimizing the Number of Late Jobs ( 最早出現的文獻 )
- 論文 : MooreHodgson