Peak Finding

来源:互联网 发布:手机fps帧数显示软件 编辑:程序博客网 时间:2024/04/30 04:14

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[n] = .

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) / 2L[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 Lpeakindex = 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 greater
than 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 getDimensionsgetDimensions(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.startRowpb.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    1   2    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 // 2divider = crossProduct(range(pb.numRow), [mid])print dividerbestLoc = 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 algorithm2peak2 = 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 algorithm4peak4 = algorithm4(pb)if pb.isPeak(peak4):    print(str(peak4) + " => is a peak")
(4, 4) => is a peak
0 0
原创粉丝点击