algorithmpython-3.xn-queens

Avoid duplicates in N Queen iterative solutions (No Recursion Allowed)


I am solving the N queen problem with iteration (no recursion). Problem I am facing right now is duplicate solutions. For example 4 x 4 board has 2 solutions I am printing 4 solutions, so to say I am finding same solution twice.

Let me get into the code for a better overview:

def solution(self):
        queen_on_board = 0
        for row in range(self.N):
            for col in range(self.N):
                self.board[row][col] = 'Q'
                queen_on_board = queen_on_board + 1
                print ("(row,col) : ", row, col)
                squares_list = self.get_posible_safe_squares(row,col)
                for square in squares_list:
                    for x,y in square.items():
                        if self.isTheQueenSafe(x,y):
                            self.board[x][y] = 'Q'
                            queen_on_board = queen_on_board + 1
                print ("Queen on board", queen_on_board)
                if queen_on_board == 4:
                    self.print_the_board()
                self.reset_the_board()
                queen_on_board = 0

so as you can see I am going over every rows and cols. This particular implementation gives me 4 solution 2 being identical.

(row,col) :  0 1
Queen on board 4
['.', 'Q', '.', '.'] 

['.', '.', '.', 'Q'] 

['Q', '.', '.', '.'] 

['.', '.', 'Q', '.'] 

(row,col) :  0 2
Queen on board 4
['.', '.', 'Q', '.'] 

['Q', '.', '.', '.'] 

['.', '.', '.', 'Q'] 

['.', 'Q', '.', '.']

(row,col) :  1 0
Queen on board 4
['.', '.', 'Q', '.'] 

['Q', '.', '.', '.'] 

['.', '.', '.', 'Q'] 

['.', 'Q', '.', '.'] 

(row,col) :  2 0
Queen on board 4
['.', 'Q', '.', '.'] 

['.', '.', '.', 'Q'] 

['Q', '.', '.', '.'] 

['.', '.', 'Q', '.']

I want to avoid getting duplicates. Would be great if someone can point me to the right direction.

The get_posible_safe_squares() method looks for possible squares in the board where the queen might be safe.

def get_posible_safe_squares(self, row, col):
        ret = []
        for i in range(self.N):
            for j in range(self.N):
                if i != row and j !=col:
                    if i + j != row + col and i - j != row - col:
                        d = { i:j }
                        ret.append(d)
        return ret 

Solution

  • The reason you get duplicates is that you also place queens "before" the position where you place the first queen. So your first queen will get its position on each square, but other queens can take their position on a square where in an earlier iteration the first queen had already been put. This means two queens were "swapped", but essentially build towards the same solution.

    I tried to rewrite your solution, but then decided to also change the following aspects:

    Here is the code:

    class Board:
        def __init__(self, size):
            self.N = size
            self.queens = [] # list of columns, where the index represents the row
    
        def is_queen_safe(self, row, col):
            for r, c in enumerate(self.queens):
                if r == row or c == col or abs(row - r) == abs(col - c):
                    return False
            return True
    
        def print_the_board(self):
            print ("solution:")
            for row in range(self.N):
                line = ['.'] * self.N
                if row < len(self.queens):
                    line[self.queens[row]] = 'Q'
                print(''.join(line))
    
        def solution(self):
            self.queens = []
            col = row = 0
            while True:
                while col < self.N and not self.is_queen_safe(row, col):
                    col += 1
                if col < self.N:
                    self.queens.append(col)
                    if row + 1 >= self.N:
                        self.print_the_board()
                        self.queens.pop()
                        col = self.N
                    else:
                        row += 1
                        col = 0
                if col >= self.N:
                    # not possible to place a queen in this row anymore
                    if row == 0:
                        return # all combinations were tried
                    col = self.queens.pop() + 1
                    row -= 1
    
    q = Board(5)
    q.solution()