2

I have a recursive function that I wrote in C that looks like this:

void findSolutions(int** B, int n, int i) {
    if (i > n) {
        printBoard(B, n);
    } else {
        for (int x = 1; x <= n; x++) {
            if (B[i][x] == 0) {
                placeQueen(B, n, i, x);
                findSolutions(B, n, i + 1);
                removeQueen(B, n, i, x);
            }
        }
    }
}

The initial call is (size is an integer given by user and B is a 2D array):

findSolutions(B, size, 1);

I tried to convert it into a iteration function but there is another function called removeQueen after findSolutions. I got stuck on where to put this function call. How to solve this problem? Stack is also fine but I'm also having trouble doing that.

3
  • 1
    Consider (int x = 0; x < n; x++) instead of (int x = 1; x <= n; x++) and (i > n) --> (i >= n). C arrays are zero based. Commented Jan 30, 2020 at 5:13
  • 1
    My 2D array has a reserved column and row for other purpose. So other data needs to start from 1. Commented Jan 30, 2020 at 5:18
  • 1
    Read about contination passing style. At an abstract level, it answers your question. But before, read some C reference, the Modern C book, and How to debug small programs so use GCC and compile with gcc -Wall -g Commented Jan 30, 2020 at 6:32

2 Answers 2

1

I'm going to assume that placeQueen(B, n, i, x) makes a change to B and that removeQueen(B, n, i, x) undoes that change.

This answer shows how to approach the problem generically. It doesn't modify the algorithm like Aconcagua has.

Let's start by defining a state structure.

typedef struct {
    int **B;
    int n;
    int i;
} State;

The original code is equivalent to the following:

void _findSolutions(State *state) {
    if (state->i >= state->n) {
        printBoard(state->B, state->n);
    } else {
        for (int x = 1; x <= state->n; ++x) {
            if (state->B[state->i][x] == 0) {
                State *state2 = State_clone(state);  // Deep clone.
                placeQueen(state2);
                ++state2->i;
                findSolutions(state2);
            }
        }
    }

    State_free(state);  // Frees the board too.
}

void findSolutions(int** B, int n, int i) {
    State *state = State_new(B, n, i);  // Deep clones B.
    _findSolutions(state);
}

Now, we're in position to eliminate the recursion.

void _findSolutions(State *state) {
    StateStack *S = StateStack_new();
    do {
        if (state->i >= state->n) {
            printBoard(state->B, state->n);
        } else {
            for (int x = state->n; x>=1; --x) {  // Reversed the loop to maintain order.
                if (state->B[state->i][x] == 0) {
                    State *state2 = State_clone(state);  // Deep clone.
                    placeQueen(state2);
                    ++state2->i;
                    StateStack_push(S, state2);
                }
            }
        }

        State_free(state);  // Frees the board too.
    } while (StateStack_pop(&state));

    StateStack_free(S);
}

void findSolutions(int** B, int n, int i) {
    State *state = State_new(B, n, i);  // Deep clones B.
    _findSolutions(state);
}

We can eliminate the helper we no longer need.

void findSolutions(int** B, int n, int i) {
    StateStack *S = StateStack_new();
    State *state = State_new(B, n, i);  // Deep clones B.
    do {
        if (state->i >= state->n) {
            printBoard(state->B, state->n);
        } else {
            for (int x = state->n; x>=1; --x) {  // Reversed the loop to maintain order.
                if (state->B[state->i][x] == 0) {
                    State *state2 = State_clone(state);  // Deep clone.
                    placeQueen(state2);
                    ++state2->i;
                    StateStack_push(S, state2);
                }
            }
        }

        State_free(state);  // Frees the board too.
    } while (StateStack_pop(S, &state));

    StateStack_free(S);
}

Functions you need to implement:

  • StateStack *StateStack_new(void)
  • void StateStack_free(StateStack *S)
  • void StateStack_push(StateStack *S, State *state)
  • int StateStack_pop(StateStack *S, State **p)

  • State *State_new(int **B, int n, int i) (Note: Clones B)

  • State *State_clone(const State *state) (Note: Clones state->B)
  • void State_free(State *state) (Note: Frees state->B)

Structures you need to implement:

  • StateStack

Tip:

It would be best if you replaced

int **B = malloc((n+1)*sizeof(int*));
for (int i=1; i<=n; ++i)
   B[i] = calloc(n+1, sizeof(int));
...
for (int x = 1; x <= n; ++x)
...
B[i][x]

with

char *B = calloc(n*n, 1);
...
for (int x = 0; x < n; ++x)
...
B[(i-1)*n+(x-1)]
Sign up to request clarification or add additional context in comments.

1 Comment

We haven't learn state stack yet so I will try to avoid the risk using a method I don't understand very much. But thanks a lot for the help!
1

What you get by the recursive call is that you get stored the location of the queen in current row before you advance to next row. You will have to re-produce this in the non-recursive version of your function.

You might use another array storing these positions:

unsigned int* positions = calloc(n + 1, sizeof(unsigned int));
// need to initialise all positions to 1 yet:
for(unsigned int i = 1; i <= n; ++i)
{
    positions[i] = 1;
}

I reserved a dummy element so that we can use the same indices...

You can now count up last position from 1 to n, and when reaching n there, you increment next position, restarting with current from 1 – just the same way as you increment numbers in decimal, hexadecimal or octal system: 1999 + 1 = 2000 (zero based in this case...).

for(;;)
{
    for(unsigned int i = 1; i <= n; ++i)
    {
        placeQueen(B, n, i, positions[i]);
    }
    printBoard(B, n);
    for(unsigned int i = 1; i <= n; ++i)
    {
        removeQueen(B, n, i, positions[i]);
    }
    for(unsigned int i = 1; i <= n; ++i)
    {
        if(++positions[i] <= n)
            // break incrementing if we are in between the numbers:
            // 1424 will get 1431 (with last position updated already before)
            goto CONTINUE;

        positions[i] = 1;
    }
    // we completed the entire positions list, i. e. we reset very
    // last position to 1 again (comparable to an overflow: 4444 got 1111)
    // so we are done -> exit main loop:
    break;
    CONTINUE: (void)0;
}

It's untested code, so you might find a bug in, but it should clearly illustrate the idea. It's the naive aproach, always placing the queens and removing them again.

You can do it a bit cleverer, though: place all queens at positions 1 initially and only move the queens if you really need:

for(unsigned int i = 1; i <= n; ++i)
{
    positions[i] = 1;
    placeQueen(B, n, i, 1);
}
for(;;)
{
    printBoard(B, n);
    for(unsigned int i = 1; i <= n; ++i)
    {
        removeQueen(B, n, i, positions[i]);
        ++positions[i]
        if(++positions[i] <= n)
        {
            placeQueen(B, n, i, positions[i]);
            goto CONTINUE;
        }

        placeQueen(B, n, i, 1);
        positions[i] = 1;
    }
    break;
    CONTINUE: (void)0;
}
// cleaning up the board again:
for(unsigned int i = 1; i <= n; ++i)
{
    removeQueen(B, n, i, 1);
}

Again, untested...

You might discover that now the queens move within first row first, different to your recursive approach before. If that disturbs you, you can count down from n to 1 while incrementing the positions and you get original order back...

At the very end (after exiting the loop), don't forget to free the array again to avoid memory leak:

free(positions);

If n doesn't get too large (eight for a typical chess board?), you might use a VLA to prevent that problem.


Edit:

Above solutions will print any possible combinations to place eight queens on a chess board. For an 8x8 board, you get 88 possible combinations, which are more than 16 millions of combinations. You pretty sure will want to filter out some of these combinations, as you did in your original solution as well (if(B[i][x] == 0)), e. g.:

unsigned char* checks = malloc(n + 1);
for(;;)
{
    memset(checks, 0, (n + 1));
    for(unsigned int i = 1; i <= n; ++i)
    {
        if(checks[positions[i]] != 0)
        goto SKIP;
        checks[positions[i]] = 1;
    }

    // place queens and print board

    SKIP:
    // increment positions
}

(Trivial approach! Including the filter in the more elaborate approach will get more tricky!)

This will even be a bit more strict than your test, which would have allowed

_ Q _
Q _ _
_ Q _

on a 3x3 board, as you only compare against previous column, whereas my filter wouldn't (leaving a bit more than 40 000 boards to be printed for an 8x8 board).


Edit 2: The diagonals

To filter out those boards where the queens attack each other on the diagonals you'll need additional checks. For these, you'll have to find out what the common criterion is for the fields on the same diagonal. At first, we have to distinguish two types of diagonals, those starting at B[1][1], B[1][2], ... as well as B[2][1], B[3][1], ... – all these run from top left to bottom right direction. On the main diagonal, you'll discover that the difference between row and column index does not differ, on next neighbouring diagonals the indices differ by 1 and -1 respectively, and so on. So we'll have differences in the range [-(n-1); n-1].

If we make the checks array twice as large and shift all differences by n, can re-use do exactly the same checks as we did already for the columns:

unsigned char* checks = (unsigned char*)malloc(2*n + 1);

and after we checked the columns:

memset(checks, 0, (2 * n + 1));
for(unsigned int i = 1; i <= n; ++i)
{
    if(checks[n + i - positions[i]] != 0)
        goto SKIP;
    checks[n + i - positions[i]] = 1;
}

Side note: Even if the array is larger, you still can just memset(checks, 0, n + 1); for the columns as we don't use the additional entries...

Now next we are interested in are the diagonals going from bottom left to top right. Similarly to the other direction, you'll discover that the difference between n - i and positions[i] remains constant for fields on the same diagonal. Again we shift by n and end up in:

memset(checks, 0, (2 * n + 1));
for(unsigned int i = 1; i <= n; ++i)
{
    if(checks[2 * n - i - positions[i]] != 0)
        goto SKIP;
    checks[2 * n - i - positions[i]] = 1;
}

Et voilà, only boards on which queens cannot attack each other.

You might discover that some boards are symmetries (rotational or reflection) of others. Filtering these, though, is much more complicated...

7 Comments

I tested the naive one while trying to understand the logic but the code ran into a infinite loop and printed out all the number combinations.
@ryanchai_1995 At first, I hope you didn't miss my comment to initialise all values to 1. Maybe should have written that explicitly... At second: I missed a line of code in your original code: if (B[i][x] == 0). My solution produces all possible placements (try with e. g. 3x3 board to see), for 8x8, you get 8^8 > 16 mio solutions printed. So you would want to add some additional checks to prevent printing some of these boards...
I did everything from above but the filter seems did not filter out all the unwanted results. It does not exclude the situation that queens attack on the diagonals.
@ryanchai_1995 Sure, because my filter doesn't consider that situation. Did your original recursive solution? Did you ask for at all??? You need additional checks... I'll add some lines about the issue.
The filter works now but somehow it is super slow when the size is 11. The queen can also be placed by user for example the user will put a queen in 4,4 and 6,1 and I need to solve the rest of the queens. My approach is to filter out all the solutions which contain 4,4 and 6,1. But is there a way to just avoid this before the for loop starts? Also, I need just one solution for each situation.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.