# Lecture 12: Maximization

### COSC 311 Algorithms, Fall 2022

$\def\compare{ {\mathrm{compare}} } \def\swap{ {\mathrm{swap}} } \def\sort{ {\mathrm{sort}} } \def\insert{ {\mathrm{insert}} } \def\true{ {\mathrm{true}} } \def\false{ {\mathrm{false}} } \def\BubbleSort{ {\mathrm{BubbleSort}} } \def\SelectionSort{ {\mathrm{SelectionSort}} } \def\Merge{ {\mathrm{Merge}} } \def\MergeSort{ {\mathrm{MergeSort}} } \def\QuickSort{ {\mathrm{QuickSort}} } \def\Split{ {\mathrm{Split}} } \def\Multiply{ {\mathrm{Multiply}} } \def\Add{ {\mathrm{Add}} }$

## Overview

1. Recurring Recurrences
2. Maximizing Profit

## Last Time

The “Master Theorem”

• Given recurrence $T(n) = a T(n / b) + f(n)$

• Define $c = \log_b a$

• Three cases:

1. If $f(n) = O(n^d)$ for $d < c$ then $T(n) = O(n^c)$
2. If $f(n) = \Theta(n^c \log^k n)$ then $T(n) = O(n^c \log^{k+1} n)$
3. If $f(n) = \Omega(n^d)$ for $d > c$, then $T(n) = O(f(n))$

## A Technical Note

In formal statement we have

• $T(n) = a T(n / b) + f(n)$

In practice, might have

• $T(n) = a T(\lceil n / b \rceil) + f(n)$

The conclusion of the theorem still holds in this case!

## Master Theorem for MergeSort

$T(n) = a T(n / b) + f(n)$

MergeSort(a, i, j):
if j - i = 1 then
return
endif
m <- (i + j) / 2
MergeSort(a,i,m)
MergeSort(a,m,j)
Merge(a,i,m,j)


## Profit Maximization

Goal. Pick day $b$ to buy and day $s$ to sell to maximize profit.

## Formalizing the Problem

Input. Array $a$ of size $n$

• $a[i] =$ price of Alphabet stock on day $i$

Output. Indices $b$ (buy) and $s$ (sell) with $1 \leq b \leq s \leq n$ that maximize profit

• $p = a[s] - a[b]$

## Simple Procedure

Devise a procedure to determine max profit in time $O(n^2)$.

## Divide and Conquer?

Question. Can we compute maximum profit faster?

• Use divide an conquer?

## Three Cases

Array $a$ of size $n$, midpoint $m = n / 2$

1. Maximum profit in left half: $b, s < n / 2$
2. Maximum profit in right half: $n/2 \leq b, s$
3. Maximum profit splits the halves: $b < n/2 \leq s$

## Algorithmic Approach

1. Find maximum profit in left half
• get value from recursive method call
2. Find maximum profit in right half
• get value from recursive method call
3. Find maximum profit for split halves
• how?

## Observation

To find the maximum profit for split halves:

1. find the lowest price $a[b]$ for $b < n/2$
2. find the highest price $a[s]$ for $s \geq n/2$

Maximum profit is $a[s] - a[b]$.

## Maximization Pseudocode

  # find maximum profit achievable for b, s
# satisfying i <= b <= s < j

MaxProfit(a, i, j):
if j - i = 1 then return 0
m <- (i + j) / 2
left <- MaxProfit(a, i, m)
right <- MaxProfit(a, m, j)
min <- FindMin(a, i, m)
max <- FindMax(a, m, j)
return Max(left, right, max - min)


## Example

MaxProfit([1, 3, 2, 4], 1, 5)

## Algorithm Correctness

Claim. MaxProfit(a, i, j) returns the maximum value of $a[s] - a[b]$ over all $s, b$ satisfying $i \leq b \leq s < j$.

Proof. Argue by induction on $n = j - i =$ size of the call.

  MaxProfit(a, i, j):
if j - i = 1 then return 0
...


Base case. $n = 1$.

## Inductive Step I

  MaxProfit(a, i, j):
...
left <- MaxProfit(a, i, m), right <- MaxProfit(a, m, j)
min <- FindMin(a, i, m), max <- FindMax(a, m, j)
return Max(left, right, max - min)


Claim. MaxProfit(a, i, j) returns the maximum value of $a[s] - a[b]$ over all $s, b$ satisfying $i \leq b \leq s < j$.

Three possible cases…

## Inductive Step II

  MaxProfit(a, i, j):
...
left <- MaxProfit(a, i, m), right <- MaxProfit(a, m, j)
min <- FindMin(a, i, m), max <- FindMax(a, m, j)
return Max(left, right, max - min)


Claim. MaxProfit(a, i, j) returns the maximum value of $a[s] - a[b]$ over all $s, b$ satisfying $i \leq b \leq s < j$.

Inductive hypothesis. Claim holds for all sizes $< n$.

Inductive step. Must show claim holds for size $n$.

• IH $\implies$ left is max profit in a[i..m-1]
• IH $\implies$ right is max profit in a[m..j-1]
• max - min is max profit with $b < m \leq s$

## Running Time Rercurrence?

$T(n) = a T(n / b) + f(n)$

  # find maximum profit achievable for b, s
# satisfying i <= b <= s < j

MaxProfit(a, i, j):
if j - i = 1 then return 0
m <- (i + j) / 2
left <- MaxProfit(a, i, m)
right <- MaxProfit(a, m, j)
min <- FindMin(a, i, m)
max <- FindMax(a, m, j)
return Max(left, right, max - min)


## Applying Master Theorem

• Given recurrence $T(n) = a T(n / b) + f(n)$

• Define $c = \log_b a$

• Three cases:

1. If $f(n) = O(n^d)$ for $d < c$ then $T(n) = O(n^c)$
2. If $f(n) = \Theta(n^c \log^k n)$ then $T(n) = O(n^c \log^{k+1} n)$
3. If $f(n) = \Omega(n^d)$ for $d > c$, then $T(n) = O(f(n))$

## Exercise

Modify the code to return the indices b and s that maximize profit.

  # find maximum profit achievable for b, s
# satisfying i <= b <= s < j

MaxProfit(a, i, j):
if j - i = 1 then return 0
m <- (i + j) / 2
left <- MaxProfit(a, i, m)
right <- MaxProfit(a, m, j)
min <- FindMin(a, i, m)
max <- FindMax(a, m, j)
return Max(left, right, max - min)