Thursday, 25 May 2017

Chapter 2 4 Problem, Introduction to Algorithms, 3rd Edition Thomas H. Cormen

2-4 Inversions

Let A[1..n] be an array of n distinct numbers. If i<j and A[i]>A[j], then the pair (i,j) is called an inversion of A.

  1. List the five inversions of the array 2,3,8,6,1.  
  2. What array with elements from the set 1,2,,n has the most inversions? How many does it have? 
  3. What is the relationship between the running time of insertion sort and the number of inversions in the input array? Justify your answer.  
  4. Give an algorithm that determines the number of inversions in any permutation on n elements in Θ(nlgn) worst-case time. (Hint: Modify merge sort.)
1. List of Inversions
Inversions in the given array are: (1, 5), (2, 5), (3, 4), (3, 5), and (4, 5). (Note: Inversions are specified by indices of the array, not by values.)

2. Array With Most Inversions
The array with elements from the set 1,2,,n
with the most inversions will have the elements in reverse sorted order, i.e. n,n1,,2,1.

 As the array has n unique elements in reverse sorted order, for every unique pair of (i,j), there will be an inversion. So, total number of inversion = number of ways to choose 2 distinct integer from the set 1,2,,n = (n2) = n(n1)2.

3. Relationship With Insertion Sort
If we take look at the pseudocode for insertion sort with the definition of inversions in mind, we will realize that the more the number of inversions in an array, the more times the inner while loop will run. The reason being more inversions means most of the array is reverse sorted, i.e. more swaps to perform in the while loop.
So, the higher the number of inversions in an array, the longer insertion sort will take to sort the array.

4. Algorithm to Calculate Inversions
Although a hint to modify merge sort is already given, without that also we should think of divide-and-conquer algorithms whenever we see running time of Θ(lgn).

As was done in merge sort, we need to recursively divide the array into halfs and count number of inversions in the sub-arrays. This will result in lgn
steps and Θ(n) operations in each step to count the inversions. All in all a Θ(nlgn) algorithm.

def merge(items, p, q, r):
    L = items[p:q+1]
    R = items[q+1:r+1]
    i = j = 0
    k = p
    inversions = 0
    while i < len(L) and j < len(R):
        if(L[i] < R[j]):
            items[k] = L[i]
            i += 1
            items[k] = R[j]
            j += 1
            inversions += (len(L) - i)
        k += 1
    if(j == len(R)):
        items[k:r+1] = L[i:]
    return inversions

def mergesort(items, p, r):
    inversions = 0
    if(p < r):
        q = (p+r)/2
        inversions += mergesort(items, p, q)
        inversions += mergesort(items, q+1, r)
        inversions += merge(items, p, q, r)
    return inversions

items = [4,3,2,1,17]
inversions = mergesort(items, 0, len(items)-1)
print items,inversions

Chapter 2 3 Problem, Introduction to Algorithms, 3rd Edition Thomas H. Cormen

2-3 Correctness of Horner’s rule

The following code fragment implements Horner’s rule for evaluating a  polynomial
$$ \begin {align} P(x) & = \sum _{k = 0}^n a_k x^k \\ & = a_0 + x(a_1 + x(a_2 +· · · + x(a_{n − 1} + xa_n) · · ·)) \end {align} $$

given the coefficients a 0 ; a 1 ; : : : ; a n and a value for x:

y = 0
for i = n downto 0
 y = a_i + x * y

  1. In terms of Θ-notation, what is the asymptotic running time of this code fragment for Horner’s rule? 
  2. Write pseudocode to implement the naive polynomial-evaluation algorithm that computes each term of the polynomial from scratch. What is the running time of this algorithm? How does it compare to Horner’s rule? 
  3. Consider the following loop invariant:. At the start of each iteration of the for loop of lines 2-3, y=n(i+1)k=0ak+i+1xk Interpret a summation with no terms as equaling 0. Your proof should follow the structure of the loop invariant proof presented in this chapter and should show that, at termination, y=nk=0akxk
  4.  Conclude by arguing that the given code fragment correctly evaluates a polynomial characterized by the coefficients a0,a1,...,an.

1. Asymptotic Running Time

From the pseudocode of Horner’s Rule, the algorithm runs in a loop for all the elements, i.e. it runs at Θ(n) time.

2. Comparison with Naive Algorithm

Pseudocode for NAIVE-POLY-EVAL(A, x), where A is the array of length n+1 consisting of the coefficients a0,a1,...,an.

y = 0
for i = 1 to A.length
    m = 1
    for j = 1 to i - 1
        m = m * x
    y = y + A[i] * m

The above algorithm runs with for inside another for loop j multiplications to evaluate ajxj and $(n - 1)$ additions in total to evaluate a polynomial. Hence, it does nj=0j=n(n+1)/2 multiplications and (n1) additions. Therefore, the algorithm runs at Θ(n2)
This algorithm is obviously worse than Horner’s rule which runs at linear time.

3. Loop Invariant for the While Loop

Initialization: At the start of the first iteration, we have i=n. So,

As the sum is zero, the loop invariant holds after the first loop.
Maintenance: From the loop invariant, for any arbitrary 0<=i<n
, at the start of the i-th iteration of the while loop of lines 3–5, y=n(i+1)k=0ak+i+1xk
Now, after the i-th iteration,

So, the loop invariant also holds after the loop.
We make two assumption:
  1.  k=k+1 : This is valid as k is nothing but the summation parameter.
  2. i=i1 : This holds as this is precisely the operation done in line 5.
Termination: When the loop terminates, we have i=1. So,

 This is precisely what we wanted to calculate.

4. Correctness Argument
When Horner’s rule terminates it successfully evaluates the polynomial as it intended to. This means the algorithm is correct.