2

I am reading a description to a solution for the N-Queens puzzle on SICP and I cannot understand most of them. Here is the solution:

One way to solve the puzzle is to work across the board, placing a queen in each column. Once we have placed k - 1 queens, we must place the kth queen in a position where it does not check any of the queens already on the board. We can formulate this approach recursively: Assume that we have already generated the sequence of all possible ways to place k - 1 queens in the first k - 1 columns of the board. For each of these ways, generate an extended set of positions by placing a queen in each row of the kth column. Now filter these, keeping only the positions for which the queen in the kth column is safe with respect to the other queens. This produces the sequence of all ways to place k queens in the first k columns. By continuing this process, we will produce not only one solution, but all solutions to the puzzle.

Suppose that an 8 by 8 chessboard looks like this: My eyes are destroyed so I cannot use pictures. 0 means no queen, 1 means queen.

0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

work across the board, placing a queen in each column.

My understanding is columns are read vertically and rows are read horizontally. Does the text mean something like this?

1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0
0 0 0 1 0 0 0 0
0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0

I have placed a queen in each column but no rows are specified, but since this is done recursively I assume I already generated the ways of positions where two queens are not in check from each other.

Assume that we have already generated the sequence of all possible ways to place k - 1 queens in the first k - 1 columns of the board.

Say k = 1. So 1-1 = column 0 which have one way of generating the positions because it's an empty board.

For each of these ways, generate an extended set of positions by placing a queen in each row of the kth column.

My solution to column 0 is 1 way, but I absolutely have no idea what following means.

generate an extended set of positions by placing a queen in each row of the kth column.

What does "generate an extended set of positions" and placing a queen in each row of the column mean? Is it like this if k = 1?

1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0

But then all of the queens aren't safe because they all are in the same columns right?

I am totally lost on how to proceed. Can someone explain this to me?

Note: If you would like to give a visual explanation, please also provide a textual explanation because I can't see images and pictures. Thanks

lightning_missile
  • 2,821
  • 5
  • 30
  • 58

4 Answers4

2

First of all, it doesn't matter whether you use columns or rows - the output will be the same, because the problem is symmetric. This symmetry will create some confusion; be prepared for this.

Without regard to your specific questions, the idea here is to do recursion. The problem talks about an arrangement of 8 queens. If you have placed k-1 queens, you got a "position". From each position, you can get several "extended" positions, in which you have placed one more queen (so there are k queens). So for each set of positions with k-1 queens, there is a set of positions with k queens.

This set should be "filtered" - remove all the invalid positions from it. In some cases, it will be empty (not possible to place another queen); this is in no way a special situation - it will happen a lot. In other cases (actually, the majority of cases), it will be big. For example, for an "empty" position - no queens - there will be several (actually, 8 - see below) "extended positions" with 1 queen placed.

Now, it doesn't really matter how you place the additional queen. In the general case (when placing any chess piece), you should place it on any free square (and make sure you really check all of them). Because the queens attack as they do, there should be exactly one queen in each column, so it's sufficient to check only 8 possible positions for each queen. For example, in "the next row". Or in "the next column" - it will work too.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
1

Let's see whether I can sort this out for you. I'm going to cut the board down to 5x5 to put a lid on the combinatorial explosion.

You start with an empty board, 0 queens placed. You now want to place a queen in each legal position of column 1. You make the following "scratch pad":

1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0

Now you go down the column and eliminate each illegally-placed queen ... gee, that was easy! All five are legal. Now, you generate a position for each of the legal placements. You get five positions from this:

1 0 0 0 0    0 0 0 0 0    0 0 0 0 0    0 0 0 0 0    0 0 0 0 0
0 0 0 0 0    1 0 0 0 0    0 0 0 0 0    0 0 0 0 0    0 0 0 0 0
0 0 0 0 0    0 0 0 0 0    1 0 0 0 0    0 0 0 0 0    0 0 0 0 0
0 0 0 0 0    0 0 0 0 0    0 0 0 0 0    1 0 0 0 0    0 0 0 0 0
0 0 0 0 0    0 0 0 0 0    0 0 0 0 0    0 0 0 0 0    1 0 0 0 0

I'll put aside the last four for now, and carry on with just the first.

This is a position with one queen, so we want to place a queen in column two. Again, we make a scratch pad, placing a queen in each row:

1 1 0 0 0
0 1 0 0 0
0 1 0 0 0
0 1 0 0 0
0 1 0 0 0

Eliminate the illegal placements:

1 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 0
0 1 0 0 0

Generate a new position for each legal placement:

1 0 0 0 0    1 0 0 0 0    1 0 0 0 0
0 0 0 0 0    0 0 0 0 0    0 0 0 0 0
0 1 0 0 0    0 0 0 0 0    0 0 0 0 0
0 0 0 0 0    0 1 0 0 0    0 0 0 0 0
0 0 0 0 0    0 0 0 0 0    0 1 0 0 0

At this point, if you're doing a breadth-first traversal of the solution space, you put these positions onto a list of some sort for later processing, and do the second 1-queen position next. If you're doing depth-first (which I recommend to save some memory), put the 2nd and 3rd positions above into the list with the remaining four 1-queen solutions, and work immediately with the first position just above.

Put a queen in each row of column 3:

1 0 1 0 0
0 0 1 0 0
0 1 1 0 0
0 0 1 0 0
0 0 1 0 0

Eliminate the illegal ones:

1 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 1 0 0

And generate a new position for each legal placement -- and there's only one.

Continuing two more steps, we get our first solution:

1 0 0 0 0
0 0 0 1 0
0 1 0 0 0
0 0 0 0 1
0 0 1 0 0

This is known as "luck" ... we often don't get a solution on the first branch. For instance, following this position down the first branch ...

1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0

The next step gives you

1 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 1 0 0 0

Now, there is no legal placement for column 4. In this case, you have to give up and take the next legal position on your waiting list.

I highly recommend a recursive, depth-first solution. This lets you keep the list of positions locally. At the deepest point, you can have only 8 active calls to the queen-adding function, each of which is trivially limited to 8-k positions (not considering diagonal attacks); the reality is somewhat less, such that you'll never have more than about 25 positions active on all levels combined. Breadth-first will exceed this before you're done placing the 2nd queen on all branches.

Prune
  • 76,765
  • 14
  • 60
  • 81
  • How is the number of solution to the 4th scartchpad 3? – lightning_missile Sep 25 '16 at 05:23
  • Do you mean the final stat of the scratchpad for column 2? There are three legal positions to place a queen in that column; hence, 3 positions to generate. – Prune Sep 25 '16 at 17:37
  • I think that's what have been confusing me. How can I find the legal positions? Or how to know if the queen is legal? Because based on the book, the two queens can be legal if there are no two queens in the same row, the same column or diagonal. But it seems the first scratchpad have each queen on each column row 1. You said all of them are legal, so I think what happened is each column is a separate position? After that I am lost on how you did the next positions... – lightning_missile Sep 25 '16 at 17:54
  • I guess what I'm asking is: How to find that there are three legal positions? – lightning_missile Sep 25 '16 at 18:03
  • Yes, there are five queens in the first column on scratchpad 1. This gives rise to the five positions from that layer, one for each queen. For the second column (and second scratchpad) we consider only one position at a time. For illustration, I chose the one with the queen in the upper-left corner. On the level two scratchpad, *there is only one queen in column 1.*" You consider the other positions separately, in later iterations. Given the queen in the upper-left corner, there are three legal places to put a queen in column 2. – Prune Sep 26 '16 at 01:53
  • "Given the queen in the upper-left corner, there are three legal places to put a queen in column 2." - are the queens at the last three rows the legal queens? What about the second one with no queen in it? – lightning_missile Sep 26 '16 at 04:17
  • Yes, those three are the legal queens. To be more precise, those are the three squares at which you can place *one* legal queen, since placing any one of the three makes the other two squares illegal. That's why you have to generate three different positions, one for each queen. – Prune Sep 26 '16 at 15:46
  • What is your confusion about the second row, the one with no queen? That's not a legal placement: you quoted the rule from the book. – Prune Sep 26 '16 at 15:47
  • I'm confused as to why you still generate the three positions to the first and second columns. – lightning_missile Sep 27 '16 at 05:02
  • You generate 5 positions for the first column, not three, because there are 5 places in which you can legally place a queen. In the one example I gave you for the second column, there are 3 squares on which you can legally place a queen. Until you can elucidate what you don't understand, I'm afraid I can't help you more. – Prune Sep 27 '16 at 05:09
0

The wording of the solution, or rather the description of the algorithm, is perhaps somewhat convoluted.

Basically, it suggests to

  • first, create all possible combinations of eight queens on a chess board, with one queen per column. The prescribed steps for that are:

    • place a queen in column 1, on the first row. Then place the next queen in column 2, on the first row. And so on, to column 8. (Obviously, this solution is invalid.) This is the first position (for all 8 queens).
    • next, do exactly the same, but for the queen in column 8 (Q8), place it on row number 2. This is position number 2.
    • next, Q8 on row 3. Etc, until Q8 has passed all rows. That is eight positions.
    • next, Q1 to Q6 keep row 1, Q7 moves to row 2, Q8 starts back at row 1.
    • again, as before, move Q8 between row 1 and 8. Another 8 positions.
    • Q1 to Q6 still row 1, Q7 to row 3, Q8 row 1 to 8.
    • And so on, until both Q7 and Q8 reach row 8.
    • Now it's Q6 turn to move to row 2. Other than that, repeat the patterns for Q8 and Q7 again. You can see the recursion.

    The above generates 8^8 = 16777216 solutions, many of which are invalid. This is the "extended set of positions".

  • now, out of all those solutions ("positions"), go through them one by one, and keep those where none of the queens can take the other (you'd have to step through 7 queens for each positions, validating their rows, columns and diagonals; Q8 is then automatically accounted for).

    If you keep only the valid positions, you'll have all the valid configurations for the eight queens puzzle.

  • 1
    This is not what the solution description states. It describes moving across the board, one column at a time. At each column, generate a new position with an added queen, one for each row -- and then eliminate the invalid positions *before* moving to the next column. *That* is the "extended set". – Prune Sep 22 '16 at 17:24
  • @morbidCode Could you please unaccept my answer, as it's incorrect? –  Sep 23 '16 at 05:12
0

Java code using backtracking, change SIZE variable to make it generic

public class EightQueens {

    private final int SIZE = 10;

    private final int safe = 0;
    private final int cut = 1;
    private final int queen = 2;

    private int[][] mat = new int[SIZE][SIZE];

    private EightQueens() {
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                mat[x][y] = safe;
            }
        }
    }

    private void solveAll(){
        solve(mat);
    }


    private int[][] copy(int[][] mat){
        int[][] test = new int[SIZE][SIZE];
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                test[x][y] = mat[x][y];
            }
        }
        return test;
    }

    private void solve(int[][] matrix) {

       // System.out.println("solving ");
       // printMat(matrix);

        if (solved(matrix)) {
            printMat(matrix);
            return;
        }
        int unsafe;
        for (int x = 0; x < SIZE; x++) {
            unsafe = 0;
            for (int y = 0; y < SIZE; y++) {
                if(matrix[x][y] == safe){

                    int[][] test = copy(matrix);
                    test[x][y] = queen;
                    //mark row
                    for(int z =0 ;z<SIZE;z++){
                        if(z == x){
                            continue;
                        }
                        test[z][y] = cut;
                    }
                    //mark col
                    for(int z =0 ;z<SIZE;z++){
                        if(z == y){
                            continue;
                        }
                        test[x][z] = cut;
                    }

                    //first diagonal
                    int a = x+1;
                    int b = y+1;
                    while(a<SIZE && b <  SIZE){
                        test[a][b] = cut;
                        a++;b++;
                    }
                    a = x-1;
                    b = y-1;
                    while(a>=0 && b >=0){
                        test[a][b] = cut;
                        a--;b--;
                    }

                    //second diagonal
                    a = x+1;
                    b = y-1;
                    while(a<SIZE && b >=  0){
                        test[a][b] = cut;
                        a++;b--;
                    }
                    a = x-1;
                    b = y+1;
                    while(a>=0 && b < SIZE){
                        test[a][b] = cut;
                        a--;b++;
                    }

                    solve(test);
                }else {
                    unsafe++;
                    if(unsafe == SIZE){
                        return;
                    }
                }
            }
        }
    }

    private void printMat(int[][] mat){
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                System.out.print(mat[x][y]+" ");
            }
            System.out.println();
        }
        System.out.println("_______________________________________________");
    }

    private boolean solved(int[][] mat) {
        int count = 0;
        for (int x = 0; x < SIZE; x++) {
            for (int y = 0; y < SIZE; y++) {
                if (mat[x][y] == queen) {
                    count++;
                }
            }
        }
        if (count == SIZE) {
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args){
        EightQueens eightQueens = new EightQueens();
        eightQueens.solveAll();
    }

}
Dheeraj Sachan
  • 3,965
  • 2
  • 17
  • 18