# SMAWK totally monotone matrix searching algorithm # David Eppstein, UC Irvine, 17 Mar 2002 # reference: # A. Aggarwal, M. Klawe, S. Moran, P. Shor and R. Wilber, # Geometric applications of a matrix searching algorithm, # Algorithmica 2, pp. 195-208 (1987). def smawk(rows,cols,lookup): '''Search for row-maxima in a 2d totally monotone matrix M[i,j]. The input is specified by a list of row indices, a list of column indices, and a function "lookup" satisfying lookup(i,j) = M[i,j]. The matrix must satisfy the totally monotone ordering property: if i occurs before i' in rows, j occurs before j' in cols, and M[i,j] < M[i,j'], then also M[i',j] < M[i',j']. The result is returned as a dictionary mapping row i to the column j containing the largest value M[i,j]. Ties are broken in favor of earlier columns. The number of calls to lookup is O(len(rows)+len(cols)).''' # base case of recursion if not rows: return {} # reduce phase: make number of columns at most equal to number of rows stack = [] for c in cols: while len(stack) >= 1 and \ lookup(rows[len(stack)-1],stack[-1]) < lookup(rows[len(stack)-1],c): stack.pop() if len(stack) != len(rows): stack.append(c) cols = stack # recursive call to search for every odd row result = smawk([rows[i] for i in xrange(1,len(rows),2)],cols,lookup) # go back and fill in the even rows c = 0 for r in xrange(0,len(rows),2): row = rows[r] if r == len(rows) - 1: cc = len(cols)-1 # if r is last row, search through last col else: cc = c # otherwise only until pos of max in row r+1 target = result[rows[r+1]] while cols[cc] != target: cc += 1 result[row] = max([ (lookup(row,cols[x]),-x,cols[x]) \ for x in xrange(c,cc+1) ]) [2] c = cc return result # Example application: All nearest neighbors of points on a line # Exercise: verify that negDist satisfies the totally monotone matrix property. def linearNearestNeighbors(A,B): '''Takes as input a set of points A and B in some Euclidean space. A lies on the x-axis and is given as a sequence of real numbers; B is given in Cartesian coordinates (tuples of real numbers). Output is a dict mapping each point in A to the nearest point in B.''' # negate squared Euclidean distance to convert minimization of distance # into a maximization problem suitable for SMAWK algorithm def square(x): return x*x def negDist(a,b): dist = square(b[0]-a) for i in range(1,len(b)): dist += square(b[i]) return -dist A.sort() # make sure points are sorted, omit if already sorted B.sort() return smawk(A,B,negDist)