How to improve (speed, memory usage) search for unique number of paths to reach opposite corner algorithm?

前端 未结 1 1429
予麋鹿
予麋鹿 2021-01-23 16:14

I have m x n grid. m >= 1 ; n >= 1

I have item in the top-left corner and need to reach bottom-right corner of the grid.

Item can only move either down or right.

相关标签:
1条回答
  • 2021-01-23 16:52

    The problem with your approach is you're taking the same steps repeatedly. It's the first brute-force approach that someone should try.

    For starter, you can try to increase the recursion limit for python.

    import sys
    sys.setrecursionlimit(1500)
    

    But it will fail if you start increasing m, or n. As the complexity grows exponentially.

    One way to improve is to break the problem down into smaller parts and solve for the smaller parts and merge them into the final solution.

    Think, you are in the green position and want to go to the blue one. That's the main solution. But, let's imagine the smaller sub-grid with red boundary, the red grid has a starting point at the orange marker and endpoint at blue, now let's say in some magical way we know the solution for the red sub-grid, can't we just merge the solution for going from green to orange + red grid part?

    Now, this recursive idea can be implemented in the following way.

    def numberOfPaths(m, n): 
        if(m == 1 or n == 1): 
            return 1
    
        return numberOfPaths(m-1, n) + numberOfPaths(m, n-1)  # traversal in the two possible directions
    
    m = 20
    n = 20
    print(numberOfPaths(m, n)) 
    

    But the complexity is still exponential, as the program tries all possible combinations for finding a solution over and over again. What if we use a map to save all the partial solutions? We can save the solution for red sub-grid and just use it from our map without re-traversing it again?

    This concept is called dynamic programming and it's very well known. So, I won't go to any details.

    We can create a 2-d array answers[m][n] it will be initialized with -1; if we know the solution from a sub-grid m_1, n_1 we just return the answer instead of traversing.

    This brings down the complexity to O(mxn).

    import numpy as np
    
    global answers
    
    def numberOfPaths(m, n): 
        if(m == 1 or n == 1): 
            return 1
        global answers
        if answers[m][n] != -1:
            return answers[m][n]
    
    
        answers[m][n] = numberOfPaths(m-1, n) + numberOfPaths(m, n-1)  # traversal
    
        return answers[m][n]
    
    m = 6
    n = 6
    
    answers = np.ones((m+1,n+1))*-1
    
    print(numberOfPaths(m, n)) 
    

    It's already a major improvement.

    We can completely re-invent the problem as a combinatorial one too.

    Look, there are m rows, n columns, if you start from top-left, you can take any set of moves (right or down), but your initial cell and final cell are fixed. So, how many possible options do you have to make moves? (m+n-2) (initial and final cell fixed so -2) Now, from all these possible moves you can only select n-1 if we consider the columns, or m-1 if we consider the rows. So, the solution will be (m+n-2)C(n-1) or (m+n-2)C(m-1).

    Now, for smaller integers where m! or n! don't overflow (luckily python integers can handle large values easily), this can be done in linear time O(max(m,n)). As nCr can be calculated in terms of factorials only.

    0 讨论(0)
提交回复
热议问题