The N Queen is the problem of placing N chess queens on an N×N chessboard so that no two queens attack each other.
For example, the following is a solution for 8 Queen problem.
Input: N = 4
Output:
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
Explanation:
The Position of queens are:
1 – {1, 2}
2 – {2, 4}
3 – {3, 1}
4 – {4, 3}
As we can see that we have placed all 4 queens
in a way that no two queens are attacking each other.
So, the output is correct
Input: N = 8
Output:
0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0
1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
Approach: The idea is to use Hill Climbing Algorithm.
- While there are algorithms like Backtracking to solve N Queen problem, let’s take an AI approach in solving the problem.
- It’s obvious that AI does not guarantee a globally correct solution all the time but it has quite a good success rate of about 97% which is not bad.
-
A description of the notions of all terminologies used in the problem will be given and are as follows:-
- Notion of a State – A state here in this context is any configuration of the N queens on the N X N board. Also, in order to reduce the search space let’s add an additional constraint that there can only be a single queen in a particular column. A state in the program is implemented using an array of length N, such that if state[i]=j then there is a queen at column index i and row index j.
- Notion of Neighbours – Neighbours of a state are other states with board configuration that differ from the current state’s board configuration with respect to the position of only a single queen. This queen that differs a state from its neighbour may be displaced anywhere in the same column.
-
Optimisation function or Objective function – We know that local search is an optimization algorithm that searches the local space to optimize a function that takes the state as input and gives some value as an output. The value of the objective function of a state here in this context is the number of pairs of queens attacking each other. Our goal here is to find a state with the minimum objective value. This function has a maximum value of NC2 and a minimum value of 0.
Algorithm:
- Start with a random state(i.e, a random configuration of the board).
- Scan through all possible neighbours of the current state and jump to the neighbour with the highest objective value, if found any. If there does not exist, a neighbour, with objective strictly higher than the current state but there exists one with equal then jump to any random neighbour(escaping shoulder and/or local optimum).
- Repeat step 2, until a state whose objective is strictly higher than all it’s neighbour’s objectives, is found and then go to step 4.
- The state thus found after the local search is either the local optimum or the global optimum. There is no way of escaping local optima but adding a random neighbour or a random restart each time a local optimum is encountered increases the chances of achieving global optimum(the solution to our problem).
- Output the state and return.
- It is easily visible that the global optimum in our case is 0 since it is the minimum number of pairs of queens that can attack each other. Also, the random restart has a higher chance of achieving global optimum but we still use random neighbour because our problem of N queens does not has a high number of local optima and random neighbour is faster than random restart.
-
Conclusion:
- Random Neighbour escapes shoulders but only has a little chance of escaping local optima.
-
Random Restart both escapes shoulders and has a high chance of escaping local optima.
Below is the implementation of the Hill-Climbing algorithm:
// C++ implementation of the // above approach #include <iostream> #include <math.h> #define N 8 using namespace std;
// A utility function that configures // the 2D array "board" and // array "state" randomly to provide // a starting point for the algorithm. void configureRandomly( int board[][N],
int * state)
{ // Seed for the random function
srand ( time (0));
// Iterating through the
// column indices
for ( int i = 0; i < N; i++) {
// Getting a random row index
state[i] = rand () % N;
// Placing a queen on the
// obtained place in
// chessboard.
board[state[i]][i] = 1;
}
} // A utility function that prints // the 2D array "board". void printBoard( int board[][N])
{ for ( int i = 0; i < N; i++) {
cout << " " ;
for ( int j = 0; j < N; j++) {
cout << board[i][j] << " " ;
}
cout << "\n" ;
}
} // A utility function that prints // the array "state". void printState( int * state)
{ for ( int i = 0; i < N; i++) {
cout << " " << state[i] << " " ;
}
cout << endl;
} // A utility function that compares // two arrays, state1 and state2 and // returns true if equal // and false otherwise. bool compareStates( int * state1,
int * state2)
{ for ( int i = 0; i < N; i++) {
if (state1[i] != state2[i]) {
return false ;
}
}
return true ;
} // A utility function that fills // the 2D array "board" with // values "value" void fill( int board[][N], int value)
{ for ( int i = 0; i < N; i++) {
for ( int j = 0; j < N; j++) {
board[i][j] = value;
}
}
} // This function calculates the // objective value of the // state(queens attacking each other) // using the board by the // following logic. int calculateObjective( int board[][N],
int * state)
{ // For each queen in a column, we check
// for other queens falling in the line
// of our current queen and if found,
// any, then we increment the variable
// attacking count.
// Number of queens attacking each other,
// initially zero.
int attacking = 0;
// Variables to index a particular
// row and column on board.
int row, col;
for ( int i = 0; i < N; i++) {
// At each column 'i', the queen is
// placed at row 'state[i]', by the
// definition of our state.
// To the left of same row
// (row remains constant
// and col decreases)
row = state[i], col = i - 1;
while (col >= 0
&& board[row][col] != 1) {
col--;
}
if (col >= 0
&& board[row][col] == 1) {
attacking++;
}
// To the right of same row
// (row remains constant
// and col increases)
row = state[i], col = i + 1;
while (col < N
&& board[row][col] != 1) {
col++;
}
if (col < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the left up
// (row and col simultaneously
// decrease)
row = state[i] - 1, col = i - 1;
while (col >= 0 && row >= 0
&& board[row][col] != 1) {
col--;
row--;
}
if (col >= 0 && row >= 0
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the right down
// (row and col simultaneously
// increase)
row = state[i] + 1, col = i + 1;
while (col < N && row < N
&& board[row][col] != 1) {
col++;
row++;
}
if (col < N && row < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the left down
// (col decreases and row
// increases)
row = state[i] + 1, col = i - 1;
while (col >= 0 && row < N
&& board[row][col] != 1) {
col--;
row++;
}
if (col >= 0 && row < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the right up
// (col increases and row
// decreases)
row = state[i] - 1, col = i + 1;
while (col < N && row >= 0
&& board[row][col] != 1) {
col++;
row--;
}
if (col < N && row >= 0
&& board[row][col] == 1) {
attacking++;
}
}
// Return pairs.
return ( int )(attacking / 2);
} // A utility function that // generates a board configuration // given the state. void generateBoard( int board[][N],
int * state)
{ fill(board, 0);
for ( int i = 0; i < N; i++) {
board[state[i]][i] = 1;
}
} // A utility function that copies // contents of state2 to state1. void copyState( int * state1, int * state2)
{ for ( int i = 0; i < N; i++) {
state1[i] = state2[i];
}
} // This function gets the neighbour // of the current state having // the least objective value // amongst all neighbours as // well as the current state. void getNeighbour( int board[][N],
int * state)
{ // Declaring and initializing the
// optimal board and state with
// the current board and the state
// as the starting point.
int opBoard[N][N];
int opState[N];
copyState(opState,
state);
generateBoard(opBoard,
opState);
// Initializing the optimal
// objective value
int opObjective
= calculateObjective(opBoard,
opState);
// Declaring and initializing
// the temporary board and
// state for the purpose
// of computation.
int NeighbourBoard[N][N];
int NeighbourState[N];
copyState(NeighbourState,
state);
generateBoard(NeighbourBoard,
NeighbourState);
// Iterating through all
// possible neighbours
// of the board.
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < N; j++) {
// Condition for skipping the
// current state
if (j != state[i]) {
// Initializing temporary
// neighbour with the
// current neighbour.
NeighbourState[i] = j;
NeighbourBoard[NeighbourState[i]][i]
= 1;
NeighbourBoard[state[i]][i]
= 0;
// Calculating the objective
// value of the neighbour.
int temp
= calculateObjective(
NeighbourBoard,
NeighbourState);
// Comparing temporary and optimal
// neighbour objectives and if
// temporary is less than optimal
// then updating accordingly.
if (temp <= opObjective) {
opObjective = temp;
copyState(opState,
NeighbourState);
generateBoard(opBoard,
opState);
}
// Going back to the original
// configuration for the next
// iteration.
NeighbourBoard[NeighbourState[i]][i]
= 0;
NeighbourState[i] = state[i];
NeighbourBoard[state[i]][i] = 1;
}
}
}
// Copying the optimal board and
// state thus found to the current
// board and, state since c++ doesn't
// allow returning multiple values.
copyState(state, opState);
fill(board, 0);
generateBoard(board, state);
} void hillClimbing( int board[][N],
int * state)
{ // Declaring and initializing the
// neighbour board and state with
// the current board and the state
// as the starting point.
int neighbourBoard[N][N] = {};
int neighbourState[N];
copyState(neighbourState, state);
generateBoard(neighbourBoard,
neighbourState);
do {
// Copying the neighbour board and
// state to the current board and
// state, since a neighbour
// becomes current after the jump.
copyState(state, neighbourState);
generateBoard(board, state);
// Getting the optimal neighbour
getNeighbour(neighbourBoard,
neighbourState);
if (compareStates(state,
neighbourState)) {
// If neighbour and current are
// equal then no optimal neighbour
// exists and therefore output the
// result and break the loop.
printBoard(board);
break ;
}
else if (calculateObjective(board,
state)
== calculateObjective(
neighbourBoard,
neighbourState)) {
// If neighbour and current are
// not equal but their objectives
// are equal then we are either
// approaching a shoulder or a
// local optimum, in any case,
// jump to a random neighbour
// to escape it.
// Random neighbour
neighbourState[ rand () % N]
= rand () % N;
generateBoard(neighbourBoard,
neighbourState);
}
} while ( true );
} // Driver code int main()
{ int state[N] = {};
int board[N][N] = {};
// Getting a starting point by
// randomly configuring the board
configureRandomly(board, state);
// Do hill climbing on the
// board obtained
hillClimbing(board, state);
return 0;
} |
import java.util.Arrays;
import java.util.Random;
public class NQueensHillClimbing {
static final int N = 8 ;
public static void main(String[] args) {
int [] state = new int [N];
int [][] board = new int [N][N];
configureRandomly(board, state);
hillClimbing(board, state);
}
static void configureRandomly( int [][] board, int [] state) {
Random rand = new Random();
for ( int i = 0 ; i < N; i++) {
state[i] = rand.nextInt(N);
board[state[i]][i] = 1 ;
}
}
static void printBoard( int [][] board) {
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < N; j++) {
System.out.print( " " + board[i][j] + " " );
}
System.out.println();
}
}
static void printState( int [] state) {
System.out.println( " " + Arrays.toString(state) + " " );
}
static boolean compareStates( int [] state1, int [] state2) {
return Arrays.equals(state1, state2);
}
static void fill( int [][] board, int value) {
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < N; j++) {
board[i][j] = value;
}
}
}
static int calculateObjective( int [][] board, int [] state) {
int attacking = 0 ;
for ( int i = 0 ; i < N; i++) {
int row, col;
row = state[i];
col = i - 1 ;
while (col >= 0 && board[row][col] != 1 ) {
col--;
}
if (col >= 0 && board[row][col] == 1 ) {
attacking++;
}
row = state[i];
col = i + 1 ;
while (col < N && board[row][col] != 1 ) {
col++;
}
if (col < N && board[row][col] == 1 ) {
attacking++;
}
row = state[i] - 1 ;
col = i - 1 ;
while (col >= 0 && row >= 0 && board[row][col] != 1 ) {
col--;
row--;
}
if (col >= 0 && row >= 0 && board[row][col] == 1 ) {
attacking++;
}
row = state[i] + 1 ;
col = i + 1 ;
while (col < N && row < N && board[row][col] != 1 ) {
col++;
row++;
}
if (col < N && row < N && board[row][col] == 1 ) {
attacking++;
}
row = state[i] + 1 ;
col = i - 1 ;
while (col >= 0 && row < N && board[row][col] != 1 ) {
col--;
row++;
}
if (col >= 0 && row < N && board[row][col] == 1 ) {
attacking++;
}
row = state[i] - 1 ;
col = i + 1 ;
while (col < N && row >= 0 && board[row][col] != 1 ) {
col++;
row--;
}
if (col < N && row >= 0 && board[row][col] == 1 ) {
attacking++;
}
}
return attacking / 2 ;
}
static void generateBoard( int [][] board, int [] state) {
fill(board, 0 );
for ( int i = 0 ; i < N; i++) {
board[state[i]][i] = 1 ;
}
}
static void copyState( int [] state1, int [] state2) {
System.arraycopy(state2, 0 , state1, 0 , N);
}
static void getNeighbour( int [][] board, int [] state) {
int [][] opBoard = new int [N][N];
int [] opState = new int [N];
copyState(opState, state);
generateBoard(opBoard, opState);
int opObjective = calculateObjective(opBoard, opState);
int [][] neighbourBoard = new int [N][N];
int [] neighbourState = new int [N];
copyState(neighbourState, state);
generateBoard(neighbourBoard, neighbourState);
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < N; j++) {
if (j != state[i]) {
neighbourState[i] = j;
neighbourBoard[neighbourState[i]][i] = 1 ;
neighbourBoard[state[i]][i] = 0 ;
int temp = calculateObjective(neighbourBoard, neighbourState);
if (temp <= opObjective) {
opObjective = temp;
copyState(opState, neighbourState);
generateBoard(opBoard, opState);
}
neighbourBoard[neighbourState[i]][i] = 0 ;
neighbourState[i] = state[i];
neighbourBoard[state[i]][i] = 1 ;
}
}
}
copyState(state, opState);
fill(board, 0 );
generateBoard(board, state);
}
static void hillClimbing( int [][] board, int [] state) {
int [][] neighbourBoard = new int [N][N];
int [] neighbourState = new int [N];
generateBoard(neighbourBoard, neighbourState);
do {
copyState(state, neighbourState);
generateBoard(board, state);
getNeighbour(neighbourBoard, neighbourState);
if (compareStates(state, neighbourState)) {
printBoard(board);
break ;
} else if (calculateObjective(board, state) == calculateObjective(neighbourBoard,
neighbourState)) {
neighbourState[( int ) (Math.random() * N)] = ( int ) (Math.random() * N);
generateBoard(neighbourBoard, neighbourState);
}
} while ( true );
}
} |
# Python3 implementation of the # above approach from random import randint
N = 8
# A utility function that configures # the 2D array "board" and # array "state" randomly to provide # a starting point for the algorithm. def configureRandomly(board, state):
# Iterating through the
# column indices
for i in range (N):
# Getting a random row index
state[i] = randint( 0 , 100000 ) % N;
# Placing a queen on the
# obtained place in
# chessboard.
board[state[i]][i] = 1 ;
# A utility function that prints # the 2D array "board". def printBoard(board):
for i in range (N):
print ( * board[i])
# A utility function that prints # the array "state". def printState( state):
print ( * state)
# A utility function that compares # two arrays, state1 and state2 and # returns True if equal # and False otherwise. def compareStates(state1, state2):
for i in range (N):
if (state1[i] ! = state2[i]):
return False ;
return True ;
# A utility function that fills # the 2D array "board" with # values "value" def fill(board, value):
for i in range (N):
for j in range (N):
board[i][j] = value;
# This function calculates the # objective value of the # state(queens attacking each other) # using the board by the # following logic. def calculateObjective( board, state):
# For each queen in a column, we check
# for other queens falling in the line
# of our current queen and if found,
# any, then we increment the variable
# attacking count.
# Number of queens attacking each other,
# initially zero.
attacking = 0 ;
# Variables to index a particular
# row and column on board.
for i in range (N):
# At each column 'i', the queen is
# placed at row 'state[i]', by the
# definition of our state.
# To the left of same row
# (row remains constant
# and col decreases)
row = state[i]
col = i - 1 ;
while (col > = 0 and board[row][col] ! = 1 ) :
col - = 1
if (col > = 0 and board[row][col] = = 1 ) :
attacking + = 1 ;
# To the right of same row
# (row remains constant
# and col increases)
row = state[i]
col = i + 1 ;
while (col < N and board[row][col] ! = 1 ):
col + = 1 ;
if (col < N and board[row][col] = = 1 ) :
attacking + = 1 ;
# Diagonally to the left up
# (row and col simultaneously
# decrease)
row = state[i] - 1
col = i - 1 ;
while (col > = 0 and row > = 0 and board[row][col] ! = 1 ) :
col - = 1 ;
row - = 1 ;
if (col > = 0 and row > = 0 and board[row][col] = = 1 ) :
attacking + = 1 ;
# Diagonally to the right down
# (row and col simultaneously
# increase)
row = state[i] + 1
col = i + 1 ;
while (col < N and row < N and board[row][col] ! = 1 ) :
col + = 1 ;
row + = 1 ;
if (col < N and row < N and board[row][col] = = 1 ) :
attacking + = 1 ;
# Diagonally to the left down
# (col decreases and row
# increases)
row = state[i] + 1
col = i - 1 ;
while (col > = 0 and row < N and board[row][col] ! = 1 ) :
col - = 1 ;
row + = 1 ;
if (col > = 0 and row < N and board[row][col] = = 1 ) :
attacking + = 1 ;
# Diagonally to the right up
# (col increases and row
# decreases)
row = state[i] - 1
col = i + 1 ;
while (col < N and row > = 0 and board[row][col] ! = 1 ) :
col + = 1 ;
row - = 1 ;
if (col < N and row > = 0 and board[row][col] = = 1 ) :
attacking + = 1 ;
# Return pairs.
return int (attacking / 2 );
# A utility function that # generates a board configuration # given the state. def generateBoard( board, state):
fill(board, 0 );
for i in range (N):
board[state[i]][i] = 1 ;
# A utility function that copies # contents of state2 to state1. def copyState( state1, state2):
for i in range (N):
state1[i] = state2[i];
# This function gets the neighbour # of the current state having # the least objective value # amongst all neighbours as # well as the current state. def getNeighbour(board, state):
# Declaring and initializing the
# optimal board and state with
# the current board and the state
# as the starting point.
opBoard = [[ 0 for _ in range (N)] for _ in range (N)]
opState = [ 0 for _ in range (N)]
copyState(opState, state);
generateBoard(opBoard, opState);
# Initializing the optimal
# objective value
opObjective = calculateObjective(opBoard, opState);
# Declaring and initializing
# the temporary board and
# state for the purpose
# of computation.
NeighbourBoard = [[ 0 for _ in range (N)] for _ in range (N)]
NeighbourState = [ 0 for _ in range (N)]
copyState(NeighbourState, state);
generateBoard(NeighbourBoard, NeighbourState);
# Iterating through all
# possible neighbours
# of the board.
for i in range (N):
for j in range (N):
# Condition for skipping the
# current state
if (j ! = state[i]) :
# Initializing temporary
# neighbour with the
# current neighbour.
NeighbourState[i] = j;
NeighbourBoard[NeighbourState[i]][i] = 1 ;
NeighbourBoard[state[i]][i] = 0 ;
# Calculating the objective
# value of the neighbour.
temp = calculateObjective( NeighbourBoard, NeighbourState);
# Comparing temporary and optimal
# neighbour objectives and if
# temporary is less than optimal
# then updating accordingly.
if (temp < = opObjective) :
opObjective = temp;
copyState(opState, NeighbourState);
generateBoard(opBoard, opState);
# Going back to the original
# configuration for the next
# iteration.
NeighbourBoard[NeighbourState[i]][i] = 0 ;
NeighbourState[i] = state[i];
NeighbourBoard[state[i]][i] = 1 ;
# Copying the optimal board and
# state thus found to the current
# board and, state since c+= 1 doesn't
# allow returning multiple values.
copyState(state, opState);
fill(board, 0 );
generateBoard(board, state);
def hillClimbing(board, state):
# Declaring and initializing the
# neighbour board and state with
# the current board and the state
# as the starting point.
neighbourBoard = [[ 0 for _ in range (N)] for _ in range (N)
neighbourState = [ 0 for _ in range (N)]
copyState(neighbourState, state);
generateBoard(neighbourBoard, neighbourState);
while True :
# Copying the neighbour board and
# state to the current board and
# state, since a neighbour
# becomes current after the jump.
copyState(state, neighbourState);
generateBoard(board, state);
# Getting the optimal neighbour
getNeighbour(neighbourBoard, neighbourState);
if (compareStates(state, neighbourState)) :
# If neighbour and current are
# equal then no optimal neighbour
# exists and therefore output the
# result and break the loop.
printBoard(board);
break ;
elif (calculateObjective(board, state) = = calculateObjective( neighbourBoard,neighbourState)):
# If neighbour and current are
# not equal but their objectives
# are equal then we are either
# approaching a shoulder or a
# local optimum, in any case,
# jump to a random neighbour
# to escape it.
# Random neighbour
neighbourState[randint( 0 , 100000 ) % N] = randint( 0 , 100000 ) % N;
generateBoard(neighbourBoard, neighbourState);
# Driver code state = [ 0 ] * N
board = [[ 0 for _ in range (N)] for _ in range (N)]
# Getting a starting point by # randomly configuring the board configureRandomly(board, state); # Do hill climbing on the # board obtained hillClimbing(board, state); # This code is contributed by phasing17. |
using System;
class Program
{ const int N = 8;
static void ConfigureRandomly( int [,] board, int [] state)
{
Random rand = new Random();
for ( int i = 0; i < N; i++)
{
state[i] = rand.Next(N);
board[state[i], i] = 1;
}
}
static void PrintBoard( int [,] board)
{
for ( int i = 0; i < N; i++)
{
Console.Write( " " );
for ( int j = 0; j < N; j++)
{
Console.Write(board[i, j] + " " );
}
Console.WriteLine();
}
}
static void PrintState( int [] state)
{
for ( int i = 0; i < N; i++)
{
Console.Write( " " + state[i] + " " );
}
Console.WriteLine();
}
static bool CompareStates( int [] state1, int [] state2)
{
for ( int i = 0; i < N; i++)
{
if (state1[i] != state2[i])
{
return false ;
}
}
return true ;
}
static void Fill( int [,] board, int value)
{
for ( int i = 0; i < N; i++)
{
for ( int j = 0; j < N; j++)
{
board[i, j] = value;
}
}
}
static int CalculateObjective( int [,] board, int [] state)
{
int attacking = 0;
int row, col;
for ( int i = 0; i < N; i++)
{
row = state[i];
col = i - 1;
while (col >= 0 && board[row, col] != 1)
{
col--;
}
if (col >= 0 && board[row, col] == 1)
{
attacking++;
}
row = state[i];
col = i + 1;
while (col < N && board[row, col] != 1)
{
col++;
}
if (col < N && board[row, col] == 1)
{
attacking++;
}
row = state[i] - 1;
col = i - 1;
while (col >= 0 && row >= 0 && board[row, col] != 1)
{
col--;
row--;
}
if (col >= 0 && row >= 0 && board[row, col] == 1)
{
attacking++;
}
row = state[i] + 1;
col = i + 1;
while (col < N && row < N && board[row, col] != 1)
{
col++;
row++;
}
if (col < N && row < N && board[row, col] == 1)
{
attacking++;
}
row = state[i] + 1;
col = i - 1;
while (col >= 0 && row < N && board[row, col] != 1)
{
col--;
row++;
}
if (col >= 0 && row < N && board[row, col] == 1)
{
attacking++;
}
row = state[i] - 1;
col = i + 1;
while (col < N && row >= 0 && board[row, col] != 1)
{
col++;
row--;
}
if (col < N && row >= 0 && board[row, col] == 1)
{
attacking++;
}
}
return attacking / 2;
}
static void GenerateBoard( int [,] board, int [] state)
{
Fill(board, 0);
for ( int i = 0; i < N; i++)
{
board[state[i], i] = 1;
}
}
static void CopyState( int [] state1, int [] state2)
{
Array.Copy(state2, state1, N);
}
static void GetNeighbour( int [,] board, int [] state)
{
int [,] opBoard = new int [N, N];
int [] opState = new int [N];
CopyState(opState, state);
GenerateBoard(opBoard, opState);
int opObjective = CalculateObjective(opBoard, opState);
int [,] neighbourBoard = new int [N, N];
int [] neighbourState = new int [N];
CopyState(neighbourState, state);
GenerateBoard(neighbourBoard, neighbourState);
for ( int i = 0; i < N; i++)
{
for ( int j = 0; j < N; j++)
{
if (j != state[i])
{
neighbourState[i] = j;
neighbourBoard[neighbourState[i], i] = 1;
neighbourBoard[state[i], i] = 0;
int temp = CalculateObjective(neighbourBoard, neighbourState);
if (temp <= opObjective)
{
opObjective = temp;
CopyState(opState, neighbourState);
GenerateBoard(opBoard, opState);
}
neighbourBoard[neighbourState[i], i] = 0;
neighbourState[i] = state[i];
neighbourBoard[state[i], i] = 1;
}
}
}
CopyState(state, opState);
Fill(board, 0);
GenerateBoard(board, state);
}
static void HillClimbing( int [,] board, int [] state)
{
int [,] neighbourBoard = new int [N, N];
int [] neighbourState = new int [N];
CopyState(neighbourState, state);
GenerateBoard(neighbourBoard, neighbourState);
do
{
CopyState(state, neighbourState);
GenerateBoard(board, state);
GetNeighbour(neighbourBoard, neighbourState);
if (CompareStates(state, neighbourState))
{
PrintBoard(board);
break ;
}
else if (CalculateObjective(board, state) == CalculateObjective(neighbourBoard, neighbourState))
{
neighbourState[ new Random().Next(N)] = new Random().Next(N);
GenerateBoard(neighbourBoard, neighbourState);
}
} while ( true );
}
static void Main( string [] args)
{
int [] state = new int [N];
int [,] board = new int [N, N];
ConfigureRandomly(board, state);
HillClimbing(board, state);
}
} |
// JS implementation of the // above approach let N = 8 // A utility function that configures // the 2D array "board" and // array "state" randomly to provide // a starting point for the algorithm. function configureRandomly(board, state)
{ // Iterating through the
// column indices
for ( var i = 0; i < N; i++) {
// Getting a random row index
state[i] = Math.floor(Math.random() * 100000) % N;
// Placing a queen on the
// obtained place in
// chessboard.
board[state[i]][i] = 1;
}
} // A utility function that prints // the 2D array "board". function printBoard(board)
{ for ( var i = 0; i < N; i++) {
process.stdout.write( " " );
for ( var j = 0; j < N; j++) {
process.stdout.write(board[i][j] + " " );
}
process.stdout.write( "\n" );
}
} // A utility function that prints // the array "state". function printState( state)
{ for ( var i = 0; i < N; i++) {
process.stdout.write( " " + state[i] + " " );
}
process.stdout.write( "\n" );
} // A utility function that compares // two arrays, state1 and state2 and // returns true if equal // and false otherwise. function compareStates(state1,
state2)
{ for ( var i = 0; i < N; i++) {
if (state1[i] != state2[i]) {
return false ;
}
}
return true ;
} // A utility function that fills // the 2D array "board" with // values "value" function fill(board, value)
{ for ( var i = 0; i < N; i++) {
for ( var j = 0; j < N; j++) {
board[i][j] = value;
}
}
} // This function calculates the // objective value of the // state(queens attacking each other) // using the board by the // following logic. function calculateObjective( board,
state)
{ // For each queen in a column, we check
// for other queens falling in the line
// of our current queen and if found,
// any, then we increment the variable
// attacking count.
// Number of queens attacking each other,
// initially zero.
var attacking = 0;
// Variables to index a particular
// row and column on board.
var row, col;
for ( var i = 0; i < N; i++) {
// At each column 'i', the queen is
// placed at row 'state[i]', by the
// definition of our state.
// To the left of same row
// (row remains constant
// and col decreases)
row = state[i], col = i - 1;
while (col >= 0
&& board[row][col] != 1) {
col--;
}
if (col >= 0
&& board[row][col] == 1) {
attacking++;
}
// To the right of same row
// (row remains constant
// and col increases)
row = state[i], col = i + 1;
while (col < N
&& board[row][col] != 1) {
col++;
}
if (col < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the left up
// (row and col simultaneously
// decrease)
row = state[i] - 1, col = i - 1;
while (col >= 0 && row >= 0
&& board[row][col] != 1) {
col--;
row--;
}
if (col >= 0 && row >= 0
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the right down
// (row and col simultaneously
// increase)
row = state[i] + 1, col = i + 1;
while (col < N && row < N
&& board[row][col] != 1) {
col++;
row++;
}
if (col < N && row < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the left down
// (col decreases and row
// increases)
row = state[i] + 1, col = i - 1;
while (col >= 0 && row < N
&& board[row][col] != 1) {
col--;
row++;
}
if (col >= 0 && row < N
&& board[row][col] == 1) {
attacking++;
}
// Diagonally to the right up
// (col increases and row
// decreases)
row = state[i] - 1, col = i + 1;
while (col < N && row >= 0
&& board[row][col] != 1) {
col++;
row--;
}
if (col < N && row >= 0
&& board[row][col] == 1) {
attacking++;
}
}
// Return pairs.
return Math.floor(attacking / 2);
} // A utility function that // generates a board configuration // given the state. function generateBoard( board,
state)
{ fill(board, 0);
for ( var i = 0; i < N; i++) {
board[state[i]][i] = 1;
}
} // A utility function that copies // contents of state2 to state1. function copyState( state1, state2)
{ for ( var i = 0; i < N; i++) {
state1[i] = state2[i];
}
} // This function gets the neighbour // of the current state having // the least objective value // amongst all neighbours as // well as the current state. function getNeighbour(board,
state)
{ // Declaring and initializing the
// optimal board and state with
// the current board and the state
// as the starting point.
var opBoard = new Array(N);
for ( var i = 0; i < N; i++)
opBoard[i] = new Array(N).fill(0);
var opState = new Array(N).fill(0);
copyState(opState,
state);
generateBoard(opBoard,
opState);
// Initializing the optimal
// objective value
var opObjective = calculateObjective(opBoard,
opState);
// Declaring and initializing
// the temporary board and
// state for the purpose
// of computation.
var NeighbourBoard = new Array(N).fill( new Array(N).fill(0));
var NeighbourState = new Array(N).fill(0);
copyState(NeighbourState,
state);
generateBoard(NeighbourBoard,
NeighbourState);
// Iterating through all
// possible neighbours
// of the board.
for ( var i = 0; i < N; i++) {
for ( var j = 0; j < N; j++) {
// Condition for skipping the
// current state
if (j != state[i]) {
// Initializing temporary
// neighbour with the
// current neighbour.
NeighbourState[i] = j;
NeighbourBoard[NeighbourState[i]][i]
= 1;
NeighbourBoard[state[i]][i]
= 0;
// Calculating the objective
// value of the neighbour.
var temp
= calculateObjective(
NeighbourBoard,
NeighbourState);
// Comparing temporary and optimal
// neighbour objectives and if
// temporary is less than optimal
// then updating accordingly.
if (temp <= opObjective) {
opObjective = temp;
copyState(opState,
NeighbourState);
generateBoard(opBoard,
opState);
}
// Going back to the original
// configuration for the next
// iteration.
NeighbourBoard[NeighbourState[i]][i]
= 0;
NeighbourState[i] = state[i];
NeighbourBoard[state[i]][i] = 1;
}
}
}
// Copying the optimal board and
// state thus found to the current
// board and, state since c++ doesn't
// allow returning multiple values.
copyState(state, opState);
fill(board, 0);
generateBoard(board, state);
} function hillClimbing(board, state)
{ // Declaring and initializing the
// neighbour board and state with
// the current board and the state
// as the starting point.
var neighbourBoard = new Array(N);
for ( var i = 0; i < N; i++)
neighbourBoard[i] = new Array(N).fill(0);
var neighbourState = new Array(N).fill(0)
copyState(neighbourState, state);
generateBoard(neighbourBoard,
neighbourState);
do {
// Copying the neighbour board and
// state to the current board and
// state, since a neighbour
// becomes current after the jump.
copyState(state, neighbourState);
generateBoard(board, state);
// Getting the optimal neighbour
getNeighbour(neighbourBoard,
neighbourState);
if (compareStates(state,
neighbourState)) {
// If neighbour and current are
// equal then no optimal neighbour
// exists and therefore output the
// result and break the loop.
printBoard(board);
break ;
}
else if (calculateObjective(board,
state)
== calculateObjective(
neighbourBoard,
neighbourState)) {
// If neighbour and current are
// not equal but their objectives
// are equal then we are either
// approaching a shoulder or a
// local optimum, in any case,
// jump to a random neighbour
// to escape it.
// Random neighbour
neighbourState[(Math.floor(Math.random() * 100000) % N)]
= Math.floor(Math.random() * 100000) % N;
generateBoard(neighbourBoard,
neighbourState);
}
} while ( true );
} // Driver code var state = new Array(N).fill(0);
var board = new Array(N);
for ( var i = 0; i < N; i++)
board[i] = new Array(N).fill(0);
// Getting a starting point by
// randomly configuring the board
configureRandomly(board, state);
// Do hill climbing on the
// board obtained
hillClimbing(board, state);
// This code is contributed by phasing17. |
0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0
Complexity Analysis
-
The time complexity for this algorithm can be divided into three parts:
-
Calculating Objective – The calculation of objective involves iterating through all queens on board and checking the no. of attacking queens, which is done by our calculateObjective function in O(N2) time.
-
Neighbour Selection and Number of neighbours – The description of neighbours in our problem gives a total of N(N-1) neighbours for the current state. The selection procedure is best fit and therefore requires iterating through all neighbours, which is again O(N2).
-
Search Space – Search space of our problem consists of a total of NN states, corresponding to all possible configurations of the N Queens on board. Note that this is after taking into account the additional constraint of one queen per column.
-
Calculating Objective – The calculation of objective involves iterating through all queens on board and checking the no. of attacking queens, which is done by our calculateObjective function in O(N2) time.
-
Therefore, the worst-case time complexity of our algorithm is O(NN). But, this worst-case occurs rarely in practice and thus we can safely consider it to be as good as any other algorithm there is for the N Queen problem. Hence, the effective time complexity consists of only calculating the objective for all neighbours up to a certain depth(no of jumps the search makes), which does not depend on N. Therefore, if the depth of search is d then the time complexity is O(N2 * N2 * d), which is O(d*N4).