0

How could I convert and optimize this recursive function into an iterative one. I'm trying to program a function that accumulates the flow using a map of directions, however, for direction maps of very large size the function just crashes. I am programming in Python, and increasing the system recursion limit is not an option.

def AcumulacionCelda(x,y):
            if Acum[x,y]==NoData:
                Acum[x,y]=1
                for m, n in product(range(-1,2), range(-1,2)):
                    if m==-1 and n==-1 and Direcciones[x+m,y+n]==4:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==-1 and n==0 and Direcciones[x+m,y+n]==5:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==-1 and n==1 and Direcciones[x+m,y+n]==6:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==0 and n==1 and Direcciones[x+m,y+n]==7:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==1 and n==1 and Direcciones[x+m,y+n]==8:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==1 and n==0 and Direcciones[x+m,y+n]==1:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==1 and n==-1 and Direcciones[x+m,y+n]==2:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]
                    elif m==0 and n==-1 and Direcciones[x+m,y+n]==3:
                        AcumulacionCelda(x+m,y+n)
                        Acum[x,y]=Acum[x,y]+Acum[x+m,y+n]

            return;

for i, j in product(range(1,Filas-1), range(1,Columnas-1)):
      AcumulacionCelda(i,j)
Rhernan4
  • 21
  • 4

2 Answers2

2

First let's refactor the series of if-statements to make the recursive function much simpler:

def AcumulacionCelda(x,y):
    d = {(-1, -1):  4,
         (-1, 0) :  5,
         (-1, 1) :  6,
         (0, -1) :  3,
         (0, 0)  :  'dummy',
         (0, 1)  :  7,
         (1, -1) :  2,
         (1, 0)  :  1,
         (1, 1)  :  8}

    if Acum[x, y] == NoData:
        Acum[x, y] = 1
        for m, n in product(range(-1,2), range(-1,2)):
            if Direcciones[x+m, y+n] == d[m, n]:
                AcumulacionCelda(x+m, y+n)
                Acum[x,y] += Acum[x+m, y+n]

Now to make the function iterative rather than recursive, we need to create a queue to store the various states and tasks that we need to partially suspend and later revisit. Instead of recursive calls, we append to the queue what tasks remain to be done, then a new task for the 'recursive' calculation, then break (or continue) out to a new iteration of the main loop.

def AcumulacionCelda(x,y):
    if Acum[x, y] != NoData:
        return
    Acum[x, y] = 1
    d = {(-1, -1):  4,
         (-1, 0) :  5,
         (-1, 1) :  6,
         (0, -1) :  3,
         (0, 0)  : 'dummy',
         (0, 1)  :  7,
         (1, -1) :  2,
         (1, 0)  :  1,
         (1, 1)  :  8}
    keys = tuple(product(range(-1,2), range(-1,2)))[::-1]
    queue = [('Loop', (x, y), list(keys))]

    while queue:
        instruction, coords, directions = queue.pop()
        x, y = coords
        if instruction == 'Loop':
            while directions:
                m, n = directions.pop()
                if Direcciones[x+m, y+n] == d[m, n]:
                    queue.append(('Loop', (x, y), directions))
                    queue.append(('Add', (x, y), (m, n)))
                    if Acum[x+m, y+n] == NoData:
                        Acum[x+m, y+n] = 1
                        queue.append(('Loop', (x+m, y+n), list(keys)))
                    break
        elif instruction == 'Add':
            m, n = directions
            Acum[x, y] += Acum[x+m, y+n]
zehnpaard
  • 6,003
  • 2
  • 25
  • 40
  • Thanks for your answer. The idea of reorganizing the if-statements seems very good, however, the function that you present me is not achieving the result of the recursive function. What makes the recursive function is to look at the cell i, j if any of its 8 neighboring cells drain over it. If any of the 8 neighboring cells drain, then the recursive function is call in the cell that drain to it and repeat the process until it find a cell with no neighbor flow accumulation. – Rhernan4 Oct 02 '15 at 21:04
  • @Rhernan4 Do you mean you are actually getting different results running the first recursive function and running your original function? If so can you try replacing 'for m, n in d:' with 'for m, n in product(range(-1,2), range(-1,2)):'? Apart from the order in which the different values of `m, n` are evaluated, the first function should be doing exactly the same thing as your original recursive function. – zehnpaard Oct 05 '15 at 03:55
  • Yes zehnpaard. I am getting a different result by running the recursive function. I agree with you, the order of m,n doesn't matter. I'm going to try again replacing the for loop. I don't understand where is the problem in your iterative function... – Rhernan4 Oct 05 '15 at 13:52
  • I get the same result in your recursive function by adding an indent in the for loop, so the for loop is excuted only if there is a NoData value in the cell x,y. I don't know what happens with the iterative function, but it is not giving the same result. The result shows that the order of m and n doesn't matter. Again, thank you very much for your help! – Rhernan4 Oct 06 '15 at 01:43
  • No, it is not working... There is a problem with the iterative function. It is not giving the same resluts – Rhernan4 Oct 06 '15 at 02:05
  • It is working much better, but i am still getting small differences in the results. The reslult is close but is not exactly the same.. your code is very close! – Rhernan4 Oct 06 '15 at 02:42
  • Edited again, making the order in which we evaluate surrounding cells the same as the recursive example. – zehnpaard Oct 06 '15 at 12:20
  • You did it. Your code is working fine. I will test it with other direction maps, but I think it is working perfectly. Thank you very much, you did a very good job. – Rhernan4 Oct 06 '15 at 14:36
0

This is a low level solution for this type of problem. I think there is a better tool for the job. You will probably want to use Dijkstra's shortest path algorithm. You may want to take a look at the NetworkX module algorithms section. Map x,y locations as points in a network graph make the weights on the edges as the squared difference of the x,y city positions.

Back2Basics
  • 7,406
  • 2
  • 32
  • 45