2

I'm trying to solve the classic n queen problem in python. Unfortunately the output was quite different with my expectation. I understood the back tracking algorithm but may be not clear on where I might make a mistake in writing the recursion. I tried 1.print one possible solution. 2. print all possible solutions. (here my solution was noted as a list of n, where value of list[i] is the selected column on the ith row)

def n_queen_find1(n):
    answer = [-1] * n
    def dfs(depth,answer):   
        if depth == n:
            print ("cur valid answer is ",answer)
            return answer
        else:
            for colNum in range(n): # check every col at cur depth
                if is_safe(depth,colNum,answer):
                    answer[depth] = colNum
                    print ("now the answer is ", answer)
                    print ("now depth move to ", depth + 1)
                    dfs(depth + 1,answer)

    def is_safe(i,j,answer_cur):
        # given current queen placement , could we place the ith queen (at row i) at the col j
        for prerow in range(i):
            if answer_cur[prerow] == j or abs(prerow - i) == abs(answer_cur[prerow] - j):
                return False
        return True

    return dfs(0,answer)

I'm expecting the n_queen_find1 to output a list of 4 element when n == 4, such as [1, 3, 0, 2]. In my recursion processes, the corrected answer was generated (as seen in the print). However, the function returned nothing. I added those print lines helping to debug, and found that I didn't understand why dfs not stopping and return when the first correct answer was created?

trial1 = n_queen_find1(4)
print trial1
('now the answer is ', [0, -1, -1, -1])
('now depth move to ', 1)
('now the answer is ', [0, 2, -1, -1])
('now depth move to ', 2)
('now the answer is ', [0, 3, -1, -1])
('now depth move to ', 2)
('now the answer is ', [0, 3, 1, -1])
('now depth move to ', 3)
('now the answer is ', [1, 3, 1, -1])
('now depth move to ', 1)
('now the answer is ', [1, 3, 1, -1])
('now depth move to ', 2)
('now the answer is ', [1, 3, 0, -1])
('now depth move to ', 3)
('now the answer is ', [1, 3, 0, 2])
('now depth move to ', 4)
('cur valid answer is ', [1, 3, 0, 2])
('now the answer is ', [2, 3, 0, 2])
('now depth move to ', 1)
('now the answer is ', [2, 0, 0, 2])
('now depth move to ', 2)
('now the answer is ', [2, 0, 3, 2])
('now depth move to ', 3)
('now the answer is ', [2, 0, 3, 1])
('now depth move to ', 4)
('cur valid answer is ', [2, 0, 3, 1])
('now the answer is ', [3, 0, 3, 1])
('now depth move to ', 1)
('now the answer is ', [3, 0, 3, 1])
('now depth move to ', 2)
('now the answer is ', [3, 0, 2, 1])
('now depth move to ', 3)
('now the answer is ', [3, 1, 2, 1])
('now depth move to ', 2)
None

I slightly modified the n_queen_find1 to generate all possible solutions, but I could not explain the output as well.

def n_queen_findall(n):
    answer = [-1] * n
    finalAnswer = []
    def dfs(depth,answer,finalAnswer):   
        if depth == n:
            print ("cur valid answer is ",answer)            
            finalAnswer.append(answer)
            print ("after appending final answer is ", finalAnswer)
            return
        else:
            for colNum in range(n): # check every col at cur depth
                if is_safe(depth,colNum,answer):
                    answer[depth] = colNum
                    print ("now the answer is ", answer)
                    print ("now depth move to ", depth + 1)
                    dfs(depth + 1,answer,finalAnswer)

    def is_safe(i,j,answer_cur):
        # given current queen placement , could we place the ith queen (at row i) at the col j
        for prerow in range(i):
            if answer_cur[prerow] == j or abs(prerow - i) == abs(answer_cur[prerow] - j):
                return False
        return True
    dfs(0,answer,finalAnswer)
    return finalAnswer

Here is my test for n_queen_findall.It was not correct too. First, why the finalAnswers would append result when depth is not equal to n? Second, why there was a duplication in finalAnswer?

trial2 = n_queen_findall(4)
print trial2
('now the answer is ', [0, -1, -1, -1])
('now depth move to ', 1)
('now the answer is ', [0, 2, -1, -1])
('now depth move to ', 2)
('now the answer is ', [0, 3, -1, -1])
('now depth move to ', 2)
('now the answer is ', [0, 3, 1, -1])
('now depth move to ', 3)
('now the answer is ', [1, 3, 1, -1])
('now depth move to ', 1)
('now the answer is ', [1, 3, 1, -1])
('now depth move to ', 2)
('now the answer is ', [1, 3, 0, -1])
('now depth move to ', 3)
('now the answer is ', [1, 3, 0, 2])
('now depth move to ', 4)
('cur valid answer is ', [1, 3, 0, 2])
('now the final answer is ', [])
('now the answer is ', [2, 3, 0, 2])
('now depth move to ', 1)
('now the answer is ', [2, 0, 0, 2])
('now depth move to ', 2)
('now the answer is ', [2, 0, 3, 2])
('now depth move to ', 3)
('now the answer is ', [2, 0, 3, 1])
('now depth move to ', 4)
('cur valid answer is ', [2, 0, 3, 1])
('now the final answer is ', [[2, 0, 3, 1]])
('now the answer is ', [3, 0, 3, 1])
('now depth move to ', 1)
('now the answer is ', [3, 0, 3, 1])
('now depth move to ', 2)
('now the answer is ', [3, 0, 2, 1])
('now depth move to ', 3)
('now the answer is ', [3, 1, 2, 1])
('now depth move to ', 2)
[[3, 1, 2, 1], [3, 1, 2, 1]]

Sorry for making the question that long.The cause of this problem may be quite simple.I'm junior in python and especially python backend.Maybe my understanding of python passing by reference was not correct? Really appreciate some guidance on this. Thanks very much in advance.

evehsu
  • 39
  • 4
  • Thanks for the comment. The `abs(prerow - i) == abs(answer_cur[prerow] - j)` in if condition was used for checking diagonals. In `is_safe` function, it was checked two things, 1) conflict in columns 2) conflict in diagonals. It didn't check row as they were in different rows by default. – evehsu Nov 19 '17 at 06:32

1 Answers1

0

In your dfs, you have a return only when n==4, but not when n is anything else. So no matter what dfs returns for 4, they will be absorbed by parent callee and nothing will get returned in the end.

So to solve your first problem, since we know failed placements in answer returns None, (because if in for loop inside else will fail), we add an if before recursive dfs to check success. If so, we return answer. So just change this:

dfs(depth + 1,answer)

To this :

if dfs(depth + 1,answer): #false i.e None for failed dfs
    return answer

Before we tackle 2nd problem, let's think, if we have 5 solutions, we need 5 arrays to store them,right? How many do you have? one answer. In finalAnswer.append(answer) you are not appending newer solution containing arrays, you are simply appending reference of our old answer, which is constantly getting modified and whatever you intended to append is getting lost.

So you need to copy and create new array before appending. Beside, you need to get rid of the above return too.

def n_queen_find1(n):
    answer = [-1] * n
    finalAnswer = []
    def dfs(depth,answer):
        if depth == n:
            print ("cur valid answer is ",answer)
            finalAnswer.append([i for i in answer]) #answer is copied
            return answer
        else:
            for colNum in range(n): # check every col at cur depth
                if is_safe(depth,colNum,answer):
                    answer[depth] = colNum
                    #print ("now the answer is ", answer)
                    #print ("now depth move to ", depth + 1)
                    dfs(depth + 1,answer)


    def is_safe(i,j,answer_cur):
        # given current queen placement , could we place the ith queen (at row i) at the col j
        for prerow in range(i):
            if answer_cur[prerow] == j or abs(prerow - i) == abs(answer_cur[prerow] - j):
                return False
        return True

    dfs(0,answer)
    return finalAnswer

which prints:

cur valid answer is  [1, 3, 0, 2]
cur valid answer is  [2, 0, 3, 1]
[[1, 3, 0, 2], [2, 0, 3, 1]]
Shihab Shahriar Khan
  • 4,930
  • 1
  • 18
  • 26