More N-Queens Puzzle Results by James Pate Williams, Jr.

N-Queens Tests				
8 Queens	Microseconds		50 Samples	
Algorithm	Minimum	Average	Maximum	Std Dev
Arc-Consistency 3	1846	4148	14220	2294
Arc-Consistency 4	3850	8987	27903	5022
Backjump	175	1057	3873	883
Backmark	198	737	3323	596
Backtracking	171	789	3132	652
Hill-Climber	1991	2097	2913	157
				
9 Queens	Microseconds		50 Samples	
Algorithm	Minimum	Average	Maximum	Std Dev
Arc-Consistency 3	2628	5785	15748	3202
Arc-Consistency 4	7492	18057	56587	11114
Backjump	208	1410	6184	1325
Backmark	214	866	4468	880
Backtracking	210	1269	6156	1186
Hill-Climber	1309	1398	1953	102
				
10 Queens	Microseconds		50 Samples	
Algorithm	Minimum	Average	Maximum	Std Dev
Arc-Consistency 3	3820	10680	30708	6963
Arc-Consistency 4	12624	46645	148221	31212
Backjump	248	3970	16736	3805
Backmark	283	1504	7613	1433
Backtracking	227	2857	12882	3022
Hill-Climber	24575	25608	33563	2037

My hill-climber results are pretty disappointing. I have gotten some decent runs of back-marking for n = 50 and n = 60 queens. I show the n = 60 queens experiment below.

==Menu==
**Instance Submenu**
1 Single Instance Test
2 Multiple Instances Test
3 Exit
Enter an option '1' to '3': 2
**Algorithm Submenu**
1 Arc-Consistency 3
2 Arc-Consistency 4
3 Backjump
4 Backmark
5 Chronological Bactracking
6 Evolutionary Hill-Climber
7 Exit
Enter an algorithm ('1' to '7'): 4
Enter the number of queens for option '2': 60
Enter the number of samples to be analyzed: 10
Minimum Runtime in Microseconds: 4819
Average Runtime in Microseconds: 10419330
Maximum Runtime in Micorseconds: 45439444
Standard Sample Deviation: 16665688

Notice the misspelling of microseconds.

Six C++ Algorithms to Solve the N-Queens Puzzle (Problem) by James Pate Williams, Jr.

Most of the six algorithms were developed back in the years 1999 to 2001 when I was a Master of Software Engineering and Doctoral candidate at Auburn University. Professor Gerry V. Dozier was my Master research advisor. The algorithms developed were in alphabetical order:

  • Arc-Consistency 3
  • Arc-Consistency 4
  • Backjump
  • Backmark
  • Chronological Backtracking
  • Evolutionary Hill-Climber (My algorithm)

The first five algorithms were stated in pseudocode in the excellent treatise Foundations of Constraint Satisfaction by Edward Tsang. Below are six runs of my test C++ application (Win32 Console App) using 8 queens and 50 samples.

The Graph Coloring Problem by James Pate Williams, Jr.

The Graph Coloring Problem and N-Queens Puzzle are both NP-Complete constraint satisfaction problems. There does not exist polynomial time algorithms to solve large instances of either problem. The Graph Coloring Problem is given a graph of n-vertices and m-edges find the minimum number of colors such that no two connected vertices are of the same color. The N-Queens Puzzle is given a n-by-n chessboard and n-queens place the queens such that no two queens are attacking one another. A queen in chess can move any number of squares diagonally, horizontally, or vertically.

As a Master of Software Engineering graduate student in the era 1998 to 2000, I majored in the subdiscipline of artificial intelligence known as constraint satisfaction. I developed evolutionary hill-climbing algorithms to solve constraint satisfaction problems. I specifically solved instances of the Graph Coloring Problem and the N-Queens Puzzle. I compared my algorithms with other researchers’ algorithms. One of the algorithms tested was Makato Yokoo’s Weak-Commitment Search Algorithm (WCSA). Yakoo wrote a very nice book named “Distributed Constraint Satisfaction” which has excellent flow-charts of several algorithms including the Min-Conflicts Search Algorithm (MCSA) and his WCSA. I implemented both algorithms in the C++ and Java computer languages.

I rewrote the preceding two algorithms in the C# computer language sometime in the epoch 2008 – 2009. Below are three instances of the Graph Coloring Problem with four, eight, and twelve vertices solved by the Yakoo’s WCSA.

Back-marking Algorithm for the N-Queens Problem

Back in the year 1999 I studied Constraint Satisfaction Problems (CSPs) which is a branch of the computer science discipline artificial intelligence (AI). I took a course in Artificial Intelligence and then a Machine Learning course both under Professor Gerry V. Dozier at Auburn University in the Winter and Spring Quarters of 1999. Professor Dozier was my Master of Software Engineering degree advisor. The N-Queens Problem is a constraint satisfaction problem within the setting of a N-by-N chessboard with the queens arranged such the no queens are attacking any other queens. The N-Queens Problem is believed to be a NP-Complete Problem which means only non-deterministic polynomial time algorithms solve the problem. I bought copies of Foundations of Constraint Satisfaction by E. P. K. Tsang for Professor Dozier and me in 2000. The back-marking algorithm is found in section 5.4.3 page 147 of the textbook. I implemented several algorithms from the textbook. Below is the source code for the back-marking algorithm and single runs from N = 4 to N = 12. A single run is not a statistically significantly experiment. Generally, 30 or more experimental trials are needed for statistical significance.

/*
	Author:	James Pate Williams, Jr. (c) 2000 - 2023

	Backmarking algorithm applied to the N-Queens CSP.
	Algorithm from _Foundations of Constraint Satisfaction_
	by E. P. K. Tsang section 5.4.3 page 147.
*/

#include <iomanip>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <vector>
#include <chrono>
using namespace std::chrono;
using namespace std;

// https://www.geeksforgeeks.org/measure-execution-time-function-cpp/

int constraintsViolated(vector<int>& Q) {
	int a, b, c = 0, i, j, n;

	n = Q.size();
	for (i = 0; i < n; i++) {
		a = Q[i];
		for (j = 0; j < n; j++) {
			b = Q[j];
			if (i != j && a != -1 && b != -1) {
				if (a == b) c++;
				if (i - j == a - b || i - j == b - a) c++;
			}
		}
	}
	return c;
}

bool satisfies(int x, int v, int y, int vp) {
	if (x == y) return false;
	if (v == vp) return false;
	if (x - y == v - vp || x - y == vp - v) return false;
	return true;
}

bool Compatible(int x, int v, int* LowUnit, int* Ordering, int** Mark,
	vector<int> compoundLabel) {
	bool compat = true;
	int vp, y = LowUnit[x];

	while (Ordering[y] < Ordering[x] && compat) {
		if (compoundLabel[y] != -1) {
			vp = compoundLabel[y];
			if (satisfies(x, v, y, vp))
				y = Ordering[y] + 1;
			else
				compat = false;
		}
	}
	Mark[x][v] = Ordering[y];
	return compat;
}

bool BM1(int Level, int* LowUnit, int* Ordering, int** Mark,
	vector<int> unlabelled, vector<int> compoundLabel, vector<int>& solution) {
	bool result;
	int i, v, x, y;
	vector<int> Dx(compoundLabel.size());

	if (unlabelled.empty()) {
		for (i = 0; i < (int)compoundLabel.size(); i++)
			solution[i] = compoundLabel[i];
		return true;
	}
	for (i = 0; i < (int)unlabelled.size(); i++) {
		x = unlabelled[i];
		if (Ordering[x] == Level) break;
	}
	result = false;
	for (i = 0; i < (int)compoundLabel.size(); i++)
		Dx[i] = i;
	do {
		// pick a random value from domain of x
		i = rand() % Dx.size();
		v = Dx[i];
		vector<int>::iterator vIterator = find(Dx.begin(), Dx.end(), v);
		Dx.erase(vIterator);
		if (Mark[x][v] >= LowUnit[x]) {
			if (Compatible(x, v, LowUnit, Ordering, Mark, compoundLabel)) {
				compoundLabel[x] = v;
				vIterator = find(unlabelled.begin(), unlabelled.end(), x);
				unlabelled.erase(vIterator);
				result = BM1(Level + 1, LowUnit, Ordering, Mark,
					unlabelled, compoundLabel, solution);
				if (!result) {
					compoundLabel[x] = -1;
					unlabelled.push_back(x);
				}
			}
		}
	} while (!Dx.empty() && !result);
	if (!result) {
		LowUnit[x] = Level - 1;
		for (i = 0; i < (int)unlabelled.size(); i++) {
			y = unlabelled[i];
			LowUnit[y] = LowUnit[y] < Level - 1 ? LowUnit[y] : Level - 1;
		}
	}
	return result;
}

bool Backmark1(vector<int> unlabelled, vector<int> compoundLabel, vector<int>& solution) {
	int i, n = compoundLabel.size(), v, x;
	int* LowUnit = new int[n];
	int* Ordering = new int[n];
	int** Mark = new int* [n];

	for (i = 0; i < n; i++)
		Mark[i] = new int[n];
	i = 0;
	for (x = 0; x < n; x++) {
		LowUnit[x] = 0;
		for (v = 0; v < n; v++)
			Mark[x][v] = 0;
		Ordering[x] = i++;
	}
	return BM1(0, LowUnit, Ordering, Mark, unlabelled, compoundLabel, solution);
}

void printSolution(vector<int>& solution) {
	char hyphen[256] = { '\0' };
	int column, i, i4, n = solution.size(), row;

	if (n <= 12) {
		for (i = 0; i < n; i++) {
			i4 = i * 4;
			hyphen[i4 + 0] = '-';
			hyphen[i4 + 1] = '-';
			hyphen[i4 + 2] = '-';
			hyphen[i4 + 3] = '-';
		}
		i4 = i * 4;
		hyphen[i4 + 0] = '-';
		hyphen[i4 + 1] = '\n';
		hyphen[i4 + 2] = '\0';
		for (row = 0; row < n; row++) {
			column = solution[row];
			cout << hyphen;
			for (i = 0; i < column; i++)
				cout << "|   ";
			cout << "| Q ";
			for (i = column + 1; i < n; i++)
				cout << "|   ";
			cout << '|' << endl;
		}
		cout << hyphen;
	}
	else
		for (row = 0; row < n; row++)
			cout << row << ' ' << solution[row] << endl;
}

int main(int argc, char* argv[]) {
	while (true)
	{
		int i, n;
		cout << "Number of queens (4 - 12) (0 to quit): ";
		cin >> n;
		if (n == 0)
			break;
		if (n < 4 || n > 12)
		{
			cout << "Illegal number of queens" << endl;
			continue;
		}
		auto start = high_resolution_clock::now();
		vector<int> compoundLabel(n, -1), solution(n, -1), unlabelled(n);
		for (i = 0; i < n; i++)
			unlabelled[i] = i;
		if (Backmark1(unlabelled, compoundLabel, solution))
			printSolution(solution);
		else
			cout << "problem has no solution" << endl;
		auto stop = high_resolution_clock::now();
		auto duration = duration_cast<microseconds>(stop - start);
		cout << "Runtime: " << setprecision(3) << setw(5)
			<< (double)duration.count() / 1.0e3 << " milliseconds" << endl;
	}
	return 0;
}

Iterative Deepening A* Search to Solve the 8-Puzzle and 15-Puzzle by James Pate Williams, Jr.

The 8-puzzle is a child’s tiled toy. The toy consists of 8 numbered tiles and a blank space. The object of the game is to get the tiles in the order 1 to 8 going from the top left hand corner for the number 1 tile around the perimeter clockwise and finish with the space in the center of the 3 x 3 board.

A* search is a complete and optimal informed or heuristic search algorithm. A good source for information on uniformed and informed search procedures is the tome “Artificial Intelligence A Modern Approach First and/or Second Edition” by Stuart Russell and Peter Norvig. The second edition has more info on the 8-puzzle and the 15-puzzle. Iterative deepening A* search is also a complete and optimal search algorithm. Below is an instance of the 8-puzzle that requires 10 steps to reach the goal state. I use a different goal state than Russell and Norvig in the second edition of their textbook.

Initial State of a 8-Puzzle Instance
Goal State of a 8-Puzzle Instance
Initial State of a 15-Puzzle Instance
Goal State of a 15-Puzzle Instance

I developed an application in 2015 that uses 5 search algorithms to solve instances of the 15-puzzle.

Best First Search Algorithm
Breadth First Search Algorithm
Failure of Depth First Algorithm
Failure of IDA* Search
Failure of the A* Search
Best First Search
Failure of Breadth First Search
Failure of Depth First Search
IDA* Search Solution
A* Search Solution