130 likes | 147 Views
Learn how to efficiently count inversions in an array using a Divide and Conquer approach with a step-by-step example. Explore the concept of merging and counting to achieve a faster algorithm. Master the application of the Divide and Conquer technique, including the Master Theorem for recursion analysis.
E N D
Example 2 Counting Inversions • Goal: Given array a[], count the number of pairs (i,j) such that i < j but a[i] > a[j]. • Input: Array a[] = {6, 2, 4, 1, 5, 3, 7, 8} • Answer = 9
How to count inversions more efficiently? • b[] = {6, 2, 4, 1}, 5 inversionsc[] = {5, 3, 7, 8}, 1 inversion • For each number in b, want to know how many numbers in c are smaller.6: 2, 2: 0, 4: 1, 1: 0, result = 2+0+1+0 = 3. • Much easier if c[] is sorted! • Idea: Can sort the arrays when we are counting inversions.
Sort&Count Sort&Count(a[]) IF Length(a) < 2 THENRETURN (a[], 0). (Base Case) Partition a[] evenly into two arrays b[], c[]. (Divide) (b[], count_b) = Sort&Count(b[]) (c[], count_c) = Sort&Count(c[]) (Conquer) (a[], count_bc) = Merge&Count(b[], c[]). (Merge) RETURN a[], count_b+count_c+count_bc.
Merge&Count • b[] = {1, 2, 4, 6} c[] = {3, 5, 7, 8} • {1, 2, 3, 4, 5, 6, 7, 8} • When 4 goes into the array, pointer in c[] is pointing at 5 (2nd element), so 4 is larger than 1 element in c[]. • When 6 goes into the array, pointer in c[] is pointing at 7 (3rd element), so 6 is larger than 2 elements in c[].
Merge&Count Merge&Count(b[], c[]) a[] = empty i = 1, j = 1 count = 0 WHILEi <= Length(b[]) OR j <= Length(c[]) IF b[i] < c[j] THEN a.append(b[i]); i = i+1 count = count + (j - 1) ELSE a.append(c[j]); j = j+1 RETURN a[], count The i-th element of a[] is smaller than c[j], but larger than c[1,2,…, j-1]
Running Time • Merging still take O(n) time, so we still have • T(n) = 2T(n/2) + A n for some constant A > 0 • T(n) = O(n log n)
Example 3 Integer Multiplication • Input: two positive integers a, b • Output: a×b • What if a = 73489553479834257983745 andb = 197878967893267834267324789? • Naïve algorithm: O(n2) time (learned in elementary school) ? multiply(a, b) RETURN a*b
Divide and Conquer • a = 123,456 b = 654,321 • How to divide? • Write a = 123,000 + 456, b = 654,000 + 321. • Thena*b = 123*654*106 + (123*321+456*654)*103 + 456*321.
First attempt • Partition a = a1*10n/2+a2, b = b1*10n/2+b2 • Recursively compute A = a1*a2, B = a2*b1, C = a1*b2, D = a2*b2 • Output A*10n+(B+C)*10n/2+D Multiply(a, b) (wlog assume n = length(a) = length(b), pad 0 for the shorter number) IF Length(a) <= 1 THENRETURN a*b Partition a into a = a1 * 10n/2+a2, b = b1*10n/2+b2 A = Multiply(a1, b1) B = Multiply(a2, b1) C = Multiply(a1, b2) D = Multiply(a2, b2) RETURN A*10n+(B+C)*10n/2 + D Need an algorithm for adding long numbers. Standard algorithm suffices (O(n))
Improving the algorithm To make a divide and conquer algorithm faster • Make the merging step faster • Make the sub-problems smaller • Reduce the number of sub-problems • Observation:(a1*b2+a2*b1) = (a1+a2)*(b1+b2) – a1*b1 – a2*b2
Improved Algorithm • Partition a = a1*10n/2+a2, b = b1*10n/2+b2 • Recursively compute A = a1*b1, B = a2*b2, C = (a1+a2)*(b1+b2) • Output A*10n+(C-A-B)*10n/2+B Multiply(a, b) (wlog assume n = length(a) = length(b), pad 0 for the shorter number) IF Length(a) <= 1 THENRETURN a*b Partition a into a = a1 * 10n/2+a2, b = b1*10n/2+b2 A = Multiply(a1, b1) B = Multiply(a2, b2) C = Multiply(a1+a2, b1+b2) RETURN A*10n+(C-A-B)*10n/2 + B
Master Theorem • “Cheat sheet” for simple recursions • Theorem: If T(n) = aT(n/b) + f(n), then • 1. If f(n) = O(nc), c < logba • 2. If f(n) = θ(nclogtn), c = logba • 3. If f(n) = θ(nc), c > logba