Peak Finding

16 篇文章 0 订阅

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 ) + c logn

= 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 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

Θ(n log m )  =  Θ(n log 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    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 // 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 algorithm1
peak = 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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Finding bugs(寻找错误)是指在软件开发过程中,为了保证软件的质量和稳定性,通过一系列的测试和调试过程,找出软件中存在的错误和缺陷,并进行修复的活动。 寻找错误是软件开发过程中必不可少的一步。在软件开发过程中,无论是编写代码、设计界面还是实施功能,都可能出现各种各样的错误。这些错误可能导致软件无法正常运行、功能异常或者性能低下。为了及时发现和修复这些错误,需要进行系统而全面的错误寻找工作。 寻找错误的方法和技巧有很多种。其中一种常用的方法是黑盒测试。黑盒测试是指在不了解软件内部结构和具体实现的情况下,通过输入一些指定的测试用例,观察软件的输出结果,并与预期结果进行对比,从而判断软件是否存在错误。另外一种方法是白盒测试。白盒测试是指在了解软件内部结构和具体实现的情况下,通过对代码进行逐行逐句的检查,发现其中潜在的错误。 除了以上的方法,还可以使用自动化的测试工具来辅助寻找错误。这些工具能够模拟用户的操作,快速地执行大量的测试用例,并生成详细的测试报告,帮助开发人员准确定位和修复错误。 在寻找错误的过程中,要保持耐心和专注。有时候错误可能隐藏得很深,需要仔细地分析和调试。同时,还要注重记录和总结错误,以便后续的修复工作。 总之,寻找错误是软件开发过程中不可或缺的一环。通过系统而全面的测试和调试工作,可以及时发现和修复软件中存在的错误和缺陷,提高软件的质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值