您的位置:首页 > 其它

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 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 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: