Peak finding algorithm This is my peak finding algorithm. I try to split the 2D array in half and find the maximum element in the middle column. After that, the algorithm will check whether there are any other element bigger than it on the left or the right side. If there is any, the matrix will slice in half depending on the position of the element and go into recursion. You can simply change the size or the elements of the matrix.

How can I improve my coding to make it more efficient or concise?

import numpy as np
matrix=np.array([[3,2,3],
    [100,55,7],
    [344,9,37]
   ])


def algo(problem):
    print('matrix =' + str(problem)) 
    Nrow=len(problem)
    print("row"+str(Nrow))
    for i in problem:
        Ncol=len(i)
        break
    if Nrow &lt;=0 or Ncol&lt;=0:
        return None
    print("col"+str(Ncol))
    mid=Ncol//2
    (substartR,subnrow)=(0,Nrow)
    (substartc1,subncol1)=(0,mid)
    (substartc2,subncol2)=(mid,Ncol-mid+1)
    subproblem=[]
    subproblem.append((substartR,substartc1,subnrow,subncol1))
    subproblem.append((substartR,substartc2,subnrow,subncol2))
    print(subproblem)

    loc=[]
    for i in range(Nrow):
        loc.append((i,mid))
    # bestloc is a tuple of location and biggest val
    bestloc=findmax(loc,problem)
    print("bestloc" + str(bestloc[0]))

    bestloc1=bestneighbour(bestloc,Nrow,Ncol,problem)
    print("bestloc1 =" + str(bestloc1))

    if bestloc[0] != bestloc1:
        print('sub problem')
        #print(problem)
        new_matrix=get_subproblem(subproblem,bestloc1,problem)
        return algo(new_matrix)
    else:
        (x,y)=bestloc1
        #print("the peak " +str( problem[x][y]))
        print( problem[x][y])



def findmax(loc,matrix):
    (bestloc,bestval)=(None,0)

    for i in loc:

      if bestloc == None or bestval &lt; get_value(i,matrix) :
        #print(get_value(i))

          bestloc=i
          bestval= get_value(i,matrix)


    return (bestloc,bestval)

def get_value(i,matrix):

    y=i[1]
    x=i[0]

    return matrix[x][y]

def bestneighbour(i,Nrow,Ncol,matrix):

    y=i[0][1]
    x=i[0][0]
    bestval=i[1]

    inew=(x,y)
    if x-1&gt;=0 and matrix[x-1][y]&gt;bestval:
        inew=(x-1,y)
        bestval=matrix[x-1][y]
    if x+1&lt;Nrow and matrix[x+1][y]&gt;bestval:
        inew=(x+1,y)
        bestval=matrix[x+1][y]

    if y-1&gt;=0 and matrix[x][y-1]&gt;bestval:
        inew=(x,y-1)
        bestval=matrix[x][y-1]

    if y+1&lt;Ncol and matrix[x][y+1]&gt;bestval:
        inew=(x,y+1)
        bestval=matrix[x][y+1]

    return inew
def get_subproblem(sub,bestloc,matrix1):
    #global matrix
    (row,col)=bestloc
    #print(row)
    #print(col)
    for i in sub:
        print(i)
        (start_R,start_C,num_of_row,num_of_col)=i
        if start_R&lt;=row and row&lt;start_R+num_of_row:
            if start_C&lt;=col and col&lt;start_C+num_of_col:
                matrix1=matrix1[start_R:num_of_row,start_C:num_of_col+start_C]
                return matrix1






algo(matrix)
1. algo

I can see 3 areas of local improvements here:

for i in problem:
    Ncol=len(i)
    break


doing a loop to just set Ncol for the first element is really ugly. Since your list isn't empty just do:

Ncol = len(problem[0])


Now why 3 lines for this:

subproblem=[]
subproblem.append((substartR,substartc1,subnrow,subncol1))
subproblem.append((substartR,substartc2,subnrow,subncol2))


when you can create your list directly:

subproblem= [(substartR,substartc1,subnrow,subncol1),(substartR,substartc2,subnrow,subncol2)]


and last thing, learn about list comprehensions for things like this:

loc=[]
for i in range(Nrow):
    loc.append((i,mid))


that can be rewritten as

loc = [(i,mid) for i in range(Nrow)]


2. findmax / getvalue

As mentionned in comments, the indentation of return statement is off, but since the code works, I'm assuming that it's just a typo. Here:

for i in loc:
  if bestloc == None or bestval &lt; get_value(i,matrix) :
      bestloc=i
      bestval= get_value(i,matrix)

return (bestloc,bestval)



get_value is a function created just for this method. It introduces a lot of overhead &amp; doesn't improve readability either. Scrap it, "inline" &amp; cache the value
set bestloc to the first value (and value is okay, but first one is the most logical choice) before the loop starts, so you don't have to test for None in the whole loop. For this we have to handle the "loc is empty" cornercase at start.


So rewrite function like:

def findmax(loc,matrix):
    if not loc:
        # get rid of the "loc is empty" case
        return (None,0)
    bestloc,bestval = loc[0]

    for i in loc:
       x,y = i  # unpack from i, no indice access
       mvalue = matrix[x][y]

       if bestval &lt; mvalue:
          bestloc = i
          bestval = mvalue

    return (bestloc,bestval)


only one access to the matrix, no unnecessary test for None, no function calls, faster index access.

get_subproblem


Use chaining comparisons since row and col are tested twice (now you can group both conditions)
don't slice then return, return the slice


like this:

for i in sub:
    print(i)
    (start_R,start_C,num_of_row,num_of_col)=i
    if start_R &lt;= row &lt;start_R+num_of_row and start_C &lt;= col &lt;start_C+num_of_col:
        return matrix1[start_R:num_of_row,start_C:num_of_col+start_C]


if you don't care about the print you could unpack directly in loop:

for start_R,start_C,num_of_row,num_of_col in sub: