Search code examples
pythonbacktrackingn-queens

What happens to list when it is modified in recursive function arguments


This is a solution for the n-queen question. In recursion calls, it passes modified lists. My question is for each recursion call, does it create three new lists: queens, xy_dif, xy_sum and keep them in the call stack? Or there are only those three lists, each recursive call just modified them in place? If that is the case, I don't understand how it solves the problem like below:

DFS([],[],[]) calls DFS([0], [0], [0]), then DFS([0], [0], [0]) calls DFS([0,2],[0,-1],[0,3]), it didn't pass the if condition so it returns.

Then the for loop continue, q=3, my question is why is DFS([0,3],[0,-2],[0,4]) the next call instead of DFS([0,2,3],[0,-1,-2],[0,3,4])?

How was those three lists reverted back to the state of original states after call DFS([0], [0], [0])?

def solveNQueens(self, n):
    def DFS(queens, xy_dif, xy_sum):
        print('call function DFS({},{},{})'.format(queens, xy_dif, xy_sum))
        p = len(queens)
        print('updated row p is ', p)
        if p==n:
            result.append(queens)
            print('p==n, result is ', result)
            return None
        for q in range(n):
            print('loop starts, q is ', q)
            if q not in queens and p-q not in xy_dif and p+q not in xy_sum: 
                DFS(queens+[q], xy_dif+[p-q], xy_sum+[p+q])  
    result = []
    DFS([],[],[])
    return [ ["."*i + "Q" + "."*(n-i-1) for i in sol] for sol in result]

Solution

  • Add some "standard" recursion tracking, and you can see the results easily. The id function is your friend in this case, showing whether a particular parameter refers to the same entity.

    As you can see in the output below, each parameter has a unique id: it's an independent object. This is because you have not passed the parameters of one call to the next. Rather, you've created a temporary variable on the stack. If you simply pass through queens, for example, then that list reference is shared by all calls.

    On the other hand, queens+[q] is not queens; rather, it is a new list expression: a temporary variable on the stack, with a separate value.

    If you wished to share the structures, you would first alter queens, and then pass that altered version to the next level:

    queens.append(q)
    DFS(queens, xy_dif+[p-q], xy_sum+[p+q])
    

    In this usage, xy_diff and xy_sum are still independent variables on each call, b ut queens is shared.

    Your code with recursion instrumentation:

    indent = ""
    
    def DFS(queens, xy_dif, xy_sum):
        global indent
        print(indent, "ENTER DFS", queens, xy_dif, xy_sum)
        print(indent, "parameter IDs:", id(queens), id(xy_dif), id(xy_sum))
        indent += "  "
    
        print('call function DFS({},{},{})'.format(queens, xy_dif, xy_sum))
        p = len(queens)
        print('updated row p is ', p)
    
        if p==n:
            result.append(queens)
            print('p==n, result is ', result)
            indent = indent[2:]
            return None
    
        for q in range(n):
            print('loop starts, q is ', q)
            if q not in queens and p-q not in xy_dif and p+q not in xy_sum: 
                DFS(queens+[q], xy_dif+[p-q], xy_sum+[p+q])  
    
        result = []
        DFS([],[],[])
        send_back = [ ["."*i + "Q" + "."*(n-i-1) for i in sol] for sol in result]
    
        print(indent, "LEAVE DFS", queens, xy_dif, xy_sum)
        indent = indent[2:]
        return send_back
    

    Start of output:

     ENTER DFS [] [] []
     parameter IDs: 1813255818376 1813255818888 1813255819016
    call function DFS([],[],[])
    updated row p is  0
    loop starts, q is  0
       ENTER DFS [0] [0] [0]
       parameter IDs: 1813255827784 1813258276680 1813255818760
    call function DFS([0],[0],[0])
    updated row p is  1
    loop starts, q is  0
    loop starts, q is  1
    loop starts, q is  2
         ENTER DFS [0, 2] [0, -1] [0, 3]
         parameter IDs: 1813255817480 1813255817352 1813255817928
    call function DFS([0, 2],[0, -1],[0, 3])
    updated row p is  2
    loop starts, q is  0
    loop starts, q is  1
    loop starts, q is  2
    loop starts, q is  3
           ENTER DFS [] [] []
           parameter IDs: 1813255817864 1813255817736 1813255816712
    call function DFS([],[],[])
    updated row p is  0
    loop starts, q is  0
             ENTER DFS [0] [0] [0]
             parameter IDs: 1813255816392 1813255816264 1813255816136
    call function DFS([0],[0],[0])
    ...