---
title: "Scheduling Problem"
format:
html:
toc: true
number-sections: true
code-line-numbers: true
---
[In-class notes](hand_written_notes/Greedy_Sched2.pdf)
## Learning Goals
- Describe typical properties of greedy algorithms
- Describe the scheduling problem, as well as related terms like "objective function," "optimization problem," and "completion time," "scoring function," "optimal order," "greedy order"
- Create counterexample to a greedy ordering
- Describe the structure of an exchange argument and prove optimality of a greedy algorithm using an exchange argument
- Modify exchange argument in the case that scoring function is not unique.
- Analyze the runtime of a greedy scheduling algorithm
## Introduction to Greedy Algorithms
There is not an agreed upon definition of greedy algorithms, and many algorithms that at first seem different from each other are all classified as greedy. Over the course of the semester we will see several examples, and this will hopefully give a better sense of what "greedy" means. However, I'll give you an informal definition
::: {#def-greedy}
(informal definition) A greedy algorithm sequentially constructs a solution through a series of myopic/short-sighted/local/not-global/not-thinking about-the-future decisions
:::
Greedy algorithms often have the follow properties:
- Easy to create (non-optimal) greedy algorithms
- Runtime is relatively easy to analyze
- Hard to find an optimal/always correct greedy algorithm
- Hard to prove correctness
### Order Problem
Greedy algorithms are frequently used to design algorithms for ordering problems, which are a subclass of optimization problems (ordering problems are a broad class of problems that are very general - we will look at a specific ordering problem shortly).
:::{#def-opt}
An *optimization problem* is a problem where you would like your solution to be better (according to some pre-decided metric) that any other possible/feasible solution.
:::
**Generic Ordering Problem**
- Input: $n$ items.
- Output: Ordering $\sigma$ of the $n$ items that minimizes (or maximizes) $A(\sigma).$
:::{#def-obj}
We call $A(\sigma)$, the function that we want our output to maximize or minimize, the *objective function*
:::
For example, the following figure shows the possible orders of 3 items, and made-up $A$-values for each ordering.
{#fig-Aex width=80%}
If our goal is to minimize $A$, then for the example of @fig-Aex, the output of this ordering problem should be $(3,1,2)$, but if our goal was to maximize $A$, then the output of this ordering problem should be $(2,1,3)$.
Here are two approaches for designing an algorithm to solve the Ordering Problem:
- **Brute Force:**
* Evaluate $A(\sigma)$ for every possible ordering of the $n$ items. Keep track of the minimium (maximum) value found and the corresponding ordering, and then return that ordering.
* Takes $\Omega(n!)$ time ($\Omega$ is asymptotic lower bound)
- **Greedy**
* Design a simple scoring function $f$. A *scoring function* is a function that maps each item to a score. Note that while the objective function maps orderings to numbers, the scoring function maps items to numbers.
* Evaluate the score of each item
* Return $\sigma$ that puts the items in decreasing (increasing) order by score
* Takes $\Omega(n\log n)$ time.
## Scheduling Problem
### Description
The scheduling problem is a type of ordering problem.
For the scheduling problem, we imagine that we have a series of tasks that we want to accomplish, but we can only do one task at a time, and must complete that task before moving onto the next. Each task takes some amount of time (tasks can have the same or different times), and each task has a weight, which tells us the importance of that task. We would like to figure out how to schedule tasks to prioritize important, short tasks.
**Input:** $n$ tasks. Information about the tasks is given in the form of two length-$n$ arrays, one labeled $t$, and one labeled $w$. The $i^\textrm{th}$ element of array $t$ is the time required to complete the $i^\textrm{th}$ task, which we call $t_i$, and the $i^\textrm{th}$ element of array $w$ is the weight (importance) of the $i^\textrm{th}$ task, which we call $w_i$, where larger weight means more important. For example, we might have the input
task | 1 | 2 | 3|
-----|---|---|---|
time | 3 | 4 | 2|
weight| 5| 1 | 2|
so in this case, $t_1=3$ and $w_3=2$
**Output:** Ordering of tasks $\sigma= (\sigma_1,\sigma_2,\dots,\sigma_n)$ that minimizes
$$
A(\sigma)=\sum_{i=1}^nw_iC_i(\sigma),
$${#eq-objective}
where $C_i(\sigma)$ is the *completion time* of task $i$ with ordering $\sigma$, described below. Note that @eq-objective is the *objective function* for this scheduling problem
Assuming we start our first task at time $0$, then run our sequence of tasks, $C_i(\sigma)$ is the time at which the task $i$ is completed when the tasks are done in the order given by $\sigma$. For an example, suppose we have
$$
t=(3,4,2)
$$
and consider the ordering $\sigma=(3,1,2)$, so we do the third task first, then the first, and then the second. This is shown graphically in @fig-comp:
{#fig-comp width=50%}
So $C_3(3,1,2)=2$ because we do it first, it takes time $2$, so it is completed at time $2$. Next we do task $1$, but it only gets started at time $2$ after completing task $3$, and runs until time $5$, so $C_1(3,1,2)=5.$ Now that task $1$ is completed, we can finally do task $2$ which takes another $4$ time units, and so we complete it at time $9$, so $C_2(3,1,2)=9$. (When it is clear which ordering $\sigma$ we are talking about, we will sometimes write $C_i$ instead of $C_i(\sigma)$.)
**Application:** Scheduling tasks on a single CPU (central processing unit) of a computer. Or scheduling your tasks...in the case that you must complete each task before moving on to the next.
::: {.callout-tip appearance="simple"}
### ABCD Question
If $t=(3,4,2)$ (the same example as above), what is $C_3(2,1,3)$
A) 2
A) 3
A) 7
A) 9
:::
::: {.callout-tip appearance="simple"}
### Group Exercises
1. Given the following input:
task | 1 | 2 |
-----|---|---|
time | 3 | 4 |
weight| 5| 1 |
What is the best order? (Use a brute force approach, and calculate $A(\sigma)$ for all possible orderings.)
Hint: first calculate $C_1$ and $C_2$ for each ordering
2. What is the objective function $A(\sigma)=\sum_{i=1}^nw_iC_i(\sigma),$ optimizing? (What time/weight jobs is it prioritizing?)
:::
## Creating Scoring Functions and Counterexamples
Creating a greedy algorithm is easy! All you need to do is come up with a scoring function that you think might optimize the objective function. This scoring function should be a function of the properties of each item. For our scheduling algorithm, this means the scoring function can depend on $t_i$ and $w_i.$
For example, we can create the scoring function
$$
f(i)=t_i \qquad \textrm{(increasing)}
$${#eq-score1}
where we will order items from smallest time to largest time. This greedy algorithm will choose the ordering that puts the shortest jobs first, and the longest jobs last.
Done! We've designed a greedy algorithm!
Problem? It might not be a good algorithm. The *greedy ordering*, the ordering created by $f$, might not be the *optimal ordering*, the one with the optimal $A$ value.
In any case, it might not be good, but we can still practice creating a greedy algorithm using @eq-score1. We first need to evaluate the score for each item (we'll use our example input from the group exercise above):
task | 1 | 2 |
-----|---|---|
time | 3 | 4 |
weight| 5| 1 |
f | 3 | 4|
Since we have decided to order jobs in increasing $f$-value, our greedy ordering is $(1,2)$. In fact, when we did the brute force approach, we also found $(1,2)$ was the optimal ordering. This is great! Our greedy algorithm found the best possible ordering!!
::: {.callout-important}
The scores, the $f$-values, of items mean nothing on their own. They can be negative, or 0, or positive, and it doesn't tell you anything. Scores only have meaning when compared to the scores of other items.
:::
### Finding Counterexamples
Our greedy ordering was correct for the example above, but....will this greedy ordering always be correct?? To show that a scoring function will not always give us the optimal ordering, we can try to find a counterexample.
Tips for finding a counterexample:
- Think extreme
- Create tasks that are close in score but very different otherwise
::: {.callout-tip}
## Example
We will find a counter example to $f(i)=t_i$ (ordered in increasing $f$-value).
We design the following set of jobs, that have very close times, but very different weights. In particular, we set the job that takes slightly longer to be much much more important.
task | 1 | 2 |
-----|---|---|
time | 1 | 2 |
weight| 1| 100 |
f | 1 | 2|
So in this case, our greedy ordering is again $(1,2)$.
However, if you do a brute force approach, you will find that actually, the optimal ordering for this input to the scheduling problem is $(2,1)$.
:::
::: {.callout-tip appearance="simple"}
### Group Exercises
Create counterexamples for the following scoring functions:
A) $f(i)=w_i$ (decreasing)
A) $f(i)=w_i-t_i$ (decreasing)
:::
### Design Approach
Here is a general approach for creating a good greedy algorithm:
1. Brainstorm several reasonable scoring functions. For example:
$$
f(i)=w_i-t_i \textrm{ (decreasing)}\qquad f(i)=w_i/t_i \textrm{ (decreasing)}\qquad f(i)=w_i^2-2t_i \textrm{ (decreasing)}
$$
These are "reasonable" because they all tend to prioritize jobs that are important and short, which is our goal.
1. Test the scoring functions on examples, see how they do, and see if you can create counterexamples showing a scoring function is not optimal. (In some cases, you might also be satisfied with an algorithm that is not always optimal, but is close to optimal. For example, Prof. Das in her research often tries to show that an algorithm never does worse than 90% of the optimal value of the objective function.) These types of algorithms that are not always optimal or that you can't prove are optimal, are called **heuristics**.
1. If you can't find any counterexamples, you can try to prove that your greedy algorithm is correct.
## Proving Correctness of Greedy Scheduling {#sec-proofgreedy1}
::: {@thm-schedule}
Ordering jobs by decreasing value of $f(i)=w_i/t_i$ is optimal for minimizing $A(\sigma)=\sum_iw_iC_i(\sigma)$.
:::
::: {.proof}
[We will use an "Exchange Argument," which is a type of proof by contradiction]
Assume that $w_i/t_i$ are distinct (i.e. no two jobs have the same score). Without loss of generality, we are going to relabel the tasks in decreasing score so that task $1$ has the highest score, then task $2$, etc, so that
$$w_1/t_1>w_2/t_2>w_3/t_3\dots w_n/t_n.$$
With this relabeling, we have that the greedy ordering is
$$\sigma=(1,2,3,\dots,n).$$
Assume for contradiction that $\sigma$ is not the optimal ordering. Then there must exists another ordering $\sigma^*$ that is the optimal ordering.
Since $\sigma^*\neq \sigma$, there must be tasks $b,y\in \{1,2,\dots,n\}$ where $bA(\sigma^{*'})$. This is a contradiction because $A(\sigma^{*})$ is optimal which meant it was supposed to have the smallest $A$-value of all orderings, but instead we see that $A(\sigma^{*'})$
has a smaller $A$-value. Thus our original assumption, hat $\sigma$ was not optimal, must have been incorrect, and in fact, $\sigma$ must be the optimal ordering.
:::
## Big Picture: Exchange Argument Structure
Exchange arguments are commonly (although not always) used to prove the correctness of greedy algorithms. Here is the general structure of an exchange algorithm proof.
**Exchange Argument**
1. Let $\sigma$ be the sequence that your greedy algorithm gives you, the one that you are trying to prove is optimal. For contradiction, assume it is not optimal.
2. Then that means there is some other sequence $\sigma^*$ that is optimal.
3. But then at some point $\sigma^*$ must be out of order relative to the greedy sequence. Create a new sequence $\sigma^{*'}$ by exchanging two elements (or doing some other small change) of the sequence of $\sigma^*$ to make it more like the greedy algorithm.
4. Show that $\sigma^{*'}$ is better than $\sigma^*$, a contradiction, since $\sigma^*$ was supposed to be optimal. Thus our original assumption in step 1 that $\sigma$ was not optimal must have been incorrect.
## Continuing to Analyze our Greedy Scheduling Algorithm
### Runtime of our greedy algorithm
::: {.callout-tip appearance="simple"}
### ABCD Question
What is the runtime of our greedy scheduling algorithm?
A) $O(1)$
A) $O(n)$
A) $O(n\log n)$
A) $O(n^2)$
:::
### Loosening our Assumption
In our proof in @sec-proofgreedy1, we assumed that no two tasks had the same value of $w_i/t_i$.
Where did we use this assumption in our proof?
Without this assumption, we no longer have a contradiction!! (See hand-written notes for details.) But we can modify the proof so that it still works.
::: {.proof}
[Proof sketch]
Choose some relabeling of the tasks so that
$$w_1/t_1\geq w_2/t_2\geq w_3/t_3\geq\dots \geq w_n/t_n.$$
We call $\sigma=(1,2,3,\dots,n)$ our greedy ordering.
Let $\sigma^*$ be any other ordering. Then we will show
that $A(\sigma^*)\geq A(\sigma)$. Since we could choose
*any other* ordering for $\sigma^*$, this will show that $\sigma$ has the same or better $A$-value compared to every other possible ordering,
and thus $\sigma$ is an optimal ordering.
Consider the following sequence:
{fig-alt="Diagram showing that a series of exchanges never increases A-value."}
By the logic of BubbleSort, if we continue to exchange out-of-order tasks, eventually we will eventually arrive at $\sigma=(1,2,3,\dots,n),$ at which point there are no further out-of-order tasks. However, at each exchange, the $A$ value of the permutation only decreases or stays the same, so we have
$A(\sigma^*)\geq A(\sigma)$. This argument works for any ordering $\sigma^*$,
so we have shown that $A(\sigma)$ is optimal.
$\square$
:::
::: {.callout-tip appearance="simple"}
### ABCD Question
If we no longer have the assumption that $w_i/t_i$ is unique, what is the runtime of our greedy scheduling algorithm?
A) $O(1)$
A) $O(n)$
A) $O(n\log n)$
A) $O(n^2)$
:::
### How to Derive Scoring Function
It is also sometimes possible to derive the scoring function without having to guess it. To do this, you would write an exchange proof, but replace $w_i/t_i$ with the function $f_i$. Then you see what you need to set $f_i$ to in order to get a contradiction. $f_i=w_i/t_i$