added nqueens benchmark
This commit is contained in:
22
benchmarks/benchmarks/queens/Makefile
Normal file
22
benchmarks/benchmarks/queens/Makefile
Normal 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
|
||||
163
benchmarks/benchmarks/queens/README.md
Normal file
163
benchmarks/benchmarks/queens/README.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# The N-Queens 女王 Problem
|
||||
|
||||
The n-queens puzzle is the problem of placing n chess queens on an n x n 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.
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
>*Keep in mind this is not following proper flowchart rules, it was drawn just for quick reference before an exam*
|
||||
0
benchmarks/benchmarks/queens/analysis.txt
Normal file
0
benchmarks/benchmarks/queens/analysis.txt
Normal file
BIN
benchmarks/benchmarks/queens/nqueens
Executable file
BIN
benchmarks/benchmarks/queens/nqueens
Executable file
Binary file not shown.
162
benchmarks/benchmarks/queens/nqueens.c
Normal file
162
benchmarks/benchmarks/queens/nqueens.c
Normal 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;
|
||||
}
|
||||
BIN
benchmarks/benchmarks/queens/nqueens.o
Normal file
BIN
benchmarks/benchmarks/queens/nqueens.o
Normal file
Binary file not shown.
29
benchmarks/benchmarks/queens/performance.csv
Normal file
29
benchmarks/benchmarks/queens/performance.csv
Normal 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
|
||||
|
34
benchmarks/benchmarks/queens/performance2.csv
Normal file
34
benchmarks/benchmarks/queens/performance2.csv
Normal 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
benchmarks/benchmarks/queens/test.dat
Normal file
1
benchmarks/benchmarks/queens/test.dat
Normal file
@@ -0,0 +1 @@
|
||||
30
|
||||
Reference in New Issue
Block a user