Проблема С N-Queens..Как далеко мы можем зайти?
Проблема N-Queens:
эта задача гласит, что, учитывая шахматную доску размером N на N, найдите различные перестановки, в которых n ферзей могут быть размещены на доске без какой-либо угрозы друг другу.
мой вопрос:
каково максимальное значение N, для которого программа может рассчитать ответ в разумное количество времени? Или какое место мы видели до сих пор?
вот моя программа в CLPFD (Prolog):
generate([],_).
generate([H|T],N) :-
H in 1..N ,
generate(T,N).
lenlist(L,N) :-
lenlist(L,0,N).
lenlist([],N,N).
lenlist([_|T],P,N) :-
P1 is P+1,
lenlist(T,P1,N).
queens(N,L) :-
generate(L,N),lenlist(L,N),
safe(L),
!,
labeling([ffc],L).
notattack(X,Xs) :-
notattack(X,Xs,1).
notattack(X,[],N).
notattack(X,[Y|Ys],N) :-
X #= Y,
X #= Y - N,
X #= Y + N,
N1 is N + 1,
notattack(X,Ys,N1).
safe([]).
safe([F|T]) :-
notattack(F,T),
safe(T).
эта программа работает просто отлично, но время продолжает увеличиваться с Н. Вот пример выполнения:
?- queens(4,L).
L = [2, 4, 1, 3] ;
L = [3, 1, 4, 2] ;
No
это означает, что вы размещаете 4 королевы в строке 2 в столбце 1, строке 4 в столбце 2, строке 1 в 3 и строке 3 в 4.(В шахматной доске 4 на 4)
теперь посмотрим, как работает эта программа(Время, затраченное на вычисление первой перестановки):
Для N=4,5.....10 вычисляет в секунду!--7-->
Для N=11-30 принимает между -1-3 секунд
Для N=40..50 все еще вычисляет в течение минуты
При N=60 он выходит из глобального стека (пространство поиска огромно).
это была проблема домашней работы, которая должна была быть в прошлом месяце. Поэтому я думаю, что сейчас самое время это обсудить.(Первоначальная проблема заключалась в том, чтобы просто закодировать N-Queens)
Я также заинтересован в том, чтобы видеть альтернативные реализации на других языках(которые работают лучше, чем моя реализация) или если есть место для улучшения в моем алгоритм / программа
9 ответов
короткое решение, представленное Раймондом хеттингером в pycon: легкий ai в python
#!/usr/bin/env python
from itertools import permutations
n = 12
cols = range(n)
for vec in permutations(cols):
if (n == len(set(vec[i] + i for i in cols))
== len(set(vec[i] - i for i in cols))):
print vec
вычисление всех перестановок не масштабируется, хотя (O(n!)
)
Это обсуждение объединяет три различные вычислительные задачи: (1) поиск решения задачи N queens, (2) перечисление всех решений для некоторого фиксированного N и (3) подсчет всех решений для некоторого фиксированного N. первая задача выглядит сложной сначала для размера платы, такого как N=8. Однако, как предполагает Википедия, В некоторых ключевых отношениях это легко, когда N велико. Королевы на большой доске не так уж много общаются. За исключением ограничений памяти, эвристический ремонт алгоритм был проще и легче, как N возрастает.
перечисление каждого решения-это другое дело. Это, вероятно, можно сделать с хорошим динамическим программным кодом до размера, достаточно большого, чтобы не было смысла читать вывод.
наиболее интересной версией вопроса является подсчет решений. Состояние искусства резюмируется в сказочной ссылке, известной как энциклопедия целочисленных последовательностей. Он был вычислен до N=26. Я бы предположил, что это также использует динамическое программирование, но в отличие от случая перечисления каждого решения, алгоритмическая проблема намного глубже и открыта для дальнейших достижений.
это увлекательное отсутствие предсказуемости в backtrack-сложности для разных размеров платы было частью этой головоломки, которая меня больше всего интересовала. В течение многих лет я строил список "подсчетов" шагов алгоритма, необходимых для поиска первый вариант для каждого размера доски-используя простой и известный алгоритм глубин-первого, в рекурсивные функции в C++.
вот список всех этих "подсчетов" для досок до N=49 ... минус N=46 и N=48, которые все еще находятся в процессе работы:
http://queens.cspea.co.uk/csp-q-allplaced.html
(У меня есть это, перечисленное в Энциклопедии целочисленных последовательностей (OEIS) как A140450)
эта страница содержит ссылку на список соответствующие решения.
(мой список Первые Решения является порядковым номером OEIS A141843)
Я в первую очередь не записываю, сколько времени обработки требует каждое решение, а скорее записываю, сколько неудачных размещений queen было необходимо до обнаружения алгоритмически первого решения каждой платы. Конечно, скорость размещения queen зависит от производительности процессора, но, учитывая быстрый тестовый запуск на конкретном процессоре и определенном размере платы, легко рассчитать, сколько времени потребовалось решите одно из этих "найденных" решений.
например, на процессоре Intel Pentium D 3.4 GHz, используя один поток процессора -
- для N=35 моя программа "поместила" 24 миллиона ферзей в секунду и заняла всего 6 минут, чтобы найти первое решение.
- для N=47 моя программа "разместила" 20,5 миллиона ферзей в секунду и заняла 199 дней.
мой текущий 2.8 GHz i7-860 пробивается через около 28,6 миллионов Королев в секунду, пытаясь найти первое решение для N=48. До сих пор потребовалось более 550 дней (теоретически, если он никогда не был непрерывным), чтобы безуспешно разместить 1,369,331,731,000,000 (и быстро поднимающихся) Королев.
мой веб-сайт (пока) не показывает код C++, но я даю ссылку на этой веб-странице на мою простую иллюстрацию каждого из 15 шагов алгоритма, необходимых для решения платы N=5.
это действительно восхитительная головоломка!
какую систему пролога вы используете? Например, с последними версиями SWI-Prolog вы можете легко найти решения для N=80 и N=100 в течение долей секунды, используя исходный код. Многие другие системы Prolog будут намного быстрее.
проблема N-queens даже показана в одном из онлайн-примеров SWI-Prolog, доступном как CLP(FD) queens в SWISH.
пример 100 королевы!--3-->:
?- time((n_queens(100, Qs), labeling([ff], Qs))). Qs = [1, 3, 5, 57, 59 | ...] . 2,984,158 inferences, 0.299 CPU in 0.299 seconds (100% CPU, 9964202 Lips)
SWISH также показывает вам изображение nices решений.
вот анимированный GIF, показывающий полный процесс решения для N=40 королевы с SWI-Prolog:
Что касается того, что является самым большим N решается компьютерами, в литературе есть ссылки, в которых решение для N около 3*10^6 было найдено с помощью алгоритма восстановления конфликтов (т. е. локального поиска). См., например, классическую статью [sosic хорватск и ГУ].
Что касается точного решения с обратным отслеживанием, существуют некоторые умные ветвящиеся эвристики, которые достигают правильных конфигураций почти без обратного отслеживания. Эти эвристики также могут быть использованы для поиска первый-k решения проблемы: после поиска начальной правильной конфигурации поиск возвращается, чтобы найти другие допустимые конфигурации в непосредственной близости.
ссылки на эти почти совершенное эвристики [Кейл 90] и [Сан-Сегундо 2011]
каково максимальное значение N, для которого программа может рассчитать ответ за разумное количество времени? Или какое место мы видели до сих пор?
нет предела. То есть проверка правильности решения стоит дороже, чем построение одного решения плюс семь симметричных.
Посмотреть В Википедии: "явные решения для размещения n ферзей на доске n × n для всех N ≥ 4, не требующим комбинаторного поиска Любой.".
Я вытащил старую программу Delphi, которая подсчитывала количество решений для любого заданного размера платы, сделала быструю модификацию, чтобы остановить ее после одного удара, и я вижу странный шаблон в данных:
первая плата, которая заняла более 1 секунды для решения, была n = 20. Однако 21 решена за 62 миллисекунды. (Примечание: это основано теперь, не любая система высокой точности.) 22 заняло 10 секунд, чтобы не повторяться до 28.
Я не знаю, насколько хороша оптимизация поскольку это изначально была высоко оптимизированная процедура, когда правила оптимизации были очень разными. Я сделал одну вещь, очень отличающуюся от большинства реализаций, хотя-у нее нет платы. Скорее, я отслеживаю, какие столбцы и диагонали атакованы, и добавляю по одной королеве на строку. Это означает, что 3 поиска массива на проверенную ячейку и никакого умножения вообще. (Как я уже сказал, С тех пор правила сильно отличались.)
теперь для некоторого реального безумия: 29 заняло 9 секунд. 30 взял почти 6 минут!
фактически ограниченное случайное блуждание (генерация и тестирование), как то, что изложил bakore, - это путь, если вам просто нужно несколько решений, потому что они могут быть быстро сгенерированы. Я сделал это для класса, когда мне было 20 или 21 и опубликовал решение в журнале Pascal, Ada & Modula-2, март 1987 года, "the Queens Problem Revisited". Я только что отряхнул код из этой статьи сегодня (и это очень неэффективный код), и после исправления пары проблем были созданы N=26 ... N=60 решений.
Если вы хотите только 1 решение, то его можно найти жадно в линейное время O (N). Мой код на Python:
import numpy as np
n = int(raw_input("Enter n: "))
rs = np.zeros(n,dtype=np.int64)
board=np.zeros((n,n),dtype=np.int64)
k=0
if n%6==2 :
for i in range(2,n+1,2) :
#print i,
rs[k]=i-1
k+=1
rs[k]=3-1
k+=1
rs[k]=1-1
k+=1
for i in range(7,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=5-1
elif n%6==3 :
rs[k]=4-1
k+=1
for i in range(6,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=2-1
k+=1
for i in range(5,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=1-1
k+=1
rs[k]=3-1
else :
for i in range(2,n+1,2) :
rs[k]=i-1
k+=1
for i in range(1,n+1,2) :
rs[k]=i-1
k+=1
for i in range(n) :
board[rs[i]][i]=1
print "\n"
for i in range(n) :
for j in range(n) :
print board[i][j],
print
здесь, однако печать занимает O (N^2) время, а также python, являющийся более медленным языком, любой может попробовать реализовать его на других языках, таких как C/C++ или Java. Но даже с python он получит первое решение для n=1000 в течение 1 или 2 секунд.