PDSA - Week 8Divide and conquer Divide and conquer exampleCounting inversionsClosest pair of pointsInteger multiplicationQuick select and Fast selectMedian of Medians(MoM)Fast select using MOMRecursion trees
Break up a problem into disjoint subproblems
Combine these subproblem solutions efficiently
Examples
Merge sort
Quicksort
Compare your profile with other customers
Identify people who share your likes and dislikes
No inversions – rankings identical
Every pair inverted – maximally dissimilar
Number of inversions ranges from 0 to n(n – 1) / 2
An inversion is a pair (i, j), i < j, where j appears before i
Recurrence:
Implementation
x1def mergeAndCount(A,B):
2 (m,n) = (len(A),len(B))
3 (C,i,j,k,count) = ([],0,0,0,0)
4 while k < m+n:
5 if i == m:
6 C.append(B[j])
7 j += 1
8 k += 1
9 elif j == n:
10 C.append(A[i])
11 i += 1
12 k += 1
13 elif A[i] < B[j]:
14 C.append(A[i])
15 i += 1
16 k += 1
17 else:
18 C.append(B[j])
19 j += 1
20 k += 1
21 count += m-i
22 return(C,count)
23
24def inversionCount(A):
25 n = len(A)
26 if n <= 1:
27 return(A,0)
28 (L,countL) = inversionCount(A[:n//2])
29 (R,countR) = inversionCount(A[n//2:])
30 (B,countB) = mergeAndCount(L,R)
31 return(B,countL + countR + countB)
32L = [2,4,3,1,5]
33print(inversionCount(L)[1])
Output
xxxxxxxxxx
114 # 4 is the number of inversions
Several objects on screen
Basic step: find closest pair of objects
There is a clever algorithm that takes time
Given n points
Recurrence:
Overall:
Pseudocode
xxxxxxxxxx
111def ClosestPair(Px,Py):
2 if len(Px) <= 3:
3 compute pairwise distances
4 return closest pair and distance
5 Construct (Qx,Qy), (Rx,Ry)
6 (q1,q2,dQ) = ClosestPair(Qx,Qy)
7 (r1,r2,dR) = ClosestPair(Rx,Ry)
8 Construct Sy from Qy,Ry
9 Scan Sy, find (s1,s2,dS)
10 return (q1,q2,dQ), (r1,r2,QR), (s1,s2,dS)
11 #depending on which of dQ, dR, dS is minimum
Implementation
xxxxxxxxxx
601import math
2
3# Returns eucledian disatnce between points p and q
4def distance(p, q):
5 return math.sqrt(math.pow(p[0] - q[0],2) + math.pow(p[1] - q[1],2))
6
7def minDistanceRec(Px, Py):
8 s = len(Px)
9 # Given number of points cannot be less than 2.
10 # If only 2 or 3 points are left return the minimum distance accordingly.
11 if (s == 2):
12 return distance(Px[0],Px[1])
13 elif (s == 3):
14 return min(distance(Px[0],Px[1]), distance(Px[1],Px[2]), distance(Px[2],Px[0]))
15
16 # For more than 3 points divide the poitns by point around median of x coordinates
17 m = s//2
18 Qx = Px[:m]
19 Rx = Px[m:]
20 xR = Rx[0][0] # minimum x value in Rx
21
22 # Construct Qy and Ry in O(n) rather from Py
23 Qy=[]
24 Ry=[]
25 for p in Py:
26 if(p[0] < xR):
27 Qy.append(p)
28 else:
29 Ry.append(p)
30
31 # Extract Sy using delta
32 delta = min(minDistanceRec(Qx, Qy), minDistanceRec(Rx, Ry))
33 Sy = []
34 for p in Py:
35 if abs(p[0]-xR) <= delta:
36 Sy.append(p)
37
38 #print(xR,delta,Sy)
39 sizeS = len(Sy)
40 if sizeS > 1:
41 minS = distance(Sy[0], Sy[1])
42 for i in range(1, sizeS-1):
43 for j in range(i, min(i+15, sizeS-1)):
44 minS = min(minS, distance(Sy[i], Sy[j+1]))
45 return min(delta, minS)
46 else:
47 return delta
48
49def minDistance(Points):
50 Px = sorted(Points)
51 Py = Points
52 Py.sort(key=lambda x: x[-1])
53 #print(Px,Py)
54 return round(minDistanceRec(Px, Py), 2)
55
56
57
58pts = [(2, 15), (40, 5), (20, 1), (21, 14), (1,4), (3, 11)]
59result = minDistance(pts)
60print(result)
Output
xxxxxxxxxx
114.12
Implementation
xxxxxxxxxx
171# here 10 represent base of input numbers x and y
2def Fast_Multiply(x,y,n):
3 if n == 1:
4 return x * y
5 else:
6 m = n/2
7 xh = x//10**m
8 xl = x % (10**m)
9 yh = y//10**m
10 yl = y % (10**m)
11 a = xh + xl
12 b = yh + yl
13 p = Fast_Multiply(xh, yh, m)
14 q = Fast_Multiply(xl, yl, m)
15 r = Fast_Multiply(a, b, m)
16 return p*(10**n) + (r - q - p) * (10**(n/2)) + q
17print(Fast_Multiply(3456,8902,4))
Output
xxxxxxxxxx
1130765312.0
Implementation
xxxxxxxxxx
231def quickselect(L,l,r,k): # k-th smallest in L[l:r]
2 if (k < 1) or (k > r-l):
3 return(None)
4
5 (pivot,lower,upper) = (L[l],l+1,l+1)
6 for i in range(l+1,r):
7 if L[i] > pivot: # Extend upper segment
8 upper = upper + 1
9 else: # Exchange L[i] with start of upper segment
10 (L[i], L[lower]) = (L[lower], L[i])
11 (lower,upper) = (lower+1,upper+1)
12 (L[l],L[lower-1]) = (L[lower-1],L[l]) # Move pivot
13 lower = lower - 1
14
15 # Recursive calls
16 lowerlen = lower - l
17 if k <= lowerlen:
18 return(quickselect(L,l,lower,k))
19 elif k == (lowerlen + 1):
20 return(L[lower])
21 else:
22 return(quickselect(L,lower+1,r,k-(lowerlen+1)))
23print(quickselect([5,3,7,2,1],0,5,2))
Output
xxxxxxxxxx
112
Divide L into blocks of 5
Find the median of each block (brute force)
Let M be the list of block medians
Recursively apply the process to M
We can visualize the blocks as follows
Each block of 5 is arranged in ascending order, top to bottom
Block medians are the middle row
The median of block medians lies between 3len(L)/10 and 7len(L)/10
Implementation
xxxxxxxxxx
121def MoM(L): # Median of medians
2 if len(L) <= 5:
3 L.sort()
4 return(L[len(L)//2])
5 # Construct list of block medians
6 M = []
7 for i in range(0,len(L),5):
8 X = L[i:i+5]
9 X.sort()
10 M.append(X[len(X)//2])
11 return(MoM(M))
12print(MoM([4,3,5,6,2,1,8,9,7,10,13,15,18,17,11]))
Output
xxxxxxxxxx
118
Implementation
xxxxxxxxxx
421def MoM(L): # Median of medians
2 if len(L) <= 5:
3 L.sort()
4 return(L[len(L)//2])
5 # Construct list of block medians
6 M = []
7 for i in range(0,len(L),5):
8 X = L[i:i+5]
9 X.sort()
10 M.append(X[len(X)//2])
11 return(MoM(M))
12
13
14
15def fastselect(L,l,r,k): # k-th smallest in L[l:r]
16 if (k < 1) or (k > r-l):
17 return(None)
18
19 # Find MoM pivot and move to L[l]
20 pivot = MoM(L[l:r])
21 pivotpos = min([i for i in range(l,r) if L[i] == pivot])
22 (L[l],L[pivotpos]) = (L[pivotpos],L[l])
23
24 (pivot,lower,upper) = (L[l],l+1,l+1)
25 for i in range(l+1,r):
26 if L[i] > pivot: # Extend upper segment
27 upper = upper + 1
28 else: # Exchange L[i] with start of upper segment
29 (L[i], L[lower]) = (L[lower], L[i])
30 (lower,upper) = (lower+1,upper+1)
31 (L[l],L[lower-1]) = (L[lower-1],L[l]) # Move pivot
32 lower = lower - 1
33
34 # Recursive calls
35 lowerlen = lower - l
36 if k <= lowerlen:
37 return(fastselect(L,l,lower,k))
38 elif k == (lowerlen + 1):
39 return(L[lower])
40 else:
41 return(fastselect(L,lower+1,r,k-(lowerlen+1)))
42print(fastselect([4,3,5,6,2,1,8,9,7,10,13,15,18,17,11],0,15,4))
Output
xxxxxxxxxx
114
Recursion tree-Rooted tree with one node for each recursive subproblem
Value of each node is time spent on that subproblem excluding recursive calls
Concretely, on an input of size
Resulting recurrence:
Root of recursion tree for
Root has
Each node at level