Jeff Somers's N Queens Solutions 最快的n皇后算法

  1 /*  Jeff Somers
  2 *
  3 *  Copyright (c) 2002
  4 *
  5 *  [email protected]
  6 *  or
  7 *  [email protected]
  8 *
  9 *  April, 2002
 10 * 
 11 *  Program:  nq
 12 * 
 13 *  Program to find number of solutions to the N queens problem.
 14 *  This program assumes a twos complement architecture.
 15 *
 16 *  For example, you can arrange 4 queens on 4 x 4 chess so that
 17 *  none of the queens can attack each other:
 18 *
 19 *  Two solutions:
 20 *     _ Q _ _        _ _ Q _
 21 *     _ _ _ Q        Q _ _ _
 22 *     Q _ _ _        _ _ _ Q
 23 *     _ _ Q _    and _ Q _ _
 24 *   
 25 *  Note that these are separate solutions, even though they
 26 *  are mirror images of each other.
 27 *
 28 *  Likewise, a 8 x 8 chess board has 92 solutions to the 8 queens
 29 *  problem.
 30 *
 31 *  Command Line Usage:
 32 *
 33 *          nq N
 34 *
 35 *       where N is the size of the N x N board.  For example,
 36 *       nq 4 will find the 4 queen solution for the 4 x 4 chess
 37 *       board.
 38 *
 39 *  By default, this program will only print the number of solutions,
 40 *  not board arrangements which are the solutions.  To print the
 41 *  boards, uncomment the call to printtable in the Nqueen function.
 42 *  Note that printing the board arrangements slows down the program
 43 *  quite a bit, unless you pipe the output to a text file:
 44 *
 45 *  nq 10 > output.txt
 46 *
 47 *
 48 *  The number of solutions for the N queens problems are known for
 49 *  boards up to 23 x 23.  With this program, I‘ve calculated the
 50 *  results for boards up to 21 x 21, and that took over a week on
 51 *  an 800 MHz PC.  The algorithm is approximated O(n!) (i.e. slow),
 52 *  and calculating the results for a 22 x 22 board will take about 8.5
 53 *  times the amount of time for the 21 x 21 board, or over 8 1/2 weeks.
 54 *  Even with a 10 GHz machine, calculating the results for a 23 x 23
 55 *  board would take over a month.  Of course, setting up a cluster of
 56 *  machines (or a distributed client) would do the work in less time.
 57 * 
 58 *  (from Sloane‘s On-Line Encyclopedia of Integer Sequences,
 59 *   Sequence A000170
 60 *   http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=000170
 61 *   )
 62 *
 63 *   Board Size:       Number of Solutions to          Time to calculate
 64 *   (length of one        N queens problem:              on 800MHz PC
 65 *    side of N x N                                    (Hours:Mins:Secs)
 66 *    chessboard)
 67 *
 68 *     1                                  1                    n/a
 69 *     2                                  0                   < 0 seconds
 70 *     3                                  0                   < 0 seconds
 71 *     4                                  2                   < 0 seconds
 72 *     5                                 10                   < 0 seconds
 73 *     6                                  4                   < 0 seconds
 74 *     7                                 40                   < 0 seconds
 75 *     8                                 92                   < 0 seconds
 76 *     9                                352                   < 0 seconds
 77 *    10                                724                   < 0 seconds
 78 *    11                               2680                   < 0 seconds
 79 *    12                              14200                   < 0 seconds
 80 *    13                              73712                   < 0 seconds
 81 *    14                             365596                  00:00:01
 82 *    15                            2279184                  00:00:04
 83 *    16                           14772512                  00:00:23
 84 *    17                           95815104                  00:02:38
 85 *    18                          666090624                  00:19:26
 86 *    19                         4968057848                  02:31:24
 87 *    20                        39029188884                  20:35:06
 88 *    21                       314666222712                 174:53:45
 89 *    22                      2691008701644                     ?
 90 *    23                     24233937684440                     ?
 91 *    24                                  ?                     ?
 92 */
 93 
 94 #include <stdio.h>
 95 #include <stdlib.h>
 96 #include <time.h>
 97 
 98 /*
 99 Notes on MAX_BOARDSIZE:
100 
101 A 32 bit unsigned long is sufficient to hold the results for an 18 x 18
102 board (666090624 solutions) but not for a 19 x 19 board (4968057848 solutions).
103 
104 In Win32, I use a 64 bit variable to hold the results, and merely set the
105 MAX_BOARDSIZE to 21 because that‘s the largest board for which I‘ve
106 calculated a result.
107 
108 Note: a 20x20 board will take over 20 hours to run on a Pentium III 800MHz,
109 while a 21x21 board will take over a week to run on the same PC.
110 
111 On Unix, you could probably change the type of g_numsolutions from unsigned long
112 to unsigned long long, or change the code to use two 32 bit ints to store the
113 results for board sizes 19 x 19 and up.
114 */
115 
116 #ifdef WIN32
117 
118 #define MAX_BOARDSIZE 21
119 typedef unsigned __int64 SOLUTIONTYPE;
120 
121 #else
122 
123 #define MAX_BOARDSIZE 18
124 typedef unsigned long SOLUTIONTYPE;
125 
126 #endif
127 
128 #define MIN_BOARDSIZE 2
129 
130 SOLUTIONTYPE g_numsolutions = 0;
131 
132 
133 /* Print a chess table with queens positioned for a solution */
134 /* This is not a critical path function & I didn‘t try to optimize it. */
135 void printtable(int boardsize, int* aQueenBitRes, SOLUTIONTYPE numSolution)
136 {
137     int i, j, k, row;
138 
139     /*  We only calculated half the solutions, because we can derive
140     the other half by reflecting the solution across the "Y axis". */
141     for (k = 0; k < 2; ++k)
142     {
143 #ifdef WIN32
144         printf("*** Solution #: %I64d ***\n", 2 * numSolution + k - 1);
145 #else
146         printf("*** Solution #: %d ***\n", 2 * numSolution + k - 1);
147 #endif
148         for ( i = 0; i < boardsize; i++)
149         {
150             unsigned int bitf;
151             /*
152             Get the column that was set (i.e. find the
153             first, least significant, bit set).
154             If aQueenBitRes[i] = 011010b, then
155             bitf = 000010b
156             */
157             bitf = aQueenBitRes[i];
158 
159             row = bitf ^ (bitf & (bitf - 1)); /* get least significant bit */
160             for ( j = 0; j < boardsize; j++)
161             {
162                 /* keep shifting row over to the right until we find the one ‘1‘ in
163                 the binary representation.  There will only be one ‘1‘. */
164                 if (0 == k && ((row >> j) & 1))
165                 {
166                     printf("Q ");
167                 }
168                 else if (1 == k && (row & (1 << (boardsize - j - 1)))) /* this is the board reflected across the "Y axis" */
169                 {
170                     printf("Q ");
171                 }
172                 else
173                 {
174                     printf(". ");
175                 }
176             }
177             printf("\n");
178         }
179         printf("\n");
180     }
181 }
182 
183 
184 /* The function which calculates the N queen solutions.
185 We calculate one-half the solutions, then flip the results over
186 the "Y axis" of the board.  Every solution can be reflected that
187 way to generate another unique solution (assuming the board size
188 isn‘t 1 x 1).  That‘s because a solution cannot be symmetrical
189 across the Y-axis (because you can‘t have two queens in the same
190 horizontal row).  A solution also cannot consist of queens
191 down the middle column of a board with an odd number of columns,
192 since you can‘t have two queens in the same vertical row.
193 
194 This is a backtracking algorithm.  We place a queen in the top
195 row, then note the column and diagonals it occupies.  We then
196 place a queen in the next row down, taking care not to place it
197 in the same column or diagonal.  We then update the occupied
198 columns & diagonals & move on to the next row.  If no position
199 is open in the next row, we back track to the previous row & move
200 the queen over to the next available spot in its row & the process
201 starts over again.
202 */
203 void Nqueen(int board_size)
204 {
205     int aQueenBitRes[MAX_BOARDSIZE]; /* results */
206     int aQueenBitCol[MAX_BOARDSIZE]; /* marks colummns which already have queens */
207     int aQueenBitPosDiag[MAX_BOARDSIZE]; /* marks "positive diagonals" which already have queens */
208     int aQueenBitNegDiag[MAX_BOARDSIZE]; /* marks "negative diagonals" which already have queens */
209     int aStack[MAX_BOARDSIZE + 2]; /* we use a stack instead of recursion */
210     register int* pnStack;
211 
212 
213     register int numrows = 0; /* numrows redundant - could use stack */
214     register unsigned int lsb; /* least significant bit */
215     register unsigned int bitfield; /* bits which are set mark possible positions for a queen */
216     int i;
217     int odd = board_size & 1; /* 0 if board_size even, 1 if odd */
218     int board_minus = board_size - 1; /* board size - 1 */
219     int mask = (1 << board_size) - 1; /* if board size is N, mask consists of N 1‘s */
220 
221 
222     /* Initialize stack */
223     aStack[0] = -1; /* set sentinel -- signifies end of stack */
224 
225     /* NOTE: (board_size & 1) is true iff board_size is odd */
226     /* We need to loop through 2x if board_size is odd */
227     for (i = 0; i < (1 + odd); ++i)
228     {
229         /* We don‘t have to optimize this part; it ain‘t the
230         critical loop */
231         bitfield = 0;
232         if (0 == i)
233         {
234             /* Handle half of the board, except the middle
235             column. So if the board is 5 x 5, the first
236             row will be: 00011, since we‘re not worrying
237             about placing a queen in the center column (yet).
238             */
239             int half = board_size>>1; /* divide by two */
240             /* fill in rightmost 1‘s in bitfield for half of board_size
241             If board_size is 7, half of that is 3 (we‘re discarding the remainder)
242             and bitfield will be set to 111 in binary. */
243             bitfield = (1 << half) - 1;
244             pnStack = aStack + 1; /* stack pointer */
245 
246             aQueenBitRes[0] = 0;
247             aQueenBitCol[0] = aQueenBitPosDiag[0] = aQueenBitNegDiag[0] = 0;
248         }
249         else
250         {
251             /* Handle the middle column (of a odd-sized board).
252             Set middle column bit to 1, then set
253             half of next row.
254             So we‘re processing first row (one element) & half of next.
255             So if the board is 5 x 5, the first row will be: 00100, and
256             the next row will be 00011.
257             */
258             bitfield = 1 << (board_size >> 1);
259             numrows = 1; /* prob. already 0 */
260 
261             /* The first row just has one queen (in the middle column).*/
262             aQueenBitRes[0] = bitfield;
263             aQueenBitCol[0] = aQueenBitPosDiag[0] = aQueenBitNegDiag[0] = 0;
264             aQueenBitCol[1] = bitfield;
265 
266             /* Now do the next row.  Only set bits in half of it, because we‘ll
267             flip the results over the "Y-axis".  */
268             aQueenBitNegDiag[1] = (bitfield >> 1);
269             aQueenBitPosDiag[1] = (bitfield << 1);
270             pnStack = aStack + 1; /* stack pointer */
271             *pnStack++ = 0; /* we‘re done w/ this row -- only 1 element & we‘ve done it */
272             bitfield = (bitfield - 1) >> 1; /* bitfield -1 is all 1‘s to the left of the single 1 */
273         }
274 
275         /* this is the critical loop */
276         for (;;)
277         {
278             /* could use
279             lsb = bitfield ^ (bitfield & (bitfield -1));
280             to get first (least sig) "1" bit, but that‘s slower. */
281             lsb = -((signed)bitfield) & bitfield; /* this assumes a 2‘s complement architecture */
282             if (0 == bitfield)
283             {
284                 bitfield = *--pnStack; /* get prev. bitfield from stack */
285                 if (pnStack == aStack) { /* if sentinel hit.... */
286                     break ;
287                 }
288                 --numrows;
289                 continue;
290             }
291             bitfield &= ~lsb; /* toggle off this bit so we don‘t try it again */
292 
293             aQueenBitRes[numrows] = lsb; /* save the result */
294             if (numrows < board_minus) /* we still have more rows to process? */
295             {
296                 int n = numrows++;
297                 aQueenBitCol[numrows] = aQueenBitCol[n] | lsb;
298                 aQueenBitNegDiag[numrows] = (aQueenBitNegDiag[n] | lsb) >> 1;
299                 aQueenBitPosDiag[numrows] = (aQueenBitPosDiag[n] | lsb) << 1;
300                 *pnStack++ = bitfield;
301                 /* We can‘t consider positions for the queen which are in the same
302                 column, same positive diagonal, or same negative diagonal as another
303                 queen already on the board. */
304                 bitfield = mask & ~(aQueenBitCol[numrows] | aQueenBitNegDiag[numrows] | aQueenBitPosDiag[numrows]);
305                 continue;
306             }
307             else
308             {
309                 /* We have no more rows to process; we found a solution. */
310                 /* Comment out the call to printtable in order to print the solutions as board position*/
311                 /* printtable(board_size, aQueenBitRes, g_numsolutions + 1);  */
312                 ++g_numsolutions;
313                 bitfield = *--pnStack;
314                 --numrows;
315                 continue;
316             }
317         }
318     }
319 
320     /* multiply solutions by two, to count mirror images */
321     g_numsolutions *= 2;
322 }
323 
324 /* Print the results at the end of the run */
325 void printResults(time_t* pt1, time_t* pt2)
326 {
327     double secs;
328     int hours , mins, intsecs;
329 
330     printf("End: \t%s", ctime(pt2));
331     secs = difftime(*pt2, *pt1);
332     intsecs = (int)secs;
333     printf("Calculations took %d second%s.\n", intsecs, (intsecs == 1 ? "" : "s"));
334 
335     /* Print hours, minutes, seconds */
336     hours = intsecs/3600;
337     intsecs -= hours * 3600;
338     mins = intsecs/60;
339     intsecs -= mins * 60;
340     if (hours > 0 || mins > 0)
341     {
342         printf("Equals ");
343         if (hours > 0)
344         {
345             printf("%d hour%s, ", hours, (hours == 1) ? "" : "s");
346         }
347         if (mins > 0)
348         {          
349             printf("%d minute%s and ", mins, (mins == 1) ? "" : "s");
350         }
351         printf("%d second%s.\n", intsecs, (intsecs == 1 ? "" : "s"));
352 
353     }
354 }
355 
356 /* main routine for N Queens program.*/
357 int main(int argc, char** argv)
358 {
359     time_t t1, t2;
360     int boardsize;
361 
362     if (argc != 2) {
363         printf("N Queens program by Jeff Somers.\n");
364         printf("\[email protected] or [email protected]\n");
365         printf("This program calculates the total number of solutions to the N Queens problem.\n");
366         printf("Usage: nq <width of board>\n"); /* user must pass in size of board */
367         return 0;
368     }
369 
370     boardsize = atoi(argv[1]);
371 
372     /* check size of board is within correct range */
373     if (MIN_BOARDSIZE > boardsize || MAX_BOARDSIZE < boardsize)
374     {
375         printf("Width of board must be between %d and %d, inclusive.\n",
376             MIN_BOARDSIZE, MAX_BOARDSIZE );
377         return 0;
378     }
379 
380     time(&t1);
381     printf("N Queens program by Jeff Somers.\n");
382     printf("\[email protected] or [email protected]\n");
383     printf("Start: \t %s", ctime(&t1));
384 
385     Nqueen(boardsize); /* find solutions */
386     time(&t2);
387 
388     printResults(&t1, &t2);
389 
390     if (g_numsolutions != 0)
391     {
392 #ifdef WIN32
393         printf("For board size %d, %I64d solution%s found.\n", boardsize, g_numsolutions, (g_numsolutions == 1 ? "" : "s"));
394 #else
395         printf("For board size %d, %d solution%s found.\n", boardsize, g_numsolutions, (g_numsolutions == 1 ? "" : "s"));
396 #endif
397     }
398     else
399     {
400         printf("No solutions found.\n");
401     }
402 
403     return 0;
404 }

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。