[llvm-commits] CVS: llvm-test/SingleSource/Benchmarks/Misc/queens.c
Chris Lattner
lattner at cs.uiuc.edu
Tue Oct 5 13:08:38 PDT 2004
Changes in directory llvm-test/SingleSource/Benchmarks/Misc:
queens.c added (r1.1)
---
Log message:
New testcase
---
Diffs of the changes: (+366 -0)
Index: llvm-test/SingleSource/Benchmarks/Misc/queens.c
diff -c /dev/null llvm-test/SingleSource/Benchmarks/Misc/queens.c:1.1
*** /dev/null Tue Oct 5 15:08:37 2004
--- llvm-test/SingleSource/Benchmarks/Misc/queens.c Tue Oct 5 15:08:27 2004
***************
*** 0 ****
--- 1,366 ----
+ /*
+ ** queens.c -- Find solutions to the Eight-Queens chess problem.
+ ** Roberto Sierra 3/19/84 Version 1.1
+ **
+ ** Description:
+ ** This program finds all the possible ways that N queens can
+ ** be placed on an NxN chessboard so that the queens cannot
+ ** capture one another -- that is, so that no rank, file or
+ ** diagonal is occupied by more than one queen. By default,
+ ** the program prints the first solution it finds. You can
+ ** use the -a option to print all solutions, or the -c option
+ ** just to count them. The program allows the chess board
+ ** to be from 1x1 (trivial case) to 100x100. Warning: the
+ ** larger the chess board, the longer it typically takes to
+ ** find each solution, even though there may be more of them.
+ **
+ ** This is a terrific example of the utility of recursion. The
+ ** algorithm uses recursion to drastically limit the number
+ ** of board positions that are tested. The program is able
+ ** to find all 8x8 queen solutions in a fraction of a second
+ ** (not counting print time). The code makes no attempt to
+ ** eliminate symmetrical solutions, so the number of solutions
+ ** reported will always be higher than the actual number of
+ ** distinct solutions.
+ **
+ **
+ ** Usage:
+ ** queens [-ac] n
+ **
+ ** n Number of queens (rows and columns). An integer from 1 to 100.
+ ** -a Find and print all solutions.
+ ** -c Count all solutions, but do not print them.
+ **
+ ** The output is sent to stdout. All errors messages are
+ ** sent to stderr. If a problem arises, the return code is -1.
+ **
+ **
+ ** Examples:
+ **
+ ** queens 8 ## Show an 8x8 solution
+ ** 8 queens on a 8x8 board...
+ ** Q - - - - - - -
+ ** - - - - Q - - -
+ ** - - - - - - - Q
+ ** - - - - - Q - -
+ ** - - Q - - - - -
+ ** - - - - - - Q -
+ ** - Q - - - - - -
+ ** - - - Q - - - -
+ **
+ ** queens -c 8 ## Count all 8x8 solutions
+ ** 8 queens on a 8x8 board...
+ ** ...there are 92 solutions.
+ **
+ ** queens -a 4 ## Show all 4x4 solutions
+ ** 4 queens on a 4x4 board...
+ **
+ ** Solution #1:
+ ** - Q - -
+ ** - - - Q
+ ** Q - - -
+ ** - - Q -
+ **
+ ** Solution #2:
+ ** - - Q -
+ ** Q - - -
+ ** - - - Q
+ ** - Q - -
+ **
+ ** ...there are 2 solutions.
+ **
+ **
+ ** Build Instructions:
+ ** You'll need an ANSI C compiler (or the willingness to edit
+ ** the program a bit). If you've got Gnu C, then you can
+ ** compile and load the program as follows:
+ **
+ ** gcc queens.c -ansi -o queens
+ **
+ ** [If you're using MPW on the Mac, define '-d MPW' on the
+ ** compile line so that background processing will occur.]
+ **
+ **
+ ** Algorithm:
+ ** In a 1984 Byte article, I ran across an interesting letter
+ ** from a high school student who was attempting to solve the
+ ** Eight Queens problem using a BASIC interpreter. He had
+ ** developed a program which placed eight queens successively
+ ** on all sixty-four squares, testing for conflicts at each
+ ** iteration. Of course, such a program would require 64^8
+ ** iterations (about 2.8x10^14 iterations). Even in C on a,
+ ** fast CPU, this could take months or years. Byte's answer was
+ ** to alter the loops so that the queens resided on separate
+ ** ranks, thereby reducing the number of iterations required
+ ** to find all solutions to 8^8 iterations (about 16 million).
+ ** More reasonable, but still requiring a chunk of CPU time.
+ **
+ ** I puzzled about this problem a bit, and came to realize that
+ ** this was still wasting a lot of CPU cycles. Though I'm sure
+ ** others have come up with good algorithms, I decided to come
+ ** up with my own, with a particular eye on efficiency. The
+ ** resulting algorithm finds all 8x8 solutions in a fraction
+ ** of a second (there are 92 solutions, including rotations).
+ ** On a Sun 4, it'll find all 365,596 solutions on a 14x14 board
+ ** in a bit over 2 minutes (printing them out requires extra
+ ** time, of course). Even Byte's solution would require 14^14
+ ** iterations (about 10^16) which would take aeons.
+ **
+ ** My algorithm works as follows:
+ ** (1) Place a queen in the top left corner.
+ ** (2) Place another queen immediately below.
+ ** (3) Test for conflicts. If the second queen conflicts (it
+ ** does at first), then move it one square to the right.
+ ** (4) Loop step 3 until there are no conflicts. Place
+ ** the next queen on the board and recurse.
+ ** (5) If any queen reaches the right edge of the board,
+ ** remove it and 'pop' to the previous recursion level.
+ ** (6) Now repeat these steps recursively until all eight
+ ** queens (or however many) have been placed without
+ ** conflict -- the result is a solution to the problem,
+ ** which is counted and optionally printed.
+ **
+ ** Because conflicts are tested as the recursion proceeds,
+ ** this has the effect of 'pruning' the recursion so that
+ ** a large number of board positions are not even attempted.
+ ** The result is that the algorithm runs in reasonable time.
+ **
+ ** I used a few tricks to make the test-for-conflict code
+ ** extremely efficient -- there is no 'inner' loop to search
+ ** along ranks, files, or diagonals. A series of arrays are
+ ** maintained instead which indicate which queen currently
+ ** 'owns' each rank, file or diagonal. This makes the
+ ** algorithm really fly, though the code is a little hard to
+ ** read. Lastly, pointer arithmetic is used to reduce the
+ ** number of implicit multiplications used in array addressing.
+ **
+ **
+ ** Contact:
+ ** For queries regarding this program, contact Roberto Sierra
+ ** at any of the following addresses:
+ **
+ ** Roberto Sierra
+ ** bert at netcom.com (preferred address)
+ ** 73557.2101 at compuserve.com
+ **
+ ** Tempered MicroDesigns
+ ** P.O. Box 170638
+ ** San Francisco, CA 94117
+ **
+ **
+ ** Fine Print:
+ ** This program is in the public domain and can be used for
+ ** any purpose whatsoever, including commercial application.
+ ** [I'd like to hear what you do with it, though.]
+ ** Absolutely no warranty or liability is implied or extended
+ ** by the author.
+ **
+ **
+ ** Modification History:
+ ** PRS 3/19/84 v1.0 -- Original version.
+ ** PRS 7/25/93 v1.1 -- ANSIfied the code. More efficient pointers.
+ */
+
+
+ #include <stdio.h> /* Need standard I/O functions */
+ #include <stdlib.h> /* Need exit() routine interface */
+ #include <string.h> /* Need strcmp() interface */
+ #ifdef MPW /* Macintosh MPW ONLY */
+ # include <CursorCtl.h> /* Need cursor control interfaces */
+ #endif
+
+ #define MAXQUEENS 100 /* Max number of queens */
+ #define MAXRANKS MAXQUEENS /* Max number of ranks (rows) */
+ #define MAXFILES MAXQUEENS /* Max number of files (columns) */
+ #define MAXDIAGS (MAXRANKS+MAXFILES-1) /* Max number of diagonals */
+ #define EMPTY (MAXQUEENS+1) /* Marks unoccupied file or diagonal */
+
+ /* GLOBAL VARIABLES */
+
+ int queens; /* Number of queens to place */
+ int ranks; /* Number of ranks (rows) */
+ int files; /* Number of files (columns) */
+ int printing = 1; /* TRUE if printing positions */
+ int findall = 0; /* TRUE if finding all solutions */
+
+ unsigned long solutions = 0; /* Number of solutions found */
+ int queen[MAXRANKS]; /* File on which each queen is located */
+ int file[MAXFILES]; /* Which queen 'owns' each file */
+ int fordiag[MAXDIAGS]; /* Which queen 'owns' forward diagonals */
+ int bakdiag[MAXDIAGS]; /* Which queen 'owns' reverse diagonals */
+ char *progname = NULL; /* The name of this program */
+
+
+ /* -------------------------- PROTOTYPES ----------------------- */
+
+ void pboard(void);
+ void find(register int level);
+
+
+ /*-------------------------- main() ----------------------------
+ ** MAIN program. The main purpose of this routine is to deal
+ ** with decoding the command line arguments, initializing the
+ ** various arrays, and starting the recursive search routine.
+ */
+ void main(int argc, char **argv)
+ {
+ register int i; /* Loop variable */
+ register char *p; /* Ptr to argument */
+ char *usage =
+ "Usage: %s [-ac] n\n\
+ \tn\tNumber of queens (rows and columns). An integer from 1 to 100.\n\
+ \t-a\tFind and print all solutions.\n\
+ \t-c\tCount all solutions, but do not print them.\n";
+
+ #ifdef MPW /* Macintosh MPW ONLY */
+ InitCursorCtl(0); /* Enable cursor control */
+ #endif
+
+ progname = argv[0]; /* Name of the program */
+
+ /**** DECODE COMMAND LINE ARGUMENTS ****/
+ printing = 0;
+ queens = 14;
+ findall = 1;
+
+ for(i = 1; i < argc; ++i) { /* Scan through arguments */
+ p = argv[i]; /* Ptr to base of argument */
+ if(*p == '-') { /* Command line option? */
+ while(*++p) { /* Loop through characters */
+ switch(*p) { /* What is the character */
+ case 'c': /* '-c' option */
+ printing = 0; /* Counting, not printing */
+ case 'a': /* '-a' option */
+ findall = 1; /* Find all solutions */
+ break;
+ default: /* Illegal option */
+ fprintf(stderr,"%s: Illegal option '%s'\n",progname,argv[i]);
+ fprintf(stderr,usage,progname);
+ exit(-1);
+ } /* End of switch */
+ } /* End of loop */
+ } /* End of option test */
+ else {
+ if(sscanf(p,"%d",&queens) != 1) { /* Read integer argument */
+ fprintf(stderr,"%s: Non-integer argument '%s'\n",progname,p);
+ exit(-1);
+ }
+ if(queens <= 0) { /* N must be positive */
+ fprintf(stderr,"%s: n must be positive integer\n",progname);
+ exit(-1);
+ }
+ if(queens > MAXQUEENS) { /* N can't be too large */
+ fprintf(stderr,"%s: Can't have more than %d queens\n",
+ progname, MAXQUEENS);
+ exit(-1);
+ }
+ } /* End of argument test */
+ } /* End of argument scan loop */
+ if(!queens) {
+ fprintf(stderr,"%s: Missing n argument\n",progname);
+ fprintf(stderr,usage,progname);
+ exit(-1);
+ }
+
+ ranks = files = queens; /* NxN board for N queens */
+ printf("%d queen%s on a %dx%d board...\n",
+ queens, queens > 1 ? "s" : "", ranks, files);
+ fflush(stdout);
+
+ /* Initialization */
+ solutions = 0; /* No solutions yet */
+ for(i = 0; i < MAXFILES; ++i) file[i] = EMPTY;
+ for(i = 0; i < MAXDIAGS; ++i) fordiag[i] = bakdiag[i] = EMPTY;
+
+ /* Find all solutions (begin recursion) */
+ find(0);
+ if(printing && solutions) putchar('\n');
+
+ /* Report results */
+ if(solutions == 1) printf("...there is 1 solution\n");
+ else printf("...there are %ld solutions\n", solutions);
+
+ exit(0); /* No errors */
+ }
+
+
+ /***********************/
+ /**** ROUTINES ****/
+ /***********************/
+
+ /*------------------------- pboard() ---------------------------
+ ** This routines prints the board for a particular solution.
+ ** The output is sent to stdout.
+ */
+ void pboard(void)
+ {
+ register int i, j; /* Rank/File indices */
+
+ if(findall) /* Only if searching for all */
+ printf("\nSolution #%lu:\n",solutions); /* Print solution number */
+
+ for(i = 0; i < ranks; ++i) { /* Loop through all ranks */
+ for(j = 0; j < files; ++j) { /* Loop through all files */
+ putchar(' '); /* Output a space */
+ if(j == queen[i]) putchar('Q'); /* Output Q for queen... */
+ else putchar('-'); /* or '-' if empty */
+ }
+ putchar('\n'); /* Break line */
+ }
+ fflush(stdout); /* Flush solution to output */
+ }
+
+
+ /*-------------------------- find() ----------------------------
+ ** FIND is the recursive heart of the program, and finds all
+ ** solutions given a set of level-1 fixed queen positions.
+ ** The routine moves a single queen through all files (columns)
+ ** at the current rank (recursion level). As the queen is moved,
+ ** conflict tests are made. If the queen can be placed without
+ ** conflict, then the routine recurses to the next level. When
+ ** all queens have been placed without conflict, a solution is
+ ** counted and reported.
+ */
+ void find(register int level)
+ {
+ register int f; /* Indexes through files */
+ register int *fp, *fdp, *bdp; /* Ptrs to file/diagonal entries */
+
+ #ifdef MPW /* Macintosh MPW ONLY */
+ if(level & 7 == 0) /* Periodically break for... */
+ SpinCursor(1); /* background processing */
+ #endif
+
+ if(level == queens) { /* Placed all queens? Stop. */
+ ++solutions; /* This is a solution! */
+ if(printing) pboard(); /* Print board if printing */
+ if(!findall) exit(0); /* May stop after first solution */
+ #ifdef MPW /* Macintosh MPW ONLY */
+ SpinCursor(1); /* background processing */
+ #endif
+ }
+ else { /* Not at final level yet */
+ for( /* Move queen through all files */
+ f = 0, /* Queen starts at left (file 0) */
+ fp = file, /* Ptr to base of file array */
+ fdp = &fordiag[level], /* Ptr to first fwd diag entry */
+ bdp = &bakdiag[level+files-1] /* Ptr to first bak diag entry */
+ ;
+ f < files /* Loop through all files */
+ ;
+ ++f, /* Advance index */
+ ++fp, ++fdp, --bdp /* Advance pointers */
+ ) {
+ if(*fp >= level && /* No queen on the file? */
+ *fdp >= level && *bdp >= level /* No queens on diagonals? */
+ ) {
+ queen[level] = f; /* Note new position of queen */
+ *fp = *fdp = *bdp = level; /* Place queen on file & diags */
+ find(level+1); /* This level OK, recurse to next */
+ *fp = *fdp = *bdp = EMPTY; /* Remove queen from file & diags */
+ } /* End of conflict test */
+ } /* End of file loop */
+ } /* End if (level == queens) */
+ }
+
+
More information about the llvm-commits
mailing list