;; Scheduling ;; Consider a list of jobs with lengths and weights (def jobs [{:length 2 :weight 2} {:length 1 :weight 3} {:length 3 :weight 1}]) ;; The cost of running each job is the completion time multiplied by the weight (defn cost [jobs] (let [lengths (map :length jobs) weights (map :weight jobs) completions (reductions + lengths) costs (map * weights completions) cost (reduce + costs)] [ cost costs weights completions lengths])) (cost jobs) ;-> [19 (4 9 6) (2 3 1) (2 3 6) (2 1 3)] ;; We might actually take this model literally. Consider a company ;; which is paying daily penalties on several jobs which have overrun, ;; and trying to work out where to put the energy of its staff. ;; Different ways of ordering the jobs result in different costs: (map cost (clojure.math.combinatorics/permutations jobs)) ;; ([15 (3 6 6) (3 2 1) (1 3 6) (1 2 3)] ;; [19 (3 4 12) (3 1 2) (1 4 6) (1 3 2)] ;; [19 (4 9 6) (2 3 1) (2 3 6) (2 1 3)] ;; [27 (4 5 18) (2 1 3) (2 5 6) (2 3 1)] ;; [27 (3 12 12) (1 3 2) (3 4 6) (3 1 2)] ;; [31 (3 10 18) (1 2 3) (3 5 6) (3 2 1)]) ;; It looks as though our best (cheapest) orderings are those where short, high-priority jobs ;; come first. ;; Let's make a list of jobs at random (def random-jobs (take 7 (repeatedly (fn[] {:weight (rand-int 10) :length (rand-int 10)})))) ;; And find by brute force the optimal arrangement (reduce min (map (comp first cost) (clojure.math.combinatorics/permutations random-jobs))) ;-> 412 ;; The cost of this operation is gigantic, factorial in the number of jobs. How can we do better? ;; Consider some possible functions that we might use to order our jobs, which ;; rank jobs higher as their priority is higher, and lower as their length is higher (def difference-cost (fn[{:keys [weight length]}] (- weight length))) (def ratio-cost (fn[{:keys [weight length]}] (/ weight length))) (def sq-ratio-cost (fn[{:keys [weight length]}] (let [s (/ weight length)] (* s s)))) (def length-square-ratio-cost (fn[{:keys [weight length]}] (/ weight (* length length)))) ;; Sorting the jobs by ratio-cost finds the optimal arrangement (cost (reverse (sort-by ratio-cost random-jobs))) ; [412 (18 28 36 90 96 115 29) (9 7 6 9 6 5 1) (2 4 6 10 16 23 29) (2 2 2 4 6 7 6)] ;; But sorting by difference-cost finds a decent, but not optimal arrangement (cost (reverse (sort-by difference-cost random-jobs))) ; [428 (18 54 56 60 96 115 29) (9 9 7 6 6 5 1) (2 6 8 10 16 23 29) (2 4 2 2 6 7 6)] ;; Sorting by square-ratio also works (cost (reverse (sort-by sq-ratio-cost random-jobs))) ; [412 (18 28 36 90 96 115 29) (9 7 6 9 6 5 1) (2 4 6 10 16 23 29) (2 2 2 4 6 7 6)] ;; As does the length-square version (cost (reverse (sort-by length-square-ratio-cost random-jobs))) ;-> [412 (18 28 36 90 96 115 29) (9 7 6 9 6 5 1) (2 4 6 10 16 23 29) (2 2 2 4 6 7 6)] ;; I think it's interesting to try to work out which of these ;; criteria, if any, will reliably order the jobs in the best way. ;; The answer is not terribly obvious, but very simple once you see it!
Blog Archive
-
▼
2013
(52)
-
▼
September
(12)
- Efficiency and Progress III : How Close Can We Get...
- Efficiency and Progress II : Behold, C
- Efficiency and Progress : My God, Clojure is Slow!
- How on earth do you program without *recursion*?
- Algorithms II
- Putting it all together : Using Union-Find in Krus...
- Implementing a Data Structure : Union-Find version II
- Implementing a Data Structure: Union-Find
- Kruskal's Algorithm for Minimal Spanning Trees
- The Knapsack Problem : Another Classic Puzzle from...
- Optimal Scheduling : A Classic Puzzle from Compute...
- SVG Graphics in a Clojure Web Application
-
▼
September
(12)
Search This Blog
Sunday, September 15, 2013
Optimal Scheduling : A Classic Puzzle from Computer Science
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment