Решите задачу с 16 ферзями всего за 1 секунду

Я должен решить Задачу с 16 ферзями за 1 секунду. Я использовал алгоритм возврата, как показано ниже. Этого кода достаточно, чтобы решить проблему N-Queens за 1 секунду, когда N меньше 13. Но это занимает много времени, если N больше 13.

Как я могу улучшить его?

#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);
}

person Raymond    schedule 23.08.2015    source источник
comment
Кажется, это не по теме, потому что требуется общее улучшение производительности рабочего кода. Возможно, вам повезет больше на Code Review, но не забудьте прочитать их справочный центр, прежде чем опубликовать его там.   -  person Nathan Tuggy    schedule 23.08.2015
comment
@NathanTuggy Я думаю, что этот вопрос находится на грани: на первый взгляд, он требует повышения производительности, но, с другой стороны, требует изменения, достаточно глубокого, чтобы быть вопросом программирования.   -  person Sergey Kalinichenko    schedule 23.08.2015
comment
@dasblinkenlight: Ну, конечно, но неясно, какое изменение необходимо: он просит совета по общему выбору того или иного алгоритмического улучшения.   -  person Nathan Tuggy    schedule 23.08.2015
comment
@NathanTuggy На самом деле, алгоритм OP в порядке. Это структура данных, которая нуждается в тонкой настройке.   -  person Sergey Kalinichenko    schedule 23.08.2015


Ответы (2)


Ваш алгоритм почти в порядке. Небольшое изменение, вероятно, даст вам достаточно времени для улучшения, чтобы найти решение намного быстрее. Кроме того, есть изменение структуры данных, которое должно позволить вам еще больше сократить время.

Во-первых, немного изменим алгоритм: вместо того, чтобы ждать проверки до тех пор, пока вы не разместите все N ферзя, проверяйте заранее: каждый раз, когда вы собираетесь разместить нового ферзя, проверяйте, не занимает ли другой ферзь тот же столбец или тот же столбец. диагонали до присвоения arr[i+1] = j;. Это сэкономит вам много циклов процессора.

Теперь нужно ускорить проверку следующего ферзя. Для этого вам нужно изменить структуру данных, чтобы вы могли выполнять все свои проверки без каких-либо циклов. Вот как это сделать:

  • У вас есть N строк
  • У вас есть N столбца
  • У вас есть 2N-1 восходящих диагоналей
  • У вас есть 2N-1 убывающих диагоналей

Поскольку никакие два ферзя не могут занять одно и то же место ни в одном из четырех «измерений» выше, вам нужен массив логических значений для последних трех вещей; строки гарантированно будут разными, потому что параметр i в backtrack, представляющий строку, гарантированно будет другим.

С N до 16, 2N-1 увеличивается до 31, так что вы можете использовать uint32_t для своих битовых массивов. Теперь вы можете проверить, занят ли столбец c, применив побитовое и & к битовой маске столбцов и 1 << c. То же самое касается диагональных битовых масок.

Примечание. Решить задачу с 16 ферзями менее чем за секунду было бы довольно сложно. очень оптимизированная программа делает это за 23 секунды на ПК с частотой 800 МГц. 3,2 ГГц должно дать вам ускорение примерно в 4 раза, но для получения решения потребуется около 8 секунд.

person Sergey Kalinichenko    schedule 23.08.2015
comment
Вот моя реализация на ideone, она выполняет 14Q примерно за 4 секунды. - person Sergey Kalinichenko; 23.08.2015
comment
Моя реализация той же идеи выполняет 16Q менее чем за 1 с. Но он использует 4 потока. - person Evgeny Kluev; 23.08.2015
comment
Спасибо, dasblinknlight и Евгений Клюев. Ошибка Код Евгения превысил лимит времени на ideone.com. Я что-то пропустил? - person Raymond; 26.08.2015
comment
@dasblinkenlight, спасибо. Это работает для 14-Queens. Но для 16-Queens требуется несколько минут. Есть ли идея увеличить скорость? - person Raymond; 26.08.2015
comment
@dasblinkenlight, я улучшил ваш код, используя зеркало. код. Это занимает половину времени. Есть ли еще идеи по улучшению? - person Raymond; 26.08.2015
comment
@Raymond Вы читали реализацию Юджина (ссылка из второго комментария к этому вопросу). В последнем абзаце ответа также есть ссылка, она тоже может быть полезна. - person Sergey Kalinichenko; 26.08.2015

Я бы заменил while (k < i && ret == 1) { на while (k < i) {
и вместо ret = 0; сделал return 0;.

(это сохранит проверку на каждой итерации. Возможно, ваш компилятор все равно делает это или какой-то другой трюк с производительностью, но это может немного помочь).

person shapiro yaacov    schedule 23.08.2015