3

I should resolve 16-Queens Problem in 1 second. I used backtracking algorithm like below. This code is enough to resolve N-Queens Problem in 1 second when the N is smaller than 13. But it takes long time if N is bigger than 13.

How can I improve it?

#include <stdio.h>
#include <stdlib.h>

int n;
int arr[100]={0,};
int solution_count = 0;

int check(int i) 
{ 
    int k=1, ret=1;
    while (k < i && ret == 1) {
        if (arr[i] == arr[k] ||                 
            abs(arr[i]-arr[k]) == abs(i-k))     
            ret = 0; 
        k++;
    }
    return ret;
}

void backtrack(int i) 
{
    if(check(i)) {
        if(i == n) {
            solution_count++;
        } else {
            for(int j=1; j<=n; j++) {
                arr[i+1] = j;
                backtrack(i+1);
            }
        }
    }
}

int main() 
{ 
    scanf("%d", &n);  
    backtrack(0);
    printf("%d", solution_count);
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Raymond
  • 473
  • 3
  • 18
  • 1
    This appears to be off-topic because it's asking for general performance enhancements to working code. You might have better luck on [CodeReview.SE], but be sure to read their help center before you post it there. – Nathan Tuggy Aug 23 '15 at 02:06
  • 2
    @NathanTuggy I think this one is on the fence: on the surface, it asks for a performance enhancement, but on the other hand it requires a change that is deep enough to be a programming question. – Sergey Kalinichenko Aug 23 '15 at 02:07
  • 1
    @dasblinkenlight: Well, sure, but it's not clear *what sort of change is needed*: it's asking for guidance on generally picking an algorithmic improvement of some sort or other. – Nathan Tuggy Aug 23 '15 at 02:08
  • 2
    @NathanTuggy Actually, OP's algo is fine. It's the data structure that needs some fine-tuning. – Sergey Kalinichenko Aug 23 '15 at 02:10

2 Answers2

4

Your algorithm is almost fine. A small change will probably give you enough time improvement to produce a solution much faster. In addition, there is a data structure change that should let you reduce the time even further.

First, tweak the algorithm a little: rather than waiting for the check all the way till you place all N queens, check early: every time you are about to place a new queen, check if another queen is occupying the same column or the same diagonal before making the arr[i+1] = j; assignment. This will save you a lot of CPU cycles.

Now you need to speed up checking of the next queen. In order to do that you have to change your data structure so that you could do all your checks without any loops. Here is how to do it:

  • You have N rows
  • You have N columns
  • You have 2N-1 ascending diagonals
  • You have 2N-1 descending diagonals

Since no two queens can take the same spot in any of the four "dimensions" above, you need an array of boolean values for the last three things; the rows are guaranteed to be different, because the i parameter of backtrack, which represents the row, is guaranteed to be different.

With N up to 16, 2N-1 goes up to 31, so you can use uint32_t for your bit arrays. Now you can check if a column c is taken by applying bitwise and & to the columns bit mask and 1 << c. Same goes for the diagonal bit masks.

Note: Doing a 16 Queen problem in under a second would be rather tricky. A very highly optimized program does it in 23 seconds on an 800 MHz PC. A 3.2 GHz should give you a speed-up of about 4 times, but it would be about 8 seconds to get a solution.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Here is [my implementation on ideone](http://ideone.com/wXR7hb), it does 14Q in about 4 seconds. – Sergey Kalinichenko Aug 23 '15 at 03:13
  • 2
    [My implementation](http://ideone.com/HavRN2) of the same idea does 16Q in less than 1s. But it uses 4 threads. – Evgeny Kluev Aug 23 '15 at 11:38
  • Thank you, dasblinknlight and Evgeny Kluev. Bug Evgeny's code exceeded Time limit in ideone.com. Did I miss something? – Raymond Aug 26 '15 at 05:01
  • @dasblinkenlight, Thank you. It works for 14-Queens. But it takes several minutes for 16-Queens. Is there any idea to improve speed more? – Raymond Aug 26 '15 at 05:26
  • @dasblinkenlight, I improved your code by using mirror. [code](http://ideone.com/9oJkhI). It takes half of time. Is there any more improvement idea? – Raymond Aug 26 '15 at 06:37
  • @Raymond Did you read Eugene's implementation (link from the second comment on this question). There's also a link in the last paragraph of the answer, it could be helpful too. – Sergey Kalinichenko Aug 26 '15 at 08:31
1

I would change while (k < i && ret == 1) { to while (k < i) {
and instead of ret = 0; do return 0;.

(this will save a check every iteration. It might be that your compiler does this anyway, or some other performance trick, but this might help a bit).

shapiro yaacov
  • 2,308
  • 2
  • 26
  • 39