ECF 1.5
nqueens.cpp
1/* nqueens.c
2 *
3 * (C) 2018 Eva Tuba <etuba@ieee.org> and Carlos M. Fonseca <cmfonsec@dei.uc.pt>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or (at
8 * your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include "nqueens.h"
24
25
26
27/**********************************/
28/* ----- Utility functions ----- */
29/**********************************/
30
31#if 1
32/*
33 * Random integer x such that 0 <= x <= n_max
34 * Status: FINAL
35 */
36static int randint(int n_max) {
37 int x;
38 if (n_max < 1)
39 return 0;
40 div_t y = div(-RAND_MAX-1, -n_max-1);
41 do
42 x = rand();
43 while (x > RAND_MAX + y.rem);
44 return x / y.quot;
45}
46#else
47#include <gsl/gsl_rng.h>
48extern gsl_rng *rng; /* The single rng instance used by the whole code */
49
50static int randint(int n_max) {
51 return gsl_rng_uniform_int(rng, n_max+1);
52}
53#endif
54
55/*
56 * Random permutation of size n
57 * Status: FINAL
58 * Notes:
59 * Based on https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_%22inside-out%22_algorithm
60 */
61static int *randperm(int *p, int n) {
62 /* Inside-out algorithm */
63 int i, j;
64 if (n > 0) {
65 p[0] = 0;
66 for (i = 1; i < n; i++) {
67 j = randint(i);
68 p[i] = p[j];
69 p[j] = i; /* = source[i] */
70 }
71 }
72 return p;
73}
74
75static void swap(int *data, int i, int j) {
76 if (i == j)
77 return;
78 int el = data[i];
79 data[i] = data[j];
80 data[j] = el;
81}
82
83/**********************************************/
84/* ----- Problem-specific instantiation ----- */
85/**********************************************/
86
87/*
88 * N-Queens instantiation
89 * Status: TENTATIVE
90 * Notes:
91 * Should just take an integer as an argument.
92 * Needs error checking
93 */
94#if 0
95struct problem *newProblem(const char *filename) {
96 struct problem *p = NULL;
97 FILE *infile;
98 int n=-1;
99 infile = fopen(filename, "r");
100 if (infile) {
101 fscanf(infile, "%d", &n);
102 fclose(infile);
103 if (n > 0) {
104 p = (struct problem*) malloc(sizeof (struct problem));
105 p->n = n;
106 } else
107 fprintf(stderr, "Invalid n-Queens instance %s\n", filename);
108 } else
109 fprintf(stderr, "Cannot open file %s\n", filename);
110 return p;
111}
112#else
113struct problem *newProblem(int n) {
114 struct problem *p = NULL;
115 if (n > 0) {
116 p = (struct problem*) malloc(sizeof (struct problem));
117 p->n = n;
118 } else
119 fprintf(stderr, "Invalid board size: %d\n", n);
120 return p;
121}
122#endif
123
124/*****************************/
125/* ----- API functions ----- */
126/*****************************/
127
128/* Memory management */
129
130/*
131 * Allocate memory for a solution
132 * Status: CHECK
133 */
134struct solution *allocSolution(struct problem *p) {
135 struct solution *s = (solution*) malloc(sizeof (struct solution));
136 s->prob = p;
137 s->data = (int*) malloc(p->n * sizeof (int));
138 s->olddata = (int*) malloc(p->n * sizeof (int));
139 s->mod = (int*) malloc(p->n * sizeof (int));
140 s->modi = (int*) malloc(p->n * sizeof (int));
141 s->n = p->n;
142 return s;
143}
144
145/*
146 * Allocate memory for a move
147 * Status: FINAL
148 */
149struct move *allocMove(struct problem *p) {
150 struct move *v = (move*) malloc(sizeof (struct move));
151 v->prob = p;
152 return v;
153}
154
155/*
156 * Allocate memory for a path
157 * Status: TENTATIVE
158 */
159struct pathState *allocPathState(struct problem *p) {
160 struct pathState *ps = (pathState*) malloc (sizeof (struct pathState));
161 ps->prob = p;
162 ps->data = (int*) malloc(p->n * sizeof (int));
163 ps->datai = (int*) malloc(p->n * sizeof (int));
164 ps->cycle = (int*) malloc(p->n * sizeof (int));
165 ps->pos = (int*) malloc(p->n * sizeof (int));
166 ps->n = p->n;
167 return ps;
168}
169
170/*
171 * Free the memory used by a problem
172 * Status: FINAL
173 */
174void freeProblem(struct problem *p) {
175 free(p);
176}
177
178/*
179 * Free the memory used by a solution
180 * Status: FINAL
181 */
182void freeSolution(struct solution *s) {
183 free(s->data);
184 free(s->olddata);
185 free(s->mod);
186 free(s->modi);
187 free(s);
188}
189
190/*
191 * Free the memory used by a move
192 * Status: FINAL
193 */
194void freeMove(struct move *v) {
195 free(v);
196}
197
198/*
199 * Free the memory used by a path
200 * Status: IN_PROGRESS
201 * Notes:
202 * update along with pathState
203 */
204void freePathState(struct pathState *ps) {
205 free(ps->data);
206 free(ps->datai);
207 free(ps->cycle);
208 free(ps->pos);
209 free(ps);
210}
211
212/* I/O */
213void printProblem(struct problem *p) {
214 printf("%d-Queens problem\n",p->n);
215}
216
217void printSolution(struct solution *s) {
218 int i;
219 printf("%d-Queens solution\n p:", s->n);
220 for(i = 0; i < s->n; i++)
221 printf(" %d", s->data[i]);
222#if 0
223 printf("\n mod :");
224 for(i = 0; i < s->n; i++)
225 printf(" %d", s->mod[i]);
226 printf("\n modi:");
227 for(i = 0; i < s->n; i++)
228 printf(" %d", s->modi[i]);
229#endif
230 printf("\n");
231}
232
233void printMove(struct move *v) {
234 printf("%d-Queens move: %d, %d\n", v->prob->n, v->data[0], v->data[1]);
235}
236
237void printPathState(struct pathState *ps) {
238 int i;
239 printf("%d-Queens path state\n", ps->n);
240 printf(" Path length: %d\n Permutation:", ps->n - ps->n_cycles);
241 for (i = 0; i < ps->n; i++)
242 printf(" %d",ps->data[i]);
243 printf("\n Inverse permutation:");
244 for (i = 0; i < ps->n; i++)
245 printf(" %d", ps->datai[i]);
246 printf("\n Cycles:");
247 for (i = 0; i < ps->n; i++)
248 printf(" %d", ps->cycle[i]);
249 printf("\n Number of different positions: %d\n Positions: ", ps->n_diff);
250 for (i = 0; i < ps->n_diff; i++)
251 printf("%d ",ps->pos[i]);
252 printf("\n");
253}
254
255/* Solution generation */
256
257/*
258 * Generate solutions uniformly at random
259 * Status: CHECK
260 */
261struct solution *randomSolution(struct solution *s) {
262 /* solution s must have been allocated with allocSolution() */
263 int i;
264 randperm(s->data, s->n);
265 /* Solution not evaluated yet */
266 for (i = 0; i < s->n; i++)
267 s->mod[i] = s->modi[i] = i;
268 s->nmod = s->n;
269 return s;
270}
271
272/* Solution inspection */
273
274/*
275 * Generate solutions uniformly at random
276 * Status: INTERIM
277 * Notes:
278 * Implements incremental evaluation for multiple moves
279 */
280double getObjectiveValue(struct solution *s) {
281 int i, j, n = s->n, att, *mod, nmod = s->nmod;
282 if (nmod > .28*n) { /* full evaluation threshold to be fine-tuned experimentally */
283 att = 0;
284 for (i = 0; i < n-1; i++)
285 for (j = i+1; j < n; j++)
286 att += (j - i == abs(s->data[i] - s->data[j]));
287 s->objvalue = att;
288 memcpy(s->olddata, s->data, n * sizeof (int));
289 s->nmod = 0;
290 } else if (nmod >= 1) { /* incremental evaluation */
291 att = s->objvalue;
292 mod = s->mod;
293 for (i = 0; i < nmod-1; i++)
294 for (j = i+1; j < nmod; j++)
295 att += (abs(mod[j] - mod[i]) == abs(s->data[mod[i]] - s->data[mod[j]])) -
296 (abs(mod[j] - mod[i]) == abs(s->olddata[mod[i]] - s->olddata[mod[j]]));
297 for (i = 0; i < nmod; i++)
298 for (j = nmod; j < n; j++)
299 att += (abs(mod[j] - mod[i]) == abs(s->data[mod[i]] - s->data[mod[j]])) -
300 (abs(mod[j] - mod[i]) == abs(s->olddata[mod[i]] - s->olddata[mod[j]]));
301 s->objvalue = att;
302 memcpy(s->olddata, s->data, n * sizeof (int));
303 s->nmod = 0;
304#if 0
305 /* for testing */
306 att = 0;
307 for (i = 0; i < n-1; i++)
308 for (j = i+1; j < n; j++)
309 if (j - i == abs(s->data[i] - s->data[j]))
310 att++;
311 printf("incremental = %d, full = %d, diff = %d\n", s->objvalue, att, s->objvalue-att);
312
313#endif
314 }
315 return (double)s->objvalue;
316}
317
318/* Operations on solutions*/
319struct solution *copySolution(struct solution *dest, const struct solution *src) {
320 dest->prob = src->prob;
321 dest->n = src->n;
322 memcpy(dest->data, src->data, src->n * sizeof (int));
323 memcpy(dest->olddata, src->olddata, src->n * sizeof (int));
324 memcpy(dest->mod, src->mod, src->n * sizeof (int));
325 memcpy(dest->modi, src->modi, src->n * sizeof (int));
326 dest->nmod = src->nmod;
327 dest->objvalue = src->objvalue;
328 return dest;
329}
330
331/*
332 * Apply a move to a solution
333 * Status: FINAL
334 */
335struct solution *applyMove(struct solution *s, const struct move *v) {
336 int i, j;
337 i = v->data[0];
338 j = v->data[1];
339 if (i == j) /* do nothing */
340 return s;
341 swap(s->data, i, j);
342 if (s->modi[i] >= s->nmod) {
343 swap(s->mod, s->modi[i], s->nmod);
344 swap(s->modi, i, s->mod[s->modi[i]]);
345 s->nmod++;
346 }
347 if (s->modi[j] >= s->nmod) {
348 swap(s->mod, s->modi[j], s->nmod);
349 swap(s->modi, j, s->mod[s->modi[j]]);
350 s->nmod++;
351 }
352 return s;
353}
354
355/* Move generation */
356
357/*
358 * Generate moves uniformly at random
359 * Status: TENTATIVE, NEEDS_TESTING
360 * Notes:
361 * Move (i,j) such that i != j. Order is irrelevant and not enforced.
362 */
363struct move *randomMove(struct move *v, const struct solution *s) {
364 /* move v must have been allocated with allocMove() */
365 int n;
366 n = s->n;
367 v->data[0] = randint(n-1);
368 v->data[1] = (v->data[0] + 1 + randint(n-2)) % n;
369 return v;
370}
371
372/*
373 * Generate the next random move in a path towards a solution
374 * Status: CHECK, NEEDS_WORK
375 * Notes:
376 * should compute moves by using cycles
377 */
378struct move *nextRandomMove(struct move *v, struct pathState *ps) {
379 int i, j, r, n = ps->n;
380 int *pos = ps->pos, *data = ps->data, *datai = ps->datai;
381
382 if (ps->n_cycles >= n) { /* end of path has been reached, no move is possible */
383 v->data[0] = v->data[1] = 0; /* null move */
384 return v;
385 }
386
387 /* Note: a swap move may correct two positions at once, but we only check
388 one at a time. Therefore, we may need to skip null moves here, and try
389 again. */
390 do {
391 /* Draw at random a position to correct */
392 r = randint(--ps->n_diff);
393 i = v->data[0] = pos[r]; /* choose an element that is not correct */
394 /* Find where correct one is */
395 j = datai[i];
396 pos[r] = pos[ps->n_diff];
397 } while (i == j);
398 v->data[1] = j;
399
400 /* Update pathState */
401 ps->n_cycles++;
402 swap(ps->datai, i, data[i]);
403 swap(ps->data, i, j);
404 return v;
405}
406
407/* Path generation */
408
409/*
410 * Set up a path from one solution to another
411 * Status: CHECK
412 * Notes:
413 * find and save cycles of permutation p2i[p1]
414 */
415struct pathState *initPathTo(struct pathState *ps, const struct solution *s1, const struct solution *s2) {
416 int i, j, n = ps->n;
417
418 /* Compute reference permutation p2i[p1] using ps->pos as a buffer */
419 for (i = 0; i < n; i++)
420 ps->pos[s2->data[i]] = i;
421 for (i = 0; i < n; i++)
422 ps->data[i] = ps->pos[s1->data[i]];
423 /* Inverse of reference permutation */
424 for (i = 0; i < n; i++)
425 ps->datai[ps->data[i]] = i;
426
427 /* Compute the cycles in reference permutation p2i[p1] */
428 ps->n_cycles = 0;
429 for (i = 0; i < n; i++)
430 ps->cycle[i] = 0; /* all elements are unchecked */
431 for (i = 0; i < n; i++) {
432 if (ps->cycle[i] == 0) {
433 ps->n_cycles++;
434 j = i;
435 do {
436 ps->cycle[j] = ps->n_cycles;
437 j = ps->data[j];
438 } while (j != i);
439 }
440 }
441
442 /* Find elements of data that differ from their position */
443 for (i = 0, j = 0; i < n; i++)
444 if (ps->data[i] != i)
445 ps->pos[j++] = i;
446 ps->n_diff = j;
447 return ps;
448}
449
450/*
451 * Set up a path away from a solution
452 * Status: NOT IMPLEMENTED
453 * Notes:
454 */
455struct pathState *initPathAwayFrom(struct pathState *ps, const struct solution *s) {
456 return NULL;
457}
458
459/* Path inspection */
460
461/*
462 * Current length of path
463 * Status: FINAL
464 */
465int getPathLength(const struct pathState *ps) {
466 return ps->n - ps->n_cycles;
467}
Definition: flowshop.h:78