added nqueens benchmark

This commit is contained in:
2025-01-21 16:35:46 +00:00
parent 710fe93ec5
commit 3bf0368a0b
9 changed files with 411 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
CC = cc
BINARY = ./nqueens
CFLAGS += -Wall -mabi=purecap-benchmark
all: clean compile clear run
run:
# run and test input file
$(BINARY)
clean:
rm *.o
clear:
clear
compile: nqueens.o
$(CC) $(CFLAGS) -pg -o $(BINARY) nqueens.o
nqueens.o:
# Ultra fast compilation
$(CC) -c -pg -O3 ./nqueens.c

View File

@@ -0,0 +1,163 @@
# The N-Queens 女王 Problem
The n-queens puzzle is the problem of placing n chess queens on an chessboard so that no two queens threaten each other.
Thus, a solution requires that no two queens share the same **row, column, or diagonal**.
## Usage
```bash
make or make compile && ./nqueens < test.dat
```
Edit ```test.dat``` to the maximum board size you want to test.
The program will test every board from size 1 to n.
Sample output with ```test.dat``` containing n = 5
    
    
    
    
    
### Profiling
```shell
gprof -P -b ./nqueens gmon.out > analysis.txt
```
You can generate a call graph using [ gprof2dot](https://github.com/jrfonseca/gprof2dot) and [GraphViz](http://www.graphviz.org/).
## Recursion
Let's discuss a simple solution to the problem, without implementing any heuristics for optimization, only bruteforce
backtracking.
Have we reached the end of the board (last line)?
* **YES**: Return *TRUE*
* **NO**: Continue
Iterate through the current row.
Place a queen at current position [row][i].
Is this queen possible? (See [**Validation**](https://github.com/felipecustodio/algorithms/new/master/backtracking/nqueens#validation))
* **YES**: Calls recursion to next row.
* **NO**: Remove queen and continue loop.
If the loop has ended and we couldn't place any queen, it means the previous queen is blocking us.
We backtrack to her and continue the process.
## Validation
If there's a queen in:
* Same row
* Same column
* Same diagonals
The function will return *FALSE*.
## Performance
The program asks for a maximum size of board. It'll try to solve every board with increasing size until n.
It doesn't have any restrictions, so it could take hours for a big test case. Use it carefully.
![Board size x Time to solve](http://i.imgur.com/82BNHJ3.png)
![Board size x Time to solve](http://i.imgur.com/b8yzF41.png)
We can observe that the number of attributions and the time needed to solve a board increases a lot with board size.
After the last case test, my computer kept running the program for almost an hour, still not producing the output for the 33 x 33 board.
*Benchmark Machine:*
> OS: Antergos
> Kernel: x86_64 Linux 4.7.6-1-ARCH
> Shell: zsh 5.2
> CPU: Intel Core i5-6200U CPU @ 2.7GHz
> GPU: Mesa DRI Intel(R) HD Graphics 520 (Skylake GT2)
> RAM: 2096MiB / 7854MiB
### Run Results
| board size | calls | time |
|------------|-------------|-------------|
| 0 | 0 | 0.000001s |
| 1 | 1 | 0.000000s |
| 4 | 26 | 0.000005s |
| 5 | 15 | 0.000004s |
| 6 | 171 | 0.000026s |
| 7 | 42 | 0.000009s |
| 8 | 876 | 0.000101s |
| 9 | 333 | 0.000046s |
| 10 | 975 | 0.000117s |
| 11 | 517 | 0.000064s |
| 12 | 3066 | 0.000374s |
| 13 | 1365 | 0.000181s |
| 14 | 26495 | 0.002876s |
| 15 | 20280 | 0.002252s |
| 16 | 160712 | 0.017150s |
| 17 | 91222 | 0.010383s |
| 18 | 743229 | 0.084488s |
| 19 | 48184 | 0.005725s |
| 20 | 3992510 | 0.490726s |
| 21 | 179592 | 0.023457s |
| 22 | 38217905 | 5.092550s |
| 23 | 584591 | 0.081210s |
| 24 | 9878316 | 1.410959s |
| 25 | 1216775 | 0.188140s |
| 26 | 10339849 | 1.599553s |
| 27 | 12263400 | 1.987257s |
| 28 | 84175966 | 14.078644s |
| 29 | 44434525 | 7.684626s |
| 30 | 1692888135 | 298.843353s |
| 31 | 408773285 | 74.617912s |
| 32 | -1495242192 | 526.441956s |
| 33 | 323601164 | 893.228821s |
Number of calls has exceed *long int* on board 32 x 32.
## Algorithm Complexity
Backtracking algorithms have a worst case complexity of O(d^n).
* d = domain (possible values for a variable)
* n = number of variables
For the n-queens problem, we have a domain of 2 (0 or 1) and n² variables.
Consider the 34 * 34 board.
![Board size x Time to solve](https://www5a.wolframalpha.com/Calculate/MSP/MSP3991f5h01dgcf77819100002275f3a04ecb8865?MSPStoreType=image/gif&s=37)
How about it? Without heuristics and a very good implementation, it's insane how much this problem grows.
## Memory
The program will allocate a structure named BOARD, that contains a double int pointer (matrix) and it's size.
After it attempts to solve the board, the heap memory allocated is destroyed.
Tested with **Valgrind**, no memory leaks.
## Flowchart
To better understand the algorithm, here's a handy flowchart of the N-Queens problem without using heuristics:
![n-queens problem without heuristics](https://s17.postimg.org/nm73d06pb/nqueens.png)
>*Keep in mind this is not following proper flowchart rules, it was drawn just for quick reference before an exam*

Binary file not shown.

View File

@@ -0,0 +1,162 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define bool char
#define TRUE 1
#define FALSE 0
typedef struct board {
int n;
int** matrix;
} BOARD;
void printStep(BOARD* b, int x, int y) {
int i, j;
for (i = 0; i < b->n; i++) {
for (j = 0; j < b->n; j++) {
if (i == x && j == y) {
printf("");
} else {
if (b->matrix[i][j]) {
printf("");
} else {
printf("");
}
}
}
printf("\n");
}
printf("\n\n");
}
bool isValid(BOARD* b, int x, int y) {
int i, j;
// check horizontal
for (i = 0; i < b->n; i++) {
if (i != y && b->matrix[x][i] == 1) return FALSE;
}
// check vertical
for (i = 0; i < b->n; i++) {
if (i != x && b->matrix[i][y] == 1) return FALSE;
}
// check diagonals
// check top left
i = x;
j = y;
while (i >= 0 && j >= 0) {
if (i != x && j != y && b->matrix[i][j] == 1) {
return FALSE;
}
i--;
j--;
}
// check top right
i = x;
j = y;
while (i >= 0 && j < b->n) {
if (i != x && j != y && b->matrix[i][j] == 1) {
return FALSE;
}
i--;
j++;
}
// check bottom left
i = x;
j = y;
while (i < b->n && j >= 0) {
if (i != x && j != y && b->matrix[i][j] == 1) {
return FALSE;
}
i++;
j--;
}
// check bottom right
i = x;
j = y;
while (i < b->n && j < b->n) {
if (i != x && j != y && b->matrix[i][j] == 1) {
return FALSE;
}
i++;
j++;
}
return TRUE;
}
bool placeQueen(BOARD** b, int line, long int* calls) {
int i;
if (line >= (*b)->n) return TRUE;
for (i = 0; i < (*b)->n; i++) {
(*b)->matrix[line][i] = 1;
(*calls)++;
if (isValid((*b), line, i) && placeQueen(b, line+1, calls)) {
return TRUE;
}
(*b)->matrix[line][i] = 0;
}
return FALSE;
}
void printBoard(BOARD* b) {
int i, j;
for (i = 0; i < b->n; i++) {
for (j = 0; j < b->n; j++) {
if (b->matrix[i][j]) {
printf("");
} else {
printf("");
}
}
printf("\n");
}
}
void queens(int n) {
// benchmarking
clock_t start_t, end_t;
float delta_t = 0.0;
long int calls = 0;
int i, j;
BOARD* b = (BOARD*)malloc(sizeof(BOARD));
b->n = n;
b->matrix = (int**)malloc(sizeof(int*) * n);
for (i = 0; i < n; i++) {
b->matrix[i] = (int*)malloc(sizeof(int) * n);
for (j = 0; j < n; j++) {
b->matrix[i][j] = 0;
}
}
start_t = clock();
if (placeQueen(&b, 0, &calls)) {
end_t = clock();
// human readable time
delta_t = ((float)(end_t - start_t) / 1000000000000.0F ) * CLOCKS_PER_SEC;
printBoard(b);
printf("%d,%ld,%lfs\n", n, calls, delta_t);
}
for (i = 0; i < n; i++) {
free(b->matrix[i]);
b->matrix[i] = NULL;
}
free(b->matrix);
b->matrix = NULL;
free(b);
}
int main(int argc, char const *argv[]) {
system("clear");
int i;
int n = 0;
scanf("%d", &n);
printf("board size,calls,time\n");
for (i = 0; i <= n; i++) {
queens(i);
}
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,29 @@
board size,calls,time
0,0,0.000004s
1,1,0.000008s
4,26,0.000017s
5,15,0.000011s
6,171,0.000080s
7,42,0.000026s
8,876,0.000362s
9,333,0.000146s
10,975,0.000423s
11,517,0.000248s
12,3066,0.001410s
13,1365,0.000681s
14,26495,0.008047s
15,20280,0.003643s
16,160712,0.014465s
17,91222,0.007131s
18,743229,0.047648s
19,48184,0.003078s
20,3992510,0.253168s
21,179592,0.011663s
22,38217905,2.577556s
23,584591,0.039400s
24,9878316,0.710558s
25,1216775,0.089587s
26,10339849,0.815349s
27,12263400,0.956678s
28,84175966,6.601450s
29,44434525,3.591837s
1 board size calls time
2 0 0 0.000004s
3 1 1 0.000008s
4 4 26 0.000017s
5 5 15 0.000011s
6 6 171 0.000080s
7 7 42 0.000026s
8 8 876 0.000362s
9 9 333 0.000146s
10 10 975 0.000423s
11 11 517 0.000248s
12 12 3066 0.001410s
13 13 1365 0.000681s
14 14 26495 0.008047s
15 15 20280 0.003643s
16 16 160712 0.014465s
17 17 91222 0.007131s
18 18 743229 0.047648s
19 19 48184 0.003078s
20 20 3992510 0.253168s
21 21 179592 0.011663s
22 22 38217905 2.577556s
23 23 584591 0.039400s
24 24 9878316 0.710558s
25 25 1216775 0.089587s
26 26 10339849 0.815349s
27 27 12263400 0.956678s
28 28 84175966 6.601450s
29 29 44434525 3.591837s

View File

@@ -0,0 +1,34 @@
board size,calls,time
0,0,0.000001s
1,1,0.000000s
4,26,0.000005s
5,15,0.000004s
6,171,0.000026s
7,42,0.000009s
8,876,0.000101s
9,333,0.000046s
10,975,0.000117s
11,517,0.000064s
12,3066,0.000374s
13,1365,0.000181s
14,26495,0.002876s
15,20280,0.002252s
16,160712,0.017150s
17,91222,0.010383s
18,743229,0.084488s
19,48184,0.005725s
20,3992510,0.490726s
21,179592,0.023457s
22,38217905,5.092550s
23,584591,0.081210s
24,9878316,1.410959s
25,1216775,0.188140s
26,10339849,1.599553s
27,12263400,1.987257s
28,84175966,14.078644s
29,44434525,7.684626s
30,1692888135,298.843353s
31,408773285,74.617912s
32,-1495242192,526.441956s
33,323601164,893.228821s
1 board size calls time
2 0 0 0.000001s
3 1 1 0.000000s
4 4 26 0.000005s
5 5 15 0.000004s
6 6 171 0.000026s
7 7 42 0.000009s
8 8 876 0.000101s
9 9 333 0.000046s
10 10 975 0.000117s
11 11 517 0.000064s
12 12 3066 0.000374s
13 13 1365 0.000181s
14 14 26495 0.002876s
15 15 20280 0.002252s
16 16 160712 0.017150s
17 17 91222 0.010383s
18 18 743229 0.084488s
19 19 48184 0.005725s
20 20 3992510 0.490726s
21 21 179592 0.023457s
22 22 38217905 5.092550s
23 23 584591 0.081210s
24 24 9878316 1.410959s
25 25 1216775 0.188140s
26 26 10339849 1.599553s
27 27 12263400 1.987257s
28 28 84175966 14.078644s
29 29 44434525 7.684626s
30 30 1692888135 298.843353s
31 31 408773285 74.617912s
32 32 -1495242192 526.441956s
33 33 323601164 893.228821s

View File

@@ -0,0 +1 @@
30