Peak Finding
2015-09-15 18:12
441 查看
1D Peak Finding
Objective
Given an array A with n elements, find the index i of the peak element A[i] where A[i] >= A[i - 1]and A[i] >= A[i + 1]. For elements on the boundaries of the array, the element only needs to be greater than or equal to its lone neighbor to be considered a peak. Or, say A[-1] = A
= ∞.
Algorithm
Take the middle element fo A, A[n2],and compare that element to its neighbors
If the middle element is greater than or equal to its neighbors, then by definition, that element is a peak. Reture its index n2
Else, if the element to the left is greater than the middle element, then recurse and use this algorithm on the left half of the array, not including the middle element.
Else, the element to the right must be greater than the middle element. Recurse and use this algorithm on the right half of the array, not including the middle element.
Runtime Analysis
T(n) = T(n2)+ c
T(n) = T(n4)
+ c + c
T(n) = T(n8)
+ c + c + c
T(n) = T(n2k)
+ ck
Substitute k = logn
T(n) = T(n2logn)
+ clogn
= T(1) + c logn
= O(log n)
In [1]:
L = [4, 8, 5, 6, 9, 10, 13, 4, 5 ,6, 0] print L[len(L) / 2:] print L[: len(L) / 2] print len(L) / 2 L[len(L) / 2]
[10, 13, 4, 5, 6, 0] [4, 8, 5, 6, 9] 5
Out[1]:
10
In [2]:
def peakFindStraight(L): ''' Find a peak in a straight way Runtime: O(len(L)) ''' if len(L) == 0: return None if len(L) == 1: return 0 for i in xrange(len(L)): if i == 0: if L[i] >= L[i + 1]: return i elif i == len(L) - 1: if L[i] >= L[i - 1]: return i else: if L[i] >= L[i + 1] and L[i] >= L[i - 1]: return i
In [3]:
print peakFindStraight(L)
1
In [4]:
def peakFinding(L, low, high): ''' Peak finding recursive way. It is similar to binary search. Runtime: O(log(len(L))) ''' mid = low + (high - low) / 2 if mid == 0 or mid == len(L) - 1 or (L[mid] >= L[mid - 1] and L[mid] >= L[mid + 1]): return mid elif L[mid - 1] > L[mid]: return peakFinding(L, low, mid - 1) else: return peakFinding(L, mid + 1, high)
In [5]:
print L peakindex = peakFinding(L, 0, len(L) - 1) print L[peakindex]
[4, 8, 5, 6, 9, 10, 13, 4, 5, 6, 0] 6
2D Peak Finding
Objective
Given an n*n matrix M, find the indices of a peak element M[i][j] where the element is greaterthan or equal to its neighbors, M[i + 1][j], M[i - 1][j], M[i][j + 1], and M[i][j - 1]. For elements
on the boundaries of the matrix, the element only needs to be greater than or equal to the neighbors
it has to be considered a peak.
Algorithm 2D:
Pick middle column j = m / 2
Find global maximum on column j at (i, j)
Compare (i, j - 1), (i, j), (i, j + 1)
Pick left columns of (i, j - 1) > (i, j)
Similarly for right
(i, j) is a 2D-peak if neither condition holds <- WHY
Solve the new problem with half the number of columns.
When you have a single column, find global maximum and you're done.
The complexity is:
If T(n, m) denotes work required to solve problem with n rows and m columns
T(n, m) = T(n, m/2) + Θ(n) (to
find global maximum on a column \-- (n rows))
T(n, m) = Θ(n)+⋯+Θ(n)logm
= Θ(nlog
m) = Θ(nlog
n) if
m = n
In [6]:
problemMatrix = [ [ 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2], [ 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3], [ 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4], [ 7, 8, 9, 10, 11, 10, 9, 8, 7, 6, 5], [ 8, 9, 10, 11, 12, 11, 10, 9, 8, 7, 6], [ 7, 8, 9, 10, 11, 10, 9, 8, 7, 6, 5], [ 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4], [ 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3], [ 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2], [ 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1], [ 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0] ]
In [7]:
def getDimensions(array): ''' Gets the dimensions for a two-dimensional array Runtime: O(len(array)) ''' rows = len(array) cols = 0 for row in array: if len(row) > cols: cols = len(row) return (rows, cols)
In [8]:
#test getDimensions getDimensions(problemMatrix)
Out[8]:
(11, 11)
In [9]:
class PeakProblem(object): ''' A class representing an instance of a peak-finding problem ''' def __init__(self, array, bounds): ''' A method for initializing an instance of the PeakProblem class. Takes an array and an argument indicating which rows to include. Runtime: O(1) ''' (startRow, startCol, numRow, numCol) = bounds self.array = array self.bounds = bounds self.startRow = startRow self.startCol = startCol self.numRow = numRow self.numCol = numCol def get(self, location): ''' Returns the value of the array at the given location, offset by the coordinates (startRow, startCol). Runtime: O(1) ''' (r, c) = location if not (0 <= r and r < self.numRow): return 0 if not (0 <= c and c < self.numCol): return 0 return self.array[self.startRow + r][self.startCol + c] def getBetterNeighbor(self, location): ''' If (r, c) has a better neighbor, return the neighbor. Otherwise, return the location (r, c) ''' (r, c) = location best = location if r - 1 >= 0 and self.get((r-1, c)) > self.get(best): best = (r - 1, c) if c - 1 >= 0 and self.get((r, c - 1)) > self.get(best): best = (r, c - 1) if r + 1 < self.numRow and self.get((r + 1, c)) > self.get(best): best = (r + 1, c) if c + 1 < self.numCol and self.get((r, c + 1)) > self.get(best): best = (r, c + 1) return best def getMaximum(self, locations): ''' Finds the location in the current problem with the greatest value ''' (bestLoc, bestVal) = (None, 0) for loc in locations: if bestLoc is None or self.get(loc) > bestVal: (bestLoc, bestVal) = (loc, self.get(loc)) return bestLoc def isPeak(self, location): ''' Returns true if the given location is a peak in the current subproblem. ''' return (self.getBetterNeighbor(location) == location) def getSubproblem(self, bounds): ''' Returns a subproblem with the given bounds. The bounds is a quadruple of numbers: (starting row, starting column, # of rows, # of columns). ''' (sRow, sCol, nRow, nCol) = bounds newBounds = (self.startRow + sRow, self.startCol + sCol, nRow, nCol) return PeakProblem(self.array, newBounds) def getSubproblemContaining(self, boundList, location): ''' Returns the subproblem containing the given location. Picks the first of the subproblems in the list which satisfies that constraint, and then constructs the subproblem using getSubproblem(). Runtime: O(len(boundList)) ''' (row, col) = location for (sRow, sCol, nRow, nCol) in boundList: if sRow <= row and row < sRow + nRow: if sCol <= col and col < sCol + nCol: return self.getSubproblem((sRow, sCol, nRow, nCol)) # shouldn't reach here return self def getLocationInSelf(self, problem, location): ''' Remaps the location in the given problem to the same location in the problem that this function is being called from. Runtime: O(1) ''' (row, col) = location newRow = row + problem.startRow - self.startRow newCol = col + problem.startCol - self.startCol return (newRow, newCol) def printPeakProblem(self): ''' Print the two-dimensional array Runtime: O(self.numRow * self.numCol) ''' for i in range(self.startRow, self.numRow): for j in range(self.startCol, self.numCol): print '{:4}'.format(self.get((i, j))), print
In [10]:
def createProblem(array): ''' Constructs an instance of the PeakProblem object for the given array, using bounds derived from the array using the getDimensions function. Runtime: O(len(arry)) ''' (rows, cols) = getDimensions(array) return PeakProblem(array, (0, 0, rows, cols))
In [11]:
pb = createProblem(problemMatrix) print pb.startRow pb.printPeakProblem()
0
4 5 6 7 8 7 6 5 4 3 2
5 6 7 8 9 8 7 6 5 4 3
6 7 8 9 10 9 8 7 6 5 4
7 8 9 10 11 10 9 8 7 6 5
8 9 10 11 12 11 10 9 8 7 6
7 8 9 10 11 10 9 8 7 6 5
6 7 8 9 10 9 8 7 6 5 4
5 6 7 8 9 8 7 6 5 4 3
4 5 6 7 8 7 6 5 4 3 2
3 4 5 6 7 6 5 4 3 2 12 3 4 5 6 5 4 3 2 1 0
In [12]:
def crossProduct(list1, list2): ''' Returns all pairs with one item from the first list and one item from the second list. (Cartesian product of the two lists) The code is equivalent to the following list comprehension: return [(a, b) for a in list1 for b in list2] but for easier reading and analysis, we have included more explicit code. ''' answer = [] for a in list1: for b in list2: answer.append((a, b)) return answer
In [13]:
mid = pb.numCol // 2 divider = crossProduct(range(pb.numRow), [mid]) print divider bestLoc = pb.getMaximum(divider) bestLoc
[(0, 5), (1, 5), (2, 5), (3, 5), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5), (10, 5)]
Out[13]:
(4, 5)
In [14]:
def algorithm1(problem): # if it's empty, we're done if problem.numRow <= 0 or problem.numCol <= 0: return None # the recursive subproblem will involve half the number of columns mid = problem.numCol // 2 # information about the two subproblems (subStartR, subNumR) = (0, problem.numRow) (subStartC1, subNumC1) = (0, mid) (subStartC2, subNumC2) = (mid + 1, problem.numCol - (mid + 1)) subproblems = [] subproblems.append((subStartR, subStartC1, subNumR, subNumC1)) subproblems.append((subStartR, subStartC2, subNumR, subNumC2)) # get a list of all locations in the dividing column divider = crossProduct(range(problem.numRow), [mid]) # find the maximum in the dividing column bestLoc = problem.getMaximum(divider) # see if the maximum value we found on the dividing line has a better # neighbor (which can't be on the dividing line, because we know that # this location is the best on the dividing line) neighbor = problem.getBetterNeighbor(bestLoc) # this is a peak, so return it if neighbor == bestLoc: return bestLoc # otherwise, figure out which subproblem contains the neighbor, and # recurse in that half sub = problem.getSubproblemContaining(subproblems, neighbor) result = algorithm1(sub) return problem.getLocationInSelf(sub, result)
In [15]:
# test algorithm1peak = algorithm1(pb)
if pb.isPeak(peak):
print(str(peak) + " => is a peak")
(4, 4) => is a peak
In [16]:
def algorithm2(problem, location = (0, 0)): # if it's empty, we're done if problem.numRow <= 0 or problem.numCol <= 0: return None nextLocation = problem.getBetterNeighbor(location) if nextLocation == location: # there is no better neighbor, so return this peak return location else: # there is a better neighbor, so move to the neighbor and recurse return algorithm2(problem, nextLocation)
In [17]:
# test algorithm2 peak2 = algorithm2(pb) if pb.isPeak(peak2): print(str(peak2) + " => is a peak")
(4, 4) => is a peak
In [18]:
def algorithm4(problem, bestSeen = None, rowSplit = True): # if it's empty, we're done if problem.numRow <= 0 or problem.numCol <= 0: return None subproblems = [] divider = [] if rowSplit: # the recursive subproblem will involve half the number of rows mid = problem.numRow // 2 # information about the two subproblems (subStartR1, subNumR1) = (0, mid) (subStartR2, subNumR2) = (mid + 1, problem.numRow - (mid + 1)) (subStartC, subNumC) = (0, problem.numCol) subproblems.append((subStartR1, subStartC, subNumR1, subNumC)) subproblems.append((subStartR2, subStartC, subNumR2, subNumC)) # get a list of all locations in the dividing column divider = crossProduct([mid], range(problem.numCol)) else: # the recursive subproblem will involve half the number of columns mid = problem.numCol // 2 # information about the two subproblems (subStartR, subNumR) = (0, problem.numRow) (subStartC1, subNumC1) = (0, mid) (subStartC2, subNumC2) = (mid + 1, problem.numCol - (mid + 1)) subproblems.append((subStartR, subStartC1, subNumR, subNumC1)) subproblems.append((subStartR, subStartC2, subNumR, subNumC2)) # get a list of all locations in the dividing column divider = crossProduct(range(problem.numRow), [mid]) # find the maximum in the dividing row or column bestLoc = problem.getMaximum(divider) neighbor = problem.getBetterNeighbor(bestLoc) # update the best we've seen so far based on this new maximum if bestSeen is None or problem.get(neighbor) > problem.get(bestSeen): bestSeen = neighbor # return when we know we've found a peak if neighbor == bestLoc and problem.get(bestLoc) >= problem.get(bestSeen): return bestLoc # figure out which subproblem contains the largest number we've seen so far, # and recurse, alternating between splitting on rows and splitting on columns. sub = problem.getSubproblemContaining(subproblems, bestSeen) newBest = sub.getLocationInSelf(problem, bestSeen) result = algorithm4(sub, newBest, not rowSplit) return problem.getLocationInSelf(sub, result)
In [19]:
# test algorithm4 peak4 = algorithm4(pb) if pb.isPeak(peak4): print(str(peak4) + " => is a peak")
(4, 4) => is a peak
相关文章推荐
- 使用C++实现功能下载文件
- fish shell安装和配置
- .NET基础--do-while
- SQlite命令行操作数据库
- 二叉树面试题
- 学习之路之Fragment和Fragment之间的传值
- struts中的findForward()和getinputforward()
- nyoj 58 最少步数 【BFS】
- Yii 上传文件(头像)详解(三)
- 单例模式(二)
- 查看Linux服务器内存使用情况
- vsftp命令使用:文件上传及下载
- 一行命令搞定/usr/bin/perl^M: bad interpreter
- linux web篇---之一apache和php
- Tomcat 7优化前及优化后的性能对比
- 一次 read by other session 的处理过程--数据走索引需要看索引字段的数据分布情况
- CentOS 5.9 下nginx proxy_store的使用
- Future,Callable学习笔记
- mysql 语句包含关键字
- [转载] Redis之七种武器