Skip to content

Week-3 (Matrix Multiplication/Quick Sort)

CE100 Algorithms and Programming II

Week-3 (Matrix Multiplication/ Quick Sort)

Spring Semester, 2021-2022

Download DOC-PDF, DOC-DOCX, SLIDE, PPTX


Matrix Multiplication / Quick Sort

Outline (1)

  • Matrix Multiplication

  • Traditional

  • Recursive

  • Strassen


Outline (2)

  • Quicksort

  • Hoare Partitioning

  • Lomuto Partitioning

  • Recursive Sorting


Outline (3)

  • Quicksort Analysis

  • Randomized Quicksort

  • Randomized Selection

    • Recursive

    • Medians


Matrix Multiplication (1)

  • Input: A=[aij],B=[bij]
  • Output: C=[cij]=AB i,j=1,2,3,,n
[c11c12c1nc21c22c2ncn1cn2cnn]=[a11a12a1na21a22a2nan1an2ann][b11b12b1nb21b22b2nbn1an2bnn]

Matrix Multiplication (2)

alt:"alt" height:450px center

  • cij=1knaik.bkj

Matrix Multiplication: Standard Algorithm

Running Time: Θ(n3)

for i=1 to n do
    for j=1 to n do
        C[i,j] = 0
        for k=1 to n do
            C[i,j] = C[i,j] + A[i,k] + B[k,j]
        endfor
    endfor
endfor

Matrix Multiplication: Divide & Conquer (1)

IDEA: Divide the nxn matrix into 2x2 matrix of (n/2)x(n/2) submatrices.

alt:"alt" height:450px center


Matrix Multiplication: Divide & Conquer (2)

[c11c12c21c22]=[a11a12a21a22][b11b12b21b22]
8 mults and 4 adds of (n/2)*(n/2) submatrices={c11=a11b11+a12b21c21=a21b11+a22b21c12=a11b12+a12b22c22=a21b12+a22b22

Matrix Multiplication: Divide & Conquer (3)

MATRIX-MULTIPLY(A, B)
    // Assuming that both A and B are nxn matrices
    if n == 1 then 
        return A * B
    else  
        //partition A, B, and C as shown before
        C[1,1] = MATRIX-MULTIPLY (A[1,1], B[1,1]) + 
                 MATRIX-MULTIPLY (A[1,2], B[2,1]); 

        C[1,2] = MATRIX-MULTIPLY (A[1,1], B[1,2]) + 
                MATRIX-MULTIPLY (A[1,2], B[2,2]); 

        C[2,1] = MATRIX-MULTIPLY (A[2,1], B[1,1]) + 
        MATRIX-MULTIPLY (A[2,2], B[2,1]);

        C[2,2] = MATRIX-MULTIPLY (A[2,1], B[1,2]) + 
        MATRIX-MULTIPLY (A[2,2], B[2,2]);
    endif      

    return C

Matrix Multiplication: Divide & Conquer Analysis

T(n)=8T(n/2)+Θ(n2)

  • 8 recursive calls 8T()
  • each problem has size n/2 T(n/2)
  • Submatrix addition Θ(n2)

Matrix Multiplication: Solving the Recurrence

  • T(n)=8T(n/2)+Θ(n2)

  • a=8, b=2

  • f(n)=Θ(n2)
  • nlogba=n3

  • Case 1: nlogbaf(n)=Ω(nε)T(n)=Θ(nlogba)

Similar with ordinary (iterative) algorithm.


Matrix Multiplication: Strassen’s Idea (1)

Compute c11,c12,c21,c22 using 7 recursive multiplications.

In normal case we need 8 as below.

[c11c12c21c22]=[a11a12a21a22][b11b12b21b22]
8 mults and 4 adds of (n/2)*(n/2) submatrices={c11=a11b11+a12b21c21=a21b11+a22b21c12=a11b12+a12b22c22=a21b12+a22b22

Matrix Multiplication: Strassen’s Idea (2)

  • Reminder:
  • Each submatrix is of size (n/2)(n/2)
  • Each add/sub operation takes Θ(n2) time
  • Compute P1P7 using 7 recursive calls to matrix-multiply
P1=a11(b12b22)P2=(a11+a12)b22P3=(a21+a22)b11P4=a22(b21b11)P5=(a11+a22)(b11+b22)P6=(a12a22)(b21+b22)P7=(a11a21)(b11+b12)

Matrix Multiplication: Strassen’s Idea (3)

P1=a11(b12b22)P2=(a11+a12)b22P3=(a21+a22)b11P4=a22(b21b11)P5=(a11+a22)(b11+b22)P6=(a12a22)(b21+b22)P7=(a11a21)(b11+b12)
  • How to compute cij using P1P7 ?
c11=P5+P4P2+P6c12=P1+P2c21=P3+P4c22=P5+P1P3P7

Matrix Multiplication: Strassen’s Idea (4)

  • 7 recursive multiply calls
  • 18 add/sub operations

Matrix Multiplication: Strassen’s Idea (5)

e.g. Show that c12=P1+P2 :

c12=P1+P2=a11(b12b22)+(a11+a12)b22=a11b12a11b22+a11b22+a12b22=a11b12+a12b22

Strassen’s Algorithm

  • Divide: Partition A and B into (n/2)(n/2) submatrices. Form terms to be multiplied using + and .

  • Conquer: Perform 7 multiplications of (n/2)(n/2) submatrices recursively.

  • Combine: Form C using + and on (n/2)(n/2)submatrices.

Recurrence: T(n)=7T(n/2)+Θ(n2)


Strassen’s Algorithm: Solving the Recurrence (1)

  • T(n)=7T(n/2)+Θ(n2)

  • a=7, b=2

  • f(n)=Θ(n2)
  • nlogba=nlg7

  • Case 1: nlogbaf(n)=Ω(nε)T(n)=Θ(nlogba)

T(n)=Θ(nlog27)

23=8,22=4 so log272.81

or use https://www.omnicalculator.com/math/log


Strassen’s Algorithm: Solving the Recurrence (2)

  • The number 2.81 may not seem much smaller than 3

  • But, it is significant because the difference is in the exponent.

  • Strassen’s algorithm beats the ordinary algorithm on today’s machines for n30 or so.

  • Best to date: Θ(n2.376) (of theoretical interest only)


Matrix Multiplication Solution Faster Than Strassen's Algorithm


Matrix Multiplication Solution Faster Than Strassen's Algorithm

For example, if the traditional algorithm taught in school multiplies a 4x5 by 5x5 matrix using 100 multiplications, and this number was reduced to 80 with human ingenuity, AlphaTensor has found algorithms that do the same operation using just 76 multiplications.


Matrix Multiplication Solution Faster Than Strassen's Algorithm

![center h:450px]


Standard Multiplication

center h:400px


Standard and Strassen Comparison

bg right h:650px


Improved Solution

bg right  h:660px


How it's done

"Single-player game played by AlphaTensor, where the goal is to find a correct matrix multiplication algorithm. The state of the game is a cubic array of numbers (shown as grey for 0, blue for 1, and green for -1), representing the remaining work to be done."

center h:350px


Maximum Subarray Problem

Input: An array of values

Output: The contiguous subarray that has the largest sum of elements

  • Input array: [13][3][25][20][3][16][23][18][20][7][12]max. contiguous subarray[22][4][7]

Maximum Subarray Problem: Divide & Conquer (1)

  • Basic idea:
  • Divide the input array into 2 from the middle
  • Pick the best solution among the following:
    • The max subarray of the left half
    • The max subarray of the right half
    • The max subarray crossing the mid-point

Maximum Subarray Problem: Divide & Conquer (2)

alt:"alt" height:450px center


Maximum Subarray Problem: Divide & Conquer (3)

  • Divide: Trivial (divide the array from the middle)

  • Conquer: Recursively compute the max subarrays of the left and right halves

  • Combine: Compute the max-subarray crossing the midpoint

  • (can be done in Θ(n) time).

  • Return the max among the following:
    • the max subarray of the left-subarray
    • the max subarray of the rightsubarray
    • the max subarray crossing the mid-point

TODO : detailed solution in textbook...


Conclusion : Divide & Conquer

  • Divide and conquer is just one of several powerful techniques for algorithm design.
  • Divide-and-conquer algorithms can be analyzed using recurrences and the master method (so practice this math).
  • Can lead to more efficient algorithms

Quicksort (1)

  • One of the most-used algorithms in practice
  • Proposed by C.A.R. Hoare in 1962.
  • Divide-and-conquer algorithm
  • In-place algorithm
  • The additional space needed is O(1)
  • The sorted array is returned in the input array
  • Reminder: Insertion-sort is also an in-place algorithm, but Merge-Sort is not in-place.
  • Very practical

Quicksort (2)

  • Divide: Partition the array into 2 subarrays such that elements in the lower part elements in the higher part
  • Conquer: Recursively sort 2 subarrays
  • Combine: Trivial (because in-place)

Key: Linear-time (Θ(n)) partitioning algorithm

alt:"alt" height:200px center


Divide: Partition the array around a pivot element

  • Choose a pivot element x

  • Rearrange the array such that:

  • Left subarray: All elements x

  • Right subarray: All elements x

alt:"alt" height:350px center


Conquer: Recursively Sort the Subarrays

Note: Everything in the left subarray ≤ everything in the right subarray

alt:"alt" height:350px center

Note: Combine is trivial after conquer. Array already sorted.


Two partitioning algorithms

  • Hoare’s algorithm: Partitions around the first element of subarray
  • (pivot=x=A[p])

alt:"alt" height:100px center

  • Lomuto’s algorithm: Partitions around the last element of subarray
  • (pivot=x=A[r])

alt:"alt" height:100px center


Hoare’s Partitioning Algorithm (1)

  • Choose a pivot element: pivot=x=A[p]

alt:"alt" height:100px center

  • Grow two regions:
  • from left to right: A[pi]
  • from right to left: A[jr]
    • such that:
  • every element in A[pi] pivot
  • every element in A[pi] pivot

Hoare’s Partitioning Algorithm (2)

alt:"alt" height:550px center


Hoare’s Partitioning Algorithm (3)

  • Elements are exchanged when
  • A[i] is too large to belong to the left region
  • A[j] is too small to belong to the right region
    • assuming that the inequality is strict
  • The two regions A[pi] and A[jr] grow until A[i]pivotA[j]
  H-PARTITION(A, p, r)
        pivot = A[p]
          i = p - 1
          j = r - 1
          while true do
            repeat j = j - 1 until A[j] <= pivot
            repeat i = i - 1 until A[i] <= pivot
          if i < j then 
            exchange A[i] with A[j]
          else 
            return j

Hoare’s Partitioning Algorithm Example (Step-1)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-2)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-3)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-4)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-5)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-6)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-7)

alt:"alt" height:550px center


Hoare’s Partitioning Algorithm Example (Step-8)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-9)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-10)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-11)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm Example (Step-12)

alt:"alt" height:500px center


Hoare’s Partitioning Algorithm - Notes

  • Elements are exchanged when
  • A[i] is too large to belong to the left region
  • A[j] is too small to belong to the right region
    • assuming that the inequality is strict
  • The two regions A[pi] and A[jr] grow until A[i]pivotA[j]
  • The asymptotic runtime of Hoare’s partitioning algorithm Θ(n)
  H-PARTITION(A, p, r)
        pivot = A[p]
          i = p - 1
          j = r - 1
          while true do
            repeat j = j - 1 until A[j] <= pivot
            repeat i = i - 1 until A[i] <= pivot
          if i < j then exchange A[i] with A[j]
          else return j

Quicksort with Hoare’s Partitioning Algorithm

QUICKSORT (A, p, r)
  if p < r then
    q = H-PARTITION(A, p, r)
    QUICKSORT(A, p, q)
    QUICKSORT(A, q + 1, r)
  endif

Initial invocation: QUICKSORT(A,1,n)

alt:"alt" height:150px center


Hoare’s Partitioning Algorithm: Pivot Selection

  • if we select pivot to be A[r] instead of A[p] in H-PARTITION

alt:"alt" height:90px center

alt:"alt" height:140px center

  • Consider the example where A[r] is the largest element in the array:
  • End of H-PARTITION: i=j=r
  • In QUICKSORT: q=r
    • So, recursive call to:
    • QUICKSORT(A, p, q=r)
      • infinite loop

Correctness of Hoare’s Algorithm (1)

We need to prove 3 claims to show correctness:

  • Indices i and j never reference A outside the interval A[pr]
  • Split is always non-trivial; i.e., jr at termination
  • Every element in A[pj] every element in A[j+1r] at termination

alt:"alt" height:150px center


Correctness of Hoare’s Algorithm (2)

  • Notations:

  • k: # of times the while-loop iterates until termination

  • im: the value of index i at the end of iteration m
  • jm: the value of index j at the end of iteration m
  • x: the value of the pivot element

  • Note: We always have i1=p and pj1r because x=A[p]


Correctness of Hoare’s Algorithm (3)

Lemma 1: Either ik=jk or ik=jk+1 at termination

Proof of Lemma 1:

  • The algorithm terminates when ij (the else condition).

  • So, it is sufficient to prove that ikjk1

  • There are 2 cases to consider:

  • Case 1: k=1, i.e. the algorithm terminates in a single iteration

  • Case 2: k>1, i.e. the alg. does not terminate in a single iter.

By contradiction, assume there is a run with ikjk>1


Correctness of Hoare’s Algorithm (4)

Original correctness claims:

  • Indices i and j never reference A outside the interval A[pr]
  • Split is always non-trivial; i.e., jr at termination

Proof:

  • For k=1:
  • Trivial because i1=j1=p (see Case 1 in proof of Lemma 2)
  • For k>1:
  • ik>p and jk<r (due to the repeat-until loops moving indices)
  • ikr and jkp (due to Lemma 1 and the statement above)

The proof of claims (a) and (b) complete


Correctness of Hoare’s Algorithm (5)

Lemma 2: At the end of iteration m, where m<k (i.e. m is not the last iteration), we must have: A[pim]x and A[jmr]x

Proof of Lemma 2:

  • Base case: m=1 and k>1 (i.e. the alg. does not terminate in the first iter.)

Ind. Hyp.: At the end of iteration m1, where m<k (i.e. m is not the last iteration), we must have: A[pim1]x and A[jm1r]x

General case: The lemma holds for m, where m<k

Proof of base case complete!


Correctness of Hoare’s Algorithm (6)

Original correctness claim:

  • © Every element in A[j] every element in A[j+r] at termination

Proof of claim ©

  • There are 3 cases to consider:
  • Case 1: k=1, i.e. the algorithm terminates in a single iteration
  • Case 2: k>1 and ik=jk
  • Case 3: k>1 and ik=jk+1

Lomuto’s Partitioning Algorithm (1)

  • Choose a pivot element: pivot=x=A[r]

alt:"alt" height:100px center

  • Grow two regions:
  • from left to right: A[pi]
  • from left to right: A[i+1j]
    • such that:
    • every element in A[pi]pivot
    • every element in A[i+1j]>pivot

Lomuto’s Partitioning Algorithm (2)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-1)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-2)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-3)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-4)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-5)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-6)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-7)

alt:"alt" height:550px center


Lomuto’s Partitioning Algorithm Ex. (Step-8)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-9)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-10)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-11)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-12)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-13)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-14)

alt:"alt" height:500px center


Lomuto’s Partitioning Algorithm Ex. (Step-15)

alt:"alt" height:500px center


Quicksort with Lomuto’s Partitioning Algorithm

QUICKSORT (A, p, r)
  if p < r then
    q = L-PARTITION(A, p, r)
    QUICKSORT(A, p, q - 1)
    QUICKSORT(A, q + 1, r)
  endif

Initial invocation: QUICKSORT(A,1,n)

alt:"alt" height:150px center


Comparison of Hoare’s & Lomuto’s Algorithms (1)

  • Notation: n=rp+1
  • pivot=A[p] (Hoare)
  • pivot=A[r] (Lomuto)
  • # of element exchanges: e(n)
  • Hoare: 0e(n)n2
    • Best: k=1 with i1=j1=p (i.e., A[p+1r]>pivot)
    • Worst: A[p+1p+n21]pivotA[p+n2r]
  • Lomuto : 1e(n)n
    • Best: A[pr1]>pivot
    • Worst: A[pr1]pivot

Comparison of Hoare’s & Lomuto’s Algorithms (2)

  • # of element comparisons: ce(n)

  • Hoare: n+1ce(n)n+2

    • Best: ik=jk
    • Worst: ik=jk+1
  • Lomuto: ce(n)=n1

  • # of index comparisons: ci(n)

  • Hoare: 1ci(n)n2+1|(ci(n)=e(n)+1)

  • Lomuto: ci(n)=n1

Comparison of Hoare’s & Lomuto’s Algorithms (3)

  • # of index increment/decrement operations: a(n)

  • Hoare: n+1a(n)n+2|(a(n)=ce(n))

  • Lomuto: na(n)2n1|(a(n)=e(n)+(n1))

  • Hoare’s algorithm is in general faster

  • Hoare behaves better when pivot is repeated in A[pr]

  • Hoare: Evenly distributes them between left & right regions

  • Lomuto: Puts all of them to the left region

Analysis of Quicksort (1)

QUICKSORT (A, p, r)
  if p < r then
    q = H-PARTITION(A, p, r)
    QUICKSORT(A, p, q)
    QUICKSORT(A, q + 1, r)
  endif

Initial invocation: QUICKSORT(A,1,n)

alt:"alt" height:150px center

Assume all elements are distinct in the following analysis


Analysis of Quicksort (2)

  • H-PARTITION always chooses A[p] (the first element) as the pivot.
  • The runtime of QUICKSORT on an already-sorted array is Θ(n2)

Example: An Already Sorted Array

Partitioning always leads to 2 parts of size 1 and n1

alt:"alt" height:450px center


Worst Case Analysis of Quicksort

  • Worst case is when the PARTITION algorithm always returns imbalanced partitions (of size 1 and n1) in every recursive call.
  • This happens when the pivot is selected to be either the min or max element.
  • This happens for H-PARTITION when the input array is already sorted or reverse sorted
T(n)=T(1)+T(n1)+Θ(n)=T(n1)+Θ(n)=Θ(n2)

Worst Case Recursion Tree

T(n)=T(1)+T(n1)+cn

alt:"alt" height:450px center


Best Case Analysis (for intuition only)

  • If we’re extremely lucky, H-PARTITION splits the array evenly at every recursive call
T(n)=2T(n/2)+Θ(n)=Θ(nlgn)

(same as merge sort)

  • Instead of splitting 0.5:0.5, if we split 0.1:0.9 then we need solve following equation.

$$

T(n)=T(n/10)+T(9n/10)+Θ(n) =Θ(nlgn) $$


“Almost-Best” Case Analysis

alt:"alt" height:500px center


Balanced Partitioning (1)

  • We have seen that if H-PARTITION always splits the array with 0.1to0.9 ratio, the runtime will be Θ(nlgn).

  • Same is true with a split ratio of 0.01to0.99, etc.

  • Possible to show that if the split has always constant (Θ(1)) proportionality, then the runtime will be Θ(nlgn).

  • In other words, for a constant α|(0<α0.5):

  • αto(1α) proportional split yields Θ(nlgn) total runtime


Balanced Partitioning (2)

  • In the rest of the analysis, assume that all input permutations are equally likely.
  • This is only to gain some intuition
  • We cannot make this assumption for average case analysis
  • We will revisit this assumption later
  • Also, assume that all input elements are distinct.

Balanced Partitioning (3)

  • Question: What is the probability that H-PARTITION returns a split that is more balanced than 0.1to0.9?

Balanced Partitioning (4)

Reminder: H-PARTITION will place the pivot in the right partition unless the pivot is the smallest element in the arrays.

Question: If the pivot selected is the mth smallest value (1<mn) in the input array, what is the size of the left region after partitioning?

alt:"alt" height:330px center


Balanced Partitioning (5)

  • Question: What is the probability that the pivot selected is the mth smallest value in the array of size n?

  • 1/n (since all input permutations are equally likely)

  • Question: What is the probability that the left partition returned by H-PARTITION has size m, where 1<m<n?

  • 1/n (due to the answers to the previous 2 questions)


Balanced Partitioning (6)

  • Question: What is the probability that H-PARTITION returns a split that is more balanced than 0.1to0.9?
Probability=q=0.1n+10.9n11n=1n(0.9n10.1n1+1)=0.81n0.8 for large n

bg right:50% w:500px


Balanced Partitioning (7)

  • The probability that H-PARTITION yields a split that is more balanced than 0.1to0.9 is 80% on a random array.

  • Let Pα> be the probability that H-PARTITION yields a split more balanced than αto(1α), where 0<α0.5

  • Repeat the analysis to generalize the previous result


Balanced Partitioning (8)

  • Question: What is the probability that H-PARTITION returns a split that is more balanced than αto(1α)?

bg right:40% w:500px

Probability=q=αn+1(1α)n11n=1n((1α)n1αn1+1)=(12α)1n(12α) for large n

Balanced Partitioning (9)

  • We found Pα>=12α
  • Ex: P0.1>=0.8 and P0.01>=0.98
  • Hence, H-PARTITION produces a split
  • more balanced than a
    • 0.1to0.9 split 80% of the time
    • 0.01to0.99 split 98% of the time
  • less balanced than a
    • 0.1to0.9 split 20% of the time
    • 0.01to0.99 split 2% of the time

Intuition for the Average Case (1)

  • Assumption: All permutations are equally likely

  • Only for intuition; we’ll revisit this assumption later

  • Unlikely: Splits always the same way at every level

  • Expectation:

  • Some splits will be reasonably balanced

  • Some splits will be fairly unbalanced

  • Average case: A mix of good and bad splits

  • Good and bad splits distributed randomly thru the tree


Intuition for the Average Case (2)

  • Assume for intuition: Good and bad splits occur in the alternate levels of the tree
  • Good split: Best case split
  • Bad split: Worst case split

Intuition for the Average Case (3)

Compare 2-successive levels of avg case vs. 1 level of best case

alt:"alt" h:450px center


Intuition for the Average Case (4)

  • In terms of the remaining subproblems, two levels of avg case is slightly better than the single level of the best case

  • The avg case has extra divide cost of Θ(n) at alternate levels

  • The extra divide cost Θ(n) of bad splits absorbed into the Θ(n) of good splits.

  • Running time is still Θ(nlgn)

  • But, slightly larger hidden constants, because the height of the recursion tree is about twice of that of best case.

Intuition for the Average Case (5)

  • Another way of looking at it:
  • Suppose we alternate lucky, unlucky, lucky, unlucky,
  • We can write the recurrence as:
    • L(n)=2U(n/2)+Θ(n) lucky split (best)
    • U(n)=L(n1)+Θ(n) unlucky split (worst)
  • Solving:
L(n)=2(L(n/21)+Θ(n/2))+Θ(n)=2L(n/21)+Θ(n)=Θ(nlgn)
  • How can we make sure we are usually lucky for all inputs?

Summary: Quicksort Runtime Analysis (1)

  • Worst case: Unbalanced split at every recursive call
T(n)=T(1)+T(n1)+Θ(n)T(n)=Θ(n2)
  • Best case: Balanced split at every recursive call (extremely lucky)
T(n)=2T(n/2)+Θ(n)T(n)=Θ(nlgn)

Summary: Quicksort Runtime Analysis (2)

  • Almost-best case: Almost-balanced split at every recursive call
T(n)=T(n/10)+T(9n/10)+Θ(n)or T(n)=T(n/100)+T(99n/100)+Θ(n)or T(n)=T(αn)+T((1αn)+Θ(n)

for any constant α,0<α0.5


Summary: Quicksort Runtime Analysis (3)

  • For a random input array, the probability of having a split

  • more balanced than 0.1to0.9:80%

  • more balanced than 0.01to0.99:98%
  • more balanced than αto(1α):12α

  • for any constant α,0<α0.5


Summary: Quicksort Runtime Analysis (4)

  • Avg case intuition: Different splits expected at different levels

  • some balanced (good), some unbalanced (bad)

  • Avg case intuition: Assume the good and bad splits alternate

  • i.e. good split -> bad split -> good split -> …

  • T(n)=Θ(nlgn)
    • (informal analysis for intuition)

Randomized Quicksort

  • In the avg-case analysis, we assumed that all permutations of the input array are equally likely.
  • But, this assumption does not always hold
  • e.g. What if all the input arrays are reverse sorted?
    • Always worst-case behavior
  • Ideally, the avg-case runtime should be independent of the input permutation.
  • Randomness should be within the algorithm, not based on the distribution of the inputs.
  • i.e. The avg case should hold for all possible inputs

Randomized Algorithms (1)

  • Alternative to assuming a uniform distribution:
  • Impose a uniform distribution
  • e.g. Choose a random pivot rather than the first element
  • Typically useful when:
  • there are many ways that an algorithm can proceed
  • but, it’s difficult to determine a way that is always guaranteed to be good.
  • If there are many good alternatives; simply choose one randomly.

Randomized Algorithms (1)

  • Ideally:
  • Runtime should be independent of the specific inputs
  • No specific input should cause worst-case behavior
  • Worst-case should be determined only by output of a random number generator.

Randomized Quicksort (1)

  • Using Hoare’s partitioning algorithm:
R-QUICKSORT(A, p, r)
  if p < r then
    q = R-PARTITION(A, p, r)
    R-QUICKSORT(A, p, q)
    R-QUICKSORT(A, q+1, r)
R-PARTITION(A, p, r)
  s = RANDOM(p, r)
  exchange A[p] with A[s]
  return H-PARTITION(A, p, r)
  • Alternatively, permuting the whole array would also work
  • but, would be more difficult to analyze

Randomized Quicksort (2)

  • Using Lomuto’s partitioning algorithm:
R-QUICKSORT(A, p, r)
  if p < r then
    q = R-PARTITION(A, p, r)
    R-QUICKSORT(A, p, q-1)
    R-QUICKSORT(A, q+1, r)
R-PARTITION(A, p, r)
  s = RANDOM(p, r)
  exchange A[r] with A[s]
  return L-PARTITION(A, p, r)
  • Alternatively, permuting the whole array would also work
  • but, would be more difficult to analyze

Notations for Formal Analysis

  • Assume all elements in A[pr] are distinct

  • Let n=rp+1

  • Let rank(x)=|A[i]:pir and A[i]x|

  • i.e. rank(x) is the number of array elements with value less than or equal to x

  • A={5,9,7,6,8,1,4}

  • p=5,r=4
  • rank(5)=3
    • i.e. it is the 3rd smallest element in the array

Formal Analysis for Average Case

  • The following analysis will be for Quicksort using Hoare’s partitioning algorithm.

  • Reminder: The pivot is selected randomly and exchanged with A[p] before calling H-PARTITION

  • Let x be the random pivot chosen.

  • What is the probability that rank(x)=i for i=1,2,n ?

  • P(rank(x)=i)=1/n


Various Outcomes of H-PARTITION (1)

  • Assume that rank(x)=1
  • i.e. the random pivot chosen is the smallest element
  • What will be the size of the left partition (|L|)?
  • Reminder: Only the elements less than or equal to x will be in the left partition.

A={2p=x=pivot,|L|=19,7,6,8,5,4r}

p=2,r=4 pivot=x=2

TODO: convert to image...S6_P9


Various Outcomes of H-PARTITION (2)

  • Assume that rank(x)>1
  • i.e. the random pivot chosen is not the smallest element
  • What will be the size of the left partition (|L|)?
  • Reminder: Only the elements less than or equal to x will be in the left partition.
  • Reminder: The pivot will stay in the right region after H-PARTITION if rank(x)>1

A={2p,4,|L|=rank(x)17,6,8,5,pivot9r}

p=2,r=4 pivot=x=5

TODO: convert to image...S6_P10


Various Outcomes of H-PARTITION - Summary (1)

  • x:pivot

  • |L|:size of left region

  • P(rank(x)=i)=1/n for 1in

  • if rank(x)=1 then |L|=1

  • if rank(x)>1 then |L|=rank(x)1

  • P(|L|=1)=P(rank(x)=1)+P(rank(x)=2)

  • P(|L|=1)=2/n

  • P(|L|=i)=P(rank(x)=i+1) for 1<i<n

  • P(|L|=i)=1/n for 1<i<n


Various Outcomes of H-PARTITION - Summary (2)

alt:"alt" h:450px center


Average - Case Analysis: Recurrence (1)

x=pivot

T(n)=1n(T(1)+t(n1))rank:1+1n(T(1)+t(n1))rank:2+1n(T(2)+t(n2))rank:3+1n(T(i)+t(ni))rank:i+1+1n(T(n1)+t(1))rank:n+Θ(n)

Average - Case Analysis: Recurrence (2)

T(n)=1nq=1n1(T(q)+T(nq))+1n(T(1)+T(n1))+Θ(n)Note: 1n(T(1)+T(n1))=1n(Θ(1)+O(n2))=O(n)T(n)=1nq=1n1(T(q)+T(nq))+Θ(n)

for k=1,2,,n1 each term T(k) appears twice once for q=k and once for q=nk

T(n)=2nk=1n1T(k)+Θ(n)

Average - Case Analysis -Solving Recurrence: Substitution

  • Guess: T(n)=O(nlgn)
  • T(k)aklgk for k<n, for some constant a>0
T(n)=2nk=1n1T(k)+Θ(n)2nk=1n1aklgk+Θ(n)2ank=1n1klgk+Θ(n)
  • Need a tight bound for klgk

Tight bound for klgk (1)

  • Bounding the terms
  •  k=1n1klgkk=1n1nlgn=n(n1)lgnn2lgn
  • This bound is not strong enough because
  • T(n)2ann2lgn+Θ(n)
  • =2anlgn+Θ(n) couldn’t prove T(n)anlgn

Tight bound for klgk (2)

  • Splitting summations: ignore ceilings for simplicity

k=1n1klgkk=1n/21klgk+k=n/2n1klgk

  • First summation: lgk<lg(n/2)=lgn1
  • Second summation: lgk<lgn

Splitting: k=1n1klgkk=1n/21klgk+k=n/2n1klgk (3)

k=1n1klgk(lg(n1))k=1n/21k+lgnk=n/2n1k=lgnk=1n1kk=1n/21k=12n(n1)lgn12n2(n21)=12n2lgn18n212n(lgn1/2)
k=1n1klgk12n2lgn18n2 for lgn1/2n2

Substituting: - k=1n1klgk12n2lgn18n2 (4)

T(n)2ank=1n1klgk+Θ(n)2an(12n2lgn18n2)+Θ(n)=anlgn(a4nΘ(n))
  • We can choose a large enough so that a4nΘ(n)
T(n)anlgnT(n)=O(nlgn)

Q.E.D.


Medians and Order Statistics

  • ith order statistic: ith smallest element of a set of n elements

  • minimum: first order statistic

  • maximum: nth order statistic

  • median: “halfway point” of the set

i=(n+1)2 or i=(n+1)2

Selection Problem

  • Selection problem: Select the ith smallest of n elements

  • Naïve algorithm: Sort the input array A; then return A[i]

  • T(n)=θ(nlgn)

    • using e.g. merge sort (but not quicksort)
  • Can we do any better?


Selection in Expected Linear Time

  • Randomized algorithm using divide and conquer

  • Similar to randomized quicksort

  • Like quicksort: Partitions input array recursively

  • Unlike quicksort: Makes a single recursive call

    • Reminder: Quicksort makes two recursive calls
  • Expected runtime: Θ(n)

  • Reminder: Expected runtime of quicksort: Θ(nlgn)


Selection in Expected Linear Time: Example 1

  • Select the 2nd smallest element:
A={6,10,13,5,8,3,2,11}i=2
  • Partition the input array:
A={2,3,5,left subarray13,8,10,6,11right subarray}
  • make a recursive call to select the 2nd smallest element in left subarray

Selection in Expected Linear Time: Example 2

  • Select the 7th smallest element:
A={6,10,13,5,8,3,2,11}i=7
  • Partition the input array:
A={2,3,5,left subarray13,8,10,6,11right subarray}
  • make a recursive call to select the 4th smallest element in right subarray

Selection in Expected Linear Time (1)

R-SELECT(A,p,r,i)
  if p == r then 
    return A[p];
  q = R-PARTITION(A, p, r)
  k = qp+1;
  if i <= k then 
    return R-SELECT(A, p, q, i);
  else
    return R-SELECT(A, q+1, r, i-k);
A={|px(k smallest elements)|qx|r}x=pivot

Selection in Expected Linear Time (2)

A={|pxL|qxR|r}x=pivot

Selection in Expected Linear Time (2)

  • All elements in L all elements in R
  • L contains:
  • |L|=qp+1 = k smallest elements of A[p...r]
  • if i|L|=k then
    • search L recursively for its ith smallest element
  • else
    • search R recursively for its (ik)th smallest element

Runtime Analysis (1)

  • Worst case:
  • Imbalanced partitioning at every level and the recursive call always to the larger partition
={1,2,3,4,5,6,7,8recursive call}i=8={2,3,4,5,6,7,8recursive call}i=7

Runtime Analysis (2)

  • Worst case: Worse than the naïve method (based on sorting)
T(n)=T(n1)+Θ(n)T(n)=Θ(n2)
  • Best case: Balanced partitioning at every recursive level
T(n)=T(n/2)+Θ(n)T(n)=Θ(n)
  • Avg case: Expected runtime – need analysis T.B.D.

Reminder: Various Outcomes of H-PARTITION

  • x:pivot

  • |L|:size of left region

  • P(rank(x)=i)=1/n for 1in

  • if rank(x)=1 then |L|=1

  • if rank(x)>1 then |L|=rank(x)1

  • P(|L|=1)=P(rank(x)=1)+P(rank(x)=2)

  • P(|L|=1)=2/n

  • P(|L|=i)=P(rank(x)=i+1) for 1<i<n

  • P(|L|=i)=1/n for 1<i<n


Average Case Analysis of Randomized Select

  • To compute the upper bound for the avg case, assume that the ith element always falls into the larger partition.
A={|pxLeftPartition|qxRightPartition|r}x=pivot
  • We will analyze the case where the recursive call is always made to the larger partition
  • This will give us an upper bound for the avg case

Various Outcomes of H-PARTITION

alt:"alt" h:450px center


Average-Case Analysis of Randomized Select (1)

Recall:P(|L|=i)={2/nfor i=11/nfor i=2,3,,n1

Upper bound: Assume ith element always falls into the larger part.

T(n)1nT(max(1,n1))+1nq=1n1T(max(q,nq))+O(n)Note:1nT(max(1,n1))=1nT(n1)=1nO(n2)=O(n)(3 dot mean therefore) T(n)1nq=1n1T(max(q,nq))+O(n)

Average-Case Analysis of Randomized Select (2)

T(n)1nq=1n1T(max(q,nq))+O(n)
max(q,nq)={q if qn/2nq if q<n/2
  • n is odd: T(k) appears twice for k=n/2+1,n/2+2,,n1
  • n is even:T(n/2) appears once T(k) appears twice for k=n/2+1,n/2+2,,n1

Average-Case Analysis of Randomized Select (3)

  • Hence, in both cases:
q=1n1T(max(q,nq))+O(n)2q=n/2n1T(q)+O(n)T(n)2nq=n/2n1T(q)+O(n)

Average-Case Analysis of Randomized Select (4)

T(n)2nq=n/2n1T(q)+O(n)
  • By substitution guess T(n)=O(n)
  • Inductive hypothesis: T(k)ck,k<n
T(n)2nq=n/2n1ck+O(n)=2cn(k=1n1kk=1n/21k)+O(n)=2cn(12n(n1)12n2(n21))+O(n)

Average-Case Analysis of Randomized Select (5)

T(n)2cn(12n(n1)12n2(n21))+O(n)c(n1)c4n+c2+O(n)=cnc4nc2+O(n)=cn((c4n+c2)+O(n))cn
  • since we can choose c large enough so that (cn/4+c/2) dominates O(n)

Summary of Randomized Order-Statistic Selection

  • Works fast: linear expected time

  • Excellent algorithm in practise

  • But, the worst case is very bad: Θ(n2)

  • Blum, Floyd, Pratt, Rivest & Tarjan[1973] algorithms are runs in linear time in the worst case.

  • Generate a good pivot recursively


Selection in Worst Case Linear Time

//return i-th element in set S with n elements
SELECT(S, n, i)  

  if n <= 5 then

    SORT S and return the i-th element

  DIVIDE S into ceil(n/5) groups
  //first ceil(n/5) groups are of size 5, last group is of size n mod 5

  FIND median set M={m ,, m_ceil(n/5)}
  // m_j : median of j-th group

  x = SELECT(M,ceil(n/5),floor((ceil(n/5)+1)/2))

  PARTITION set S around the pivot x into L and R

  if i <= |L| then
    return  SELECT(L, |L|, i)
  else
    return  SELECT(R, n|L|, i|L|)

Selection in Worst Case Linear Time - Example (1)

  • Input: Array S and index i
  • Output: The ith smallest value
25916811273942156321436203322314173304121319721103413723405291824123828263543

Selection in Worst Case Linear Time - Example (2)

Step 1: Divide the input array into groups of size 5

25916811273942156321436203322314173304121319721103413723405291824123828263543group size=5

Selection in Worst Case Linear Time - Example (3)

Step 2: Compute the median of each group (Θ(n))

251611Medians89394227615363332201422311734413019132213410173740292353828241218263543
  • Let M be the set of the medians computed:
  • M={11,27,32,17,19,10,29,24,35}

Selection in Worst Case Linear Time - Example (4)

Step 3: Compute the median of the median group M

xSELECT(M,|M|,(|M|+1)/2) where |M|=n/5

  • Let M be the set of the medians computed:

  • M={11,27,32,17,19,10,29,24Median,35}

  • Median=24

  • The runtime of the recursive call: T(|M|)=T(n/5)


Selection in Worst Case Linear Time - Example (5)

Step 4: Partition the input array S around the median-of-medians x

25916811273942156321436203322314173304121319721103413723405291824123828263543

Partition S around x=24

Claim: Partitioning around x is guaranteed to be well-balanced.


Selection in Worst Case Linear Time - Example (6)

  • M : Median, M : Median of Medians
413019M132213410172231173425161189382824M12183633322014374029235394227615263543
  • About half of the medians greater than x=24 (about n/10)

Selection in Worst Case Linear Time - Example (7)

alt:"alt" h:500px center


Selection in Worst Case Linear Time - Example (8)

alt:"alt" h:500px center


Selection in Worst Case Linear Time - Example (9)

S={25916811273942156321436203322314173304121319721103413723405291824123828263543}
  • Partitioning S around x=24 will lead to partitions of sizes 3n/10 and 7n/10 in the worst case.

Step 5: Make a recursive call to one of the partitions

if i <= |L| then 
  return SELECT(L,|L|,i)
else 
  return SELECT(R,n-|L|,i-|L|)

Selection in Worst Case Linear Time

//return i-th element in set S with n elements
SELECT(S, n, i)  

  if n <= 5 then

    SORT S and return the i-th element

  DIVIDE S into ceil(n/5) groups
  //first ceil(n/5) groups are of size 5, last group is of size n mod 5

  FIND median set M={m ,, m_ceil(n/5)}
  // m_j : median of j-th group

  x = SELECT(M,ceil(n/5),floor((ceil(n/5)+1)/2))

  PARTITION set S around the pivot x into L and R

  if i <= |L| then
    return  SELECT(L, |L|, i)
  else
    return  SELECT(R, n|L|, i|L|)

Choosing the Pivot (1)

  1. Divide S into groups of size 5

alt:"alt" h:450px center


Choosing the Pivot (2)

  • Divide S into groups of size 5
  • Find the median of each group

alt:"alt" h:450px center


Choosing the Pivot (3)

  • Divide S into groups of size 5
  • Find the median of each group
  • Recursively select the median x of the medians

alt:"alt" h:450px center


Choosing the Pivot (4)

  • At least half of the medians x
  • Thus m=n/5/2 groups contribute 3 elements to R except possibly the last group and the group that contains x, |R|3(m2)3n106

alt:"alt" h:400px center


Choosing the Pivot (5)

  • Similarly |L|3n106
  • Therefore, SELECT is recursively called on at most n(3n106)=7n10+6 elements

alt:"alt" h:400px center


Selection in Worst Case Linear Time (1)

alt:"alt" h:500px center


Selection in Worst Case Linear Time (2)

  • Thus recurrence becomes
  • T(n)T(n5)+T(7n10+6)+Θ(n)
  • Guess T(n)=O(n) and prove by induction
  • Inductive step:
T(n)cn/5+c(7n/10+6)+Θ(n)cn/5+c+7cn/10+6c+Θ(n)=9cn/10+7c+Θ(n)=cn[c(n/107)Θ(n)]cn( for large c)
  • Work at each level of recursion is a constant factor (9/10) smaller

References


EndOfWeek3CourseModule


Last update: October 6, 2022
Back to top