patterncppMinor
Chess puzzle improvement
Viewed 0 times
chessimprovementpuzzle
Problem
Is it possible to optimize this solution to the N-queens puzzle?
#include
#include
#include
#define N 10
using namespace std;
/* print solution */
void printSolution(int board[N][N])
{
for (int i = 0; i = 0 && j >= 0; i--, j--)
{
if (board[i][j])
return false;
}
for (i = row, j = col; j >= 0 && i = N)
return true;
for (int i = 0; i < N; i++)
{
if ( isSafe(board, i, col) )
{
board[i][col] = 1;
if (solveNQUtil(board, col + 1) == true)
return true;
board[i][col] = 0;
}
}
return false;
}
/* solves the N Queen problem using Backtracking.*/
bool solveNQ()
{
int board[N][N] = {0};
if (solveNQUtil(board, 0) == false)
{
cout<<"Solution does not exist"<<endl;
return false;
}
printSolution(board);
return true;
}
// Main
int main()
{
solveNQ();
return 0;
}Solution
This can definitely be optimized. First, though, a few style points. Don't use
Sort your imports. Don't use
This:
should be
Be consistent with spacing (eg. don't add spacing inside brackets in only one place). Use
Remove
I'd rename
Convert
You should use
Now the tiny things are out of the way, consider improving
Consider giving a better description of
This can be simplified by compressing the
It looks like this:
The next thing to do is optimize
However, there's a special trick to just enumerate the set bits in a value, making this unneeded. I'll just convert the code from the Java version, since it's what I'd get to anyway.
This optimization takes it from a good 23 seconds for N=30 to to just 0.36; or a speed improvement of almost 100x. This gives
#define N as Hosch250 says. Jamal points out to use std:array; this can make some later aspects nicer, too.Sort your imports. Don't use
using namespace std. I'm not sure why you imported cstdio or cstdlib... so don't. Space your operators, even <<. Use braces around all blocks.This:
if (x == true)
if (x == false)should be
if (x)
if (!x)Be consistent with spacing (eg. don't add spacing inside brackets in only one place). Use
const in printSolution.Remove
return 0 from main and the useless // Main comment. Similarly for the / print solution / comment. I'm personally not a fan of / this comment style / although I admit that's particularly subjective.I'd rename
printSolution to printBoard, since it doesn't require a solution.Convert
ints to bools where appropriate.You should use
++i and --i over the postfix forms by convention.Now the tiny things are out of the way, consider improving
printSolution with range-based for loops:void printSolution(const std::array, N> &board)
{
for (auto row : board)
{
for (auto val : row)
{
std::cout << val << " ";
}
std::cout << std::endl;
}
}Consider giving a better description of
isSafe and what it assumes:// Check if a queen can be placed at (row, col)
// This assumes that there is exactly one queen in each column
// less than col and none in any other.This can be simplified by compressing the
array to a 1-dimensional array of integers, since we know that the positions are bounded to one-per-column. This speeds up isSafe, improving timings.It looks like this:
#include
#include
const int N = 25;
void printBoard(const std::array &board)
{
for (int i = 0; i &board, int row, int col)
{
for (int i = 1; i &board, int col)
{
if (col >= N)
{
return true;
}
for (int i = 0; i board{};
if (!solveNQRecurse(board, 0))
{
std::cout << "Solution does not exist" << std::endl;
return false;
}
printBoard(board);
return true;
}
int main()
{
solveNQ();
}The next thing to do is optimize
isSafe by converting it to a bitset. Since it will be useful later, I will use an uint64_t instead of an std::bitset; this is fine since a 65x65 grid won't be quickly solvable anyway. The idea is expressed in the Java version of this question; you keep track of three masks of which diagonals and rows are free. You then shift these and combine them with &. This makes isSafe justbool isSafe(uint64_t rowsFree, uint64_t upDiagFree, uint64_t downDiagFree, uint64_t col) {
return (rowsFree & upDiagFree & downDiagFree) & (1ULL << col);
}However, there's a special trick to just enumerate the set bits in a value, making this unneeded. I'll just convert the code from the Java version, since it's what I'd get to anyway.
This optimization takes it from a good 23 seconds for N=30 to to just 0.36; or a speed improvement of almost 100x. This gives
#include
#include
#include
uint64_t const N = 30;
void printBoard(std::array const &board)
{
for (uint64_t i = 0; i &board,
uint64_t const rowsFree,
uint64_t const upDiagFree,
uint64_t const downDiagFree,
uint64_t const col
)
{
if (!rowsFree) {
return true;
}
uint64_t spaces = rowsFree & upDiagFree & downDiagFree;
while (spaces)
{
uint64_t bit;
splitLowestBit(spaces, bit);
board[col] = bit;
bool solvable = solveNQRecurse(
board,
(bit ^ rowsFree),
((bit ^ upDiagFree) >> 1ULL) | (1ULL board{};
uint64_t fullMask = (1ULL << N) - 1ULL;
bool solvable = solveNQRecurse(
board, // board
fullMask, // rowsFree
fullMask, // upDiagFree
fullMask, // downDiagFree
0 // col
);
if (solvable)
{
printBoard(board);
}
else
{
std::cout << "Solution does not exist" << std::endl;
}
return solvable;
}
int main()
{
solveNQ();
}Code Snippets
if (x == true)
if (x == false)if (x)
if (!x)void printSolution(const std::array<std::array<int, N>, N> &board)
{
for (auto row : board)
{
for (auto val : row)
{
std::cout << val << " ";
}
std::cout << std::endl;
}
}// Check if a queen can be placed at (row, col)
// This assumes that there is exactly one queen in each column
// less than col and none in any other.#include <array>
#include <iostream>
const int N = 25;
void printBoard(const std::array<int, N> &board)
{
for (int i = 0; i < board.size(); ++i)
{
for (auto val : board)
{
std::cout << (val == i) << " ";
}
std::cout << std::endl;
}
}
// Check if a queen can be placed at (row, col)
// This assumes that there is exactly one queen in each column
// less than col and none in any other.
bool isSafe(std::array<int, N> &board, int row, int col)
{
for (int i = 1; i <= col; ++i)
{
if (board[col-i] == row)
{
return false;
}
// Up diagonal
if (board[col-i] == row - i)
{
return false;
}
// Down diagonal
if (board[col-i] == row + i)
{
return false;
}
}
return true;
}
bool solveNQRecurse(std::array<int, N> &board, int col)
{
if (col >= N)
{
return true;
}
for (int i = 0; i < N; ++i)
{
if (isSafe(board, i, col))
{
board[col] = i;
if (solveNQRecurse(board, col + 1))
{
return true;
}
}
}
return false;
}
/* solves the N Queen problem using Backtracking.*/
bool solveNQ()
{
std::array<int, N> board{};
if (!solveNQRecurse(board, 0))
{
std::cout << "Solution does not exist" << std::endl;
return false;
}
printBoard(board);
return true;
}
int main()
{
solveNQ();
}Context
StackExchange Code Review Q#77669, answer score: 7
Revisions (0)
No revisions yet.