Задача о восьми ферзях
Задача о восьми ферзях — широко известная задача по расстановке фигур на шахматной доске. Исходная формулировка: «Расставить на стандартной 64-клеточной шахматной доске 8 ферзей так, чтобы ни один из них не находился под боем другого».
Содержание |
[править] Формулировка
В более «математическом» виде задача может быть сформулирована несколькими способами, например, так: «Заполнить матрицу размером 8×8 нулями и единицами таким образом, чтобы сумма всех элементов матрицы была равна 8, при этом сумма элементов ни в одном столбце, строке или диагональном ряде матрицы не превышала единицы».
Конечная цель, поставленная перед решающим задачу, может формулироваться в нескольких вариантах:
- Построить одно, любое решение задачи.
- Аналитически доказать, что решение существует.
- Определить количество решений.
- Построить все возможные решения.
- Одна из типовых задач по программированию алгоритмов перебора: создать компьютерную программу, находящую все возможные решения задачи.
Иногда постановка задачи требует нахождения способов расстановки N ферзей на доске N×N клеток (при этом при 1<N<4 задача не имеет решения).Подробно об этом изложено в украинской и английской версиях данной статьи в Википедии.
Мы же подробнее рассмотрим классический “шахматный” случай для доски 8*8.
[править] Особенности решения
Общее число возможных расположений 8 ферзей на 64-клеточной доске равно 4426165368. Общее число возможных расположений, удовлетворяющих условию задачи равно 92. В принципе, современные компьютеры уже позволяют произвести решение задачи (нахождение любого или всех решений) путём прямого перебора всех возможных вариантов расстановки, но обычно такое решение считается некорректным, и от решающего задачу требуется найти алгоритм, который позволял бы существенно сократить объём перебора. Например, очевидно, что на одной горизонтали или вертикали доски не может находиться больше одного ферзя, поэтому алгоритм решения изначально не должен включать в перебор позиции, где два ферзя стоят на одной горизонтали или вертикали. Даже такое простое правило способно существенно уменьшить число возможных расположений: 16777216 (то есть
) вместо 4426165368. Генерируя перестановки, которые являются решениями задачи о восьми ладьях и затем проверяя атаки по диагоналям, можно сократить число возможных расположений всего до 40320 (то есть 8!). Однако, если условие нападения по диагонали учитывать при генерации позиций, скорость счёта возрастает на порядок.
Один из типовых алгоритмов решения задачи — использование поиска с возвратом: первый ферзь ставится на первую горизонталь, затем каждый следующий пытаются поставить на следующую так, чтобы его не били ранее установленные ферзи. Если на очередном этапе постановки свободных полей не оказывается, происходит возврат на шаг назад — переставляется ранее установленный ферзь (такой алгоритм использован в опубликованных ниже программах).
Ниже приведена программа, реализующая этот алгоритм на QBasic-е.
CLS DEFINT A-I OPEN "ferz" FOR OUTPUT AS #1 FOR a = 1 TO 8 FOR b = 1 TO 8 IF b = a THEN 8 IF ABS(b - a) = 1 THEN 8 FOR c = 1 TO 8 IF c = a OR c = b THEN 7 IF ABS(c - b) = 1 OR ABS(c - a) = 2 THEN 7 FOR d = 1 TO 8 IF d = a OR d = b OR d = c THEN 6 IF ABS(d - c) = 1 OR ABS(d - b) = 2 OR ABS(d - a) = 3 THEN 6 FOR e = 1 TO 8 IF e = a OR e = b OR e = c OR e = d THEN 5 IF ABS(e - d) = 1 OR ABS(e - c) = 2 OR ABS(e - b) = 3 OR ABS(e - a) = 4 THEN 5 FOR f = 1 TO 8 IF f = a OR f = b OR f = c OR f = d OR f = e THEN 4 IF ABS(f - e) = 1 OR ABS(f - d) = 2 OR ABS(f - c) = 3 OR ABS(f - b) = 4 > OR ABS(f - a) = 5 THEN 4 FOR g = 1 TO 8 IF g = a OR g = b OR g = c OR g = d OR g = e OR g = f THEN 3 IF ABS(g - f) = 1 OR ABS(g - e) = 2 OR ABS(g - d) = 3 OR ABS(g - c) = 4 > OR ABS(g - b) = 5 OR ABS(g - a) = 6 THEN 3 FOR h = 1 TO 8 IF h = a OR h = b OR h = c OR h = d OR h = e OR h = f OR h = g THEN 2 IF ABS(h-g) = 1 OR ABS(h-f) = 2 OR ABS(h-e) = 3 OR ABS(h-d) = 4 OR ABS(h-c) = 5 > OR ABS(h-b) = 6 OR ABS(h-a) = 7 THEN 2 i = i + 1 PRINT i; " "; "a"; a; " "; "b"; b; " "; "c"; c; " "; "d"; d; PRINT " "; "e"; e; " "; "f"; f; " "; "g"; g; " "; "h"; h PRINT #1, i; " "; "a"; a; " "; "b"; b; " "; "c"; c; " "; "d"; PRINT #1, d; " "; "e"; e; " "; "f"; f; " "; "g"; g; " "; "h"; h 2 NEXT h 3 NEXT g 4 NEXT f 5 NEXT e 6 NEXT d 7 NEXT c 8 NEXT b NEXT a END
Список позиций, выдаваемый программой, является списком решений исходной задачи:
1 a 1 b 5 c 8 d 6 e 3 f 7 g 2 h 4 2 a 1 b 6 c 8 d 3 e 7 f 4 g 2 h 5 3 a 1 b 7 c 4 d 6 e 8 f 2 g 5 h 3 4 a 1 b 7 c 5 d 8 e 2 f 4 g 6 h 3 5 a 2 b 4 c 6 d 8 e 3 f 1 g 7 h 5 6 a 2 b 5 c 7 d 1 e 3 f 8 g 6 h 4 7 a 2 b 5 c 7 d 4 e 1 f 8 g 6 h 3 8 a 2 b 6 c 1 d 7 e 4 f 8 g 3 h 5 9 a 2 b 6 c 8 d 3 e 1 f 4 g 7 h 5 10 a 2 b 7 c 3 d 6 e 8 f 5 g 1 h 4 11 a 2 b 7 c 5 d 8 e 1 f 4 g 6 h 3 12 a 2 b 8 c 6 d 1 e 3 f 5 g 7 h 4 13 a 3 b 1 c 7 d 5 e 8 f 2 g 4 h 6 14 a 3 b 5 c 2 d 8 e 1 f 7 g 4 h 6 15 a 3 b 5 c 2 d 8 e 6 f 4 g 7 h 1 16 a 3 b 5 c 7 d 1 e 4 f 2 g 8 h 6 17 a 3 b 5 c 8 d 4 e 1 f 7 g 2 h 6 18 a 3 b 6 c 2 d 5 e 8 f 1 g 7 h 4 19 a 3 b 6 c 2 d 7 e 1 f 4 g 8 h 5 20 a 3 b 6 c 2 d 7 e 5 f 1 g 8 h 4 21 a 3 b 6 c 4 d 1 e 8 f 5 g 7 h 2 22 a 3 b 6 c 4 d 2 e 8 f 5 g 7 h 1 23 a 3 b 6 c 8 d 1 e 4 f 7 g 5 h 2 24 a 3 b 6 c 8 d 1 e 5 f 7 g 2 h 4 25 a 3 b 6 c 8 d 2 e 4 f 1 g 7 h 5 26 a 3 b 7 c 2 d 8 e 5 f 1 g 4 h 6 27 a 3 b 7 c 2 d 8 e 6 f 4 g 1 h 5 28 a 3 b 8 c 4 d 7 e 1 f 6 g 2 h 5 29 a 4 b 1 c 5 d 8 e 2 f 7 g 3 h 6 30 a 4 b 1 c 5 d 8 e 6 f 3 g 7 h 2 31 a 4 b 2 c 5 d 8 e 6 f 1 g 3 h 7 32 a 4 b 2 c 7 d 3 e 6 f 8 g 1 h 5 33 a 4 b 2 c 7 d 3 e 6 f 8 g 5 h 1 34 a 4 b 2 c 7 d 5 e 1 f 8 g 6 h 3 35 a 4 b 2 c 8 d 5 e 7 f 1 g 3 h 6 36 a 4 b 2 c 8 d 6 e 1 f 3 g 5 h 7 37 a 4 b 6 c 1 d 5 e 2 f 8 g 3 h 7 38 a 4 b 6 c 8 d 2 e 7 f 1 g 3 h 5 39 a 4 b 6 c 8 d 3 e 1 f 7 g 5 h 2 40 a 4 b 7 c 1 d 8 e 5 f 2 g 6 h 3 41 a 4 b 7 c 3 d 8 e 2 f 5 g 1 h 6 42 a 4 b 7 c 5 d 2 e 6 f 1 g 3 h 8 43 a 4 b 7 c 5 d 3 e 1 f 6 g 8 h 2 44 a 4 b 8 c 1 d 3 e 6 f 2 g 7 h 5 45 a 4 b 8 c 1 d 5 e 7 f 2 g 6 h 3 46 a 4 b 8 c 5 d 3 e 1 f 7 g 2 h 6 47 a 5 b 1 c 4 d 6 e 8 f 2 g 7 h 3 48 a 5 b 1 c 8 d 4 e 2 f 7 g 3 h 6 49 a 5 b 1 c 8 d 6 e 3 f 7 g 2 h 4 50 a 5 b 2 c 4 d 6 e 8 f 3 g 1 h 7 51 a 5 b 2 c 4 d 7 e 3 f 8 g 6 h 1 52 a 5 b 2 c 6 d 1 e 7 f 4 g 8 h 3 53 a 5 b 2 c 8 d 1 e 4 f 7 g 3 h 6 54 a 5 b 3 c 1 d 6 e 8 f 2 g 4 h 7 55 a 5 b 3 c 1 d 7 e 2 f 8 g 6 h 4 56 a 5 b 3 c 8 d 4 e 7 f 1 g 6 h 2 57 a 5 b 7 c 1 d 3 e 8 f 6 g 4 h 2 58 a 5 b 7 c 1 d 4 e 2 f 8 g 6 h 3 59 a 5 b 7 c 2 d 4 e 8 f 1 g 3 h 6 60 a 5 b 7 c 2 d 6 e 3 f 1 g 4 h 8 61 a 5 b 7 c 2 d 6 e 3 f 1 g 8 h 4 62 a 5 b 7 c 4 d 1 e 3 f 8 g 6 h 2 63 a 5 b 8 c 4 d 1 e 3 f 6 g 2 h 7 64 a 5 b 8 c 4 d 1 e 7 f 2 g 6 h 3 65 a 6 b 1 c 5 d 2 e 8 f 3 g 7 h 4 66 a 6 b 2 c 7 d 1 e 3 f 5 g 8 h 4 67 a 6 b 2 c 7 d 1 e 4 f 8 g 5 h 3 68 a 6 b 3 c 1 d 7 e 5 f 8 g 2 h 4 69 a 6 b 3 c 1 d 8 e 4 f 2 g 7 h 5 70 a 6 b 3 c 1 d 8 e 5 f 2 g 4 h 7 71 a 6 b 3 c 5 d 7 e 1 f 4 g 2 h 8 72 a 6 b 3 c 5 d 8 e 1 f 4 g 2 h 7 73 a 6 b 3 c 7 d 2 e 4 f 8 g 1 h 5 74 a 6 b 3 c 7 d 2 e 8 f 5 g 1 h 4 75 a 6 b 3 c 7 d 4 e 1 f 8 g 2 h 5 76 a 6 b 4 c 1 d 5 e 8 f 2 g 7 h 3 77 a 6 b 4 c 2 d 8 e 5 f 7 g 1 h 3 78 a 6 b 4 c 7 d 1 e 3 f 5 g 2 h 8 79 a 6 b 4 c 7 d 1 e 8 f 2 g 5 h 3 80 a 6 b 8 c 2 d 4 e 1 f 7 g 5 h 3 81 a 7 b 1 c 3 d 8 e 6 f 4 g 2 h 5 82 a 7 b 2 c 4 d 1 e 8 f 5 g 3 h 6 83 a 7 b 2 c 6 d 3 e 1 f 4 g 8 h 5 84 a 7 b 3 c 1 d 6 e 8 f 5 g 2 h 4 85 a 7 b 3 c 8 d 2 e 5 f 1 g 6 h 4 86 a 7 b 4 c 2 d 5 e 8 f 1 g 3 h 6 87 a 7 b 4 c 2 d 8 e 6 f 1 g 3 h 5 88 a 7 b 5 c 3 d 1 e 6 f 8 g 2 h 4 89 a 8 b 2 c 4 d 1 e 7 f 5 g 3 h 6 90 a 8 b 2 c 5 d 3 e 1 f 7 g 4 h 6 91 a 8 b 3 c 1 d 6 e 2 f 5 g 7 h 4 92 a 8 b 4 c 1 d 3 e 6 f 2 g 7 h 5
Интересно отметить, что эти 92 расположения разбиваются на 12 групп:11 групп по 8 и 1-у из 4-х расположений. Положения внутри групп получаются из одного положения путем преобразований симметрии:отражения от вертикальной и горизонтальной осей, отражения от диагоналей доски и поворотов на 90,180 и 270 градусов.Пары расположений,симметричные относительно горизонтальной оси,имеют сумму номеров равную 93,т.е. для каждой группы эта суммма равна 93*4.Программу,получающую эти группы,программу,получающую позиции ферзей при любом n и другие программы на эту тему см.на сайте:
[ http://nabasice.narod2.ru/]
1
a 1 b 5 c 8 d 6 e 3 f 7 g 2 h 4 :1 a 4 b 2 c 7 d 3 e 6 f 8 g 5 h 1 :33 a 8 b 4 c 1 d 3 e 6 f 2 g 7 h 5 :92 a 1 b 7 c 5 d 8 e 2 f 4 g 6 h 3 :4 +(3) a 6 b 3 c 5 d 7 e 1 f 4 g 2 h 8 :71 a 8 b 2 c 4 d 1 e 7 f 5 g 3 h 6 :89 a 5 b 7 c 2 d 6 e 3 f 1 g 4 h 8 :60 a 3 b 6 c 4 d 2 e 8 f 5 g 7 h 1 :22
2
a 1 b 6 c 8 d 3 e 7 f 4 g 2 h 5 :2 a 5 b 2 c 4 d 7 e 3 f 8 g 6 h 1 :51 a 8 b 3 c 1 d 6 e 2 f 5 g 7 h 4 :91 a 1 b 7 c 4 d 6 e 8 f 2 g 5 h 3 :3 +(2) a 6 b 4 c 7 d 1 e 3 f 5 g 2 h 8 :78 a 8 b 2 c 5 d 3 e 1 f 7 g 4 h 6 :90 a 4 b 7 c 5 d 2 e 6 f 1 g 3 h 8 :42 a 3 b 5 c 2 d 8 e 6 f 4 g 7 h 1 :15
3
a 2 b 4 c 6 d 8 e 3 f 1 g 7 h 5 :5 +(1) a 5 b 7 c 1 d 3 e 8 f 6 g 4 h 2 :57 a 7 b 5 c 3 d 1 e 6 f 8 g 2 h 4 :88 a 6 b 1 c 5 d 2 e 8 f 3 g 7 h 4 :65 a 5 b 2 c 6 d 1 e 7 f 4 g 8 h 3 :52 a 3 b 8 c 4 d 7 e 1 f 6 g 2 h 5 :28 a 4 b 2 c 8 d 6 e 1 f 3 g 5 h 7 :36 a 4 b 7 c 3 d 8 e 2 f 5 g 1 h 6 :41
4
a 2 b 5 c 7 d 1 e 3 f 8 g 6 h 4:6 a 4 b 6 c 8 d 3 e 1 f 7 g 5 h 2:39 a 7 b 4 c 2 d 8 e 6 f 1 g 3 h 5:87 a 4 b 1 c 5 d 8 e 2 f 7 g 3 h 6:29 +(4) a 3 b 6 c 2 d 7 e 1 f 4 g 8 h 5:19 a 5 b 8 c 4 d 1 e 7 f 2 g 6 h 3:64 a 5 b 3 c 1 d 6 e 8 f 2 g 4 h 7:54 a 6 b 3 c 7 d 2 e 8 f 5 g 1 h 4:74
5
a 2 b 5 c 7 d 4 e 1 f 8 g 6 h 3 :7 a 3 b 6 c 8 d 1 e 4 f 7 g 5 h 2 :23 a 7 b 4 c 2 d 5 e 8 f 1 g 3 h 6 :86 a 5 b 1 c 8 d 4 e 2 f 7 g 3 h 6 :48 +(5) a 3 b 6 c 2 d 7 e 5 f 1 g 8 h 4 :20 a 4 b 8 c 1 d 5 e 7 f 2 g 6 h 3 :45 a 6 b 3 c 1 d 8 e 5 f 2 g 4 h 7 :70 a 6 b 3 c 7 d 2 e 4 f 8 g 1 h 5 :73
6
a 2 b 6 c 1 d 7 e 4 f 8 g 3 h 5 :8 a 5 b 3 c 8 d 4 e 7 f 1 g 6 h 2 :56 a 7 b 3 c 8 d 2 e 5 f 1 g 6 h 4 :85 a 3 b 1 c 7 d 5 e 8 f 2 g 4 h 6 :13 +(6) a 3 b 5 c 7 d 1 e 4 f 2 g 8 h 6 :16 a 6 b 8 c 2 d 4 e 1 f 7 g 5 h 3 :80 a 4 b 6 c 1 d 5 e 2 f 8 g 3 h 7 :37 a 6 b 4 c 2 d 8 e 5 f 7 g 1 h 3 :77
7
a 2 b 6 c 8 d 3 e 1 f 4 g 7 h 5 :9 a 5 b 7 c 4 d 1 e 3 f 8 g 6 h 2 :62 a 7 b 3 c 1 d 6 e 8 f 5 g 2 h 4 :84 a 5 b 1 c 4 d 6 e 8 f 2 g 7 h 3 :47 +(7) a 6 b 2 c 7 d 1 e 3 f 5 g 8 h 4 :66 a 4 b 8 c 5 d 3 e 1 f 7 g 2 h 6 :46 a 4 b 2 c 5 d 8 e 6 f 1 g 3 h 7 :31 a 3 b 7 c 2 d 8 e 6 f 4 g 1 h 5 :27
8
a 2 b 7 c 3 d 6 e 8 f 5 g 1 h 4 :10 a 4 b 1 c 5 d 8 e 6 f 3 g 7 h 2 :30 a 7 b 2 c 6 d 3 e 1 f 4 g 8 h 5 :83 a 7 b 1 c 3 d 8 e 6 f 4 g 2 h 5 :81 +(8) a 4 b 7 c 5 d 3 e 1 f 6 g 8 h 2 :43 a 2 b 8 c 6 d 1 e 3 f 5 g 7 h 4 :12 a 5 b 8 c 4 d 1 e 3 f 6 g 2 h 7 :63 a 5 b 2 c 4 d 6 e 8 f 3 g 1 h 7 :50
9
a 2 b 7 c 5 d 8 e 1 f 4 g 6 h 3 :11 a 3 b 6 c 4 d 1 e 8 f 5 g 7 h 2 :21 a 7 b 2 c 4 d 1 e 8 f 5 g 3 h 6 :82 a 5 b 1 c 8 d 6 e 3 f 7 g 2 h 4 :49 +(9) a 5 b 7 c 2 d 6 e 3 f 1 g 8 h 4 :61 a 4 b 8 c 1 d 3 e 6 f 2 g 7 h 5 :44 a 6 b 3 c 5 d 8 e 1 f 4 g 2 h 7 :72 a 4 b 2 c 7 d 3 e 6 f 8 g 1 h 5 :32
10
a 3 b 5 c 2 d 8 e 1 f 7 g 4 h 6 :14 a 6 b 4 c 7 d 1 e 8 f 2 g 5 h 3 :79 a 6 b 4 c 7 d 1 e 8 f 2 g 5 h 3 :79 a 5 b 3 c 1 d 7 e 2 f 8 g 6 h 4 :55 +(12) a 5 b 3 c 1 d 7 e 2 f 8 g 6 h 4 :55 a 4 b 6 c 8 d 2 e 7 f 1 g 3 h 5 :38 a 3 b 5 c 2 d 8 e 1 f 7 g 4 h 6 :14 a 4 b 6 c 8 d 2 e 7 f 1 g 3 h 5 :38
11
a 3 b 5 c 8 d 4 e 1 f 7 g 2 h 6 :17 a 6 b 2 c 7 d 1 e 4 f 8 g 5 h 3 :67 a 6 b 4 c 1 d 5 e 8 f 2 g 7 h 3 :76 a 5 b 7 c 1 d 4 e 2 f 8 g 6 h 3 :58 +(10) a 6 b 3 c 1 d 7 e 5 f 8 g 2 h 4 :68 a 4 b 2 c 8 d 5 e 7 f 1 g 3 h 6 :35 a 3 b 7 c 2 d 8 e 5 f 1 g 4 h 6 :26 a 3 b 6 c 8 d 2 e 4 f 1 g 7 h 5 :25
12
a 3 b 6 c 2 d 5 e 8 f 1 g 7 h 4 :18
a 4 b 7 c 1 d 8 e 5 f 2 g 6 h 3 :40
a 6 b 3 c 7 d 4 e 1 f 8 g 2 h 5 :75
a 6 b 3 c 1 d 8 e 4 f 2 g 7 h 5 :69 +(11)
a 4 b 2 c 7 d 5 e 1 f 8 g 6 h 3 :34
a 3 b 6 c 8 d 1 e 5 f 7 g 2 h 4 :24
a 5 b 2 c 8 d 1 e 4 f 7 g 3 h 6 :53
a 5 b 7 c 2 d 4 e 8 f 1 g 3 h 6 :59
Вот как выглядит это разделение.Причём сумма номеров позиций
в каждой группе :372.Группа номер 10 – симметрическая.
В этой группе при повороте на 180 градусов позиции переходят сами в себя. Характерной особенностью симметрических положений является пустой квадрат 4*4 в середине доски и пустые диагонали. Таким образом,имея 12 расположений — по одному из каждой группы, можно получить все остальные,что,впрочем,известно из шахматной литературы: (Е.Я.Гик:Шахматы и математика,Наука,Москва 1983). В списке крестиком отмечены позиции,которые выбраны в качестве базовых (т.е. тех,с которыми производятся преобразования симметрии) в английской и украинской версии данной статьи в Википедии и указаны номера, под которыми они в ней фигурируют. Здесь же базовые позиции (первые позиции в каждой группе) выбраны по признаку минимального номера в группе. Очевидно, что наборов базовых позиций можно составить (8^11)*4.
[править] Вариант решения для доски произвольного размера на C#
Ниже приведен только класс, непосредственно реализующий алгоритм решения. (Полный код программы можно скачать по ссылке: http://adels.zp.ua/other/queens_cs.zip).
class QueensProblem : IProblem { private int size; private int[] positions; private List<int[]> saved; private IProblemCallback cb; public void Init(int size, IProblemCallback cb) { this.size = size; this.cb = cb; positions = new int[size]; saved = new List<int[]>(); } private void Save() { saved.Add((int[]) positions.Clone()); } private bool Step() { int s = size; for (int i = 1; i < s; i++) { int pos = positions[i]; while (true) { bool found = false; for (int j = 0; j < i; j++) { int epos = positions[j]; if (epos == pos || pos - i == epos - j || pos + i == epos + j) { if (++pos >= s) { if (!Next(i)) return false; while (++i < s) positions[i] = 0; return true; } positions[i] = pos; found = true; break; } } if (!found) break; } } Save(); return Next(s - 1); } private bool Next(int pos) { int s = size; if (++positions[pos] < s) return true; do { positions[pos] = 0; if (++positions[--pos] < s) return true; } while (pos > 0); return false; } public int Solve() { float prev = 0; int sq = size * size; while (Step()) { int part = positions[0] * size + positions[1]; if (part != prev) cb.Progress((float) (prev = part) / sq); } return saved.Count; } public List<int[]> GetResults() { return saved; } public int Size { get { return size; } } }
[править] Время выполнения алгоритма
Ниже представлена зависимость времени выполнения программы от размера доски (время приведено для компьютера с процессором Intel Core2 Q6600 @ 2.4 ГГц):
| Размер | Решений | Время (мс) |
|---|---|---|
| 4 | 2 | 1 |
| 5 | 10 | 2 |
| 6 | 4 | 2 |
| 7 | 40 | 2 |
| 8 | 92 | 3 |
| 9 | 352 | 6 |
| 10 | 724 | 16 |
| 11 | 2 680 | 69 |
| 12 | 14 200 | 376 |
| 13 | 73 712 | 2 311 |
| 14 | 365 596 | 15 411 |
| 15 | 2 279 184 | 108 587 |
| 16 | 14 772 512 | 812 945 |
Характер зависимости времени выполнения от размера доски — экспоненциальный (как показано на графике).
[править] Вариант решения для доски размера до 10-11 на Oracle 11 SQL
В Oracle SQL возможно решение одним рекурсивным оператором. Время выполнения - секунды для доски 8*8, до 1 минуты для доски 9*9.
Использован усовершенствованный метод поиска с возвратом:
На доске есть четыре типа ресурсов: вертикали, горизонтали, левые и правые диагонали. Каждая поставленная фигура на доску "потребляет" одну горизонталь, одну вертикаль, и две диагонали. Соответственно, следующего ферзя мы будем пробовать ставить на ту клетку, для которого есть оставшиеся незанятые горизонталь, вертикаль и диагонали. Этим существенно сокращаем время перебора.
WITH T1 AS (SELECT level - 1 AS K FROM dual CONNECT BY level <= &&D) , T2 (N, L, X, Y, D1, D2, NL) AS (SELECT a.K * &D + b.K, 0 , POWER (2, a.K), POWER (2, b.K), POWER (2, a.K - b.K + &D - 1) , POWER (2, a.K + b.K), chr (a.K + 97) || to_char (b.K + 1) FROM T1 a, T1 b) , T3 (RN, RL, RX, RY, RD1, RD2, RNL) AS (SELECT * FROM T2 UNION ALL SELECT N, RL + 1, RX + X, RY + Y, RD1 + D1, RD2 + D2, RNL || ' ' || NL FROM T2, T3 WHERE bitand (RX , X ) = 0 AND bitand (RY , Y ) = 0 AND bitand (RD1, D1) = 0 AND bitand (RD2, D2) = 0 AND N > RN ) SELECT RNL FROM T3 WHERE RL = &D - 1;
[править] Рекурсивное решение для доски произвольного размера на C++
// Backtracking method. "Поиск с возвратом" на примере // задачи о восьми ферзях. #include <iostream> const int SIZE = 8; // Размер. int board[SIZE][SIZE]; int results_count = 0; // Количество решений. // Функция showBoard() - отображает доску. void showBoard() { for(int a = 0; a < SIZE; ++a) { for(int b = 0; b < SIZE; ++b) { std::cout << ((board[a][b]) ? "Q " : ". "); } std::cout << '\n'; } } // Функция tryQueen() - проверяет нет ли уже установленных ферзей, // по вертикали, диагоналям. bool tryQueen(int a, int b) { for(int i = 0; i < a; ++i) { if(board[i][b]) { return false; } } for(int i = 1; i <= a && b-i >= 0; ++i) { if(board[a-i][b-i]) { return false; } } for(int i = 1; i <= a && b+i < SIZE; i++) { if(board[a-i][b+i]) { return false; } } return true; } // Функция setQueen() - пробует найти результаты решений. void setQueen(int a) // a - номер очередной строки в которую нужно поставить очередного ферзя. { if(a == SIZE) { showBoard(); std::cout << "Result #" << ++results_count << "\n\n"; return; // Опционально. } for(int i = 0; i < SIZE; ++i) { // Здесь проверяем, что если поставим в board[a][i] ферзя (единицу), // то он будет единственным в этой строке, столбце и диагоналях. if(tryQueen(a, i)) { board[a][i] = 1; setQueen(a+1); board[a][i] = 0; } } return; // Опционально. } int main() { setQueen(0); return 0; }
[править] Альтернативный подход
Если решать более общую задачу об N ферзях при помощи описанного выше алгоритма, то такой перебор вариантов даже с использованием описанных выше сокращений будет весьма долго работать уже при N = 20, так как 20! = 2.433·1018. Поэтому представляет особенный интерес следующий эвристический алгоритм, решающий задачу об N ферзях на поле размером N x N. Он работает для всех N ≥ 4 или N = 1:
- Разделить N на 12 и запомнить остаток (N будет равно 8 для задачи о восьми ферзях).
- Занести в список все четные числа от 2 до N по порядку.
- Если остаток равен 3 или 9, перенести 2 в конец списка.
- Добавить в список все нечетные числа от 1 до N по порядку, но, если остаток равен 8, перевернуть пары соседних чисел (например: 3, 1, 7, 5, 11, 9, …).
- Если остаток равен 2 и N ≥ 3, поменять местами 1 и 3, затем, если N ≥ 5, перенести 5 в конец списка.
- Если остаток равен 3 или 9, переместить 1 и 3 (именно в этом порядке, а не в котором они сейчас) в конец списка.
- Разместить ферзя в первом столбце и в строке с номером, равным первому элементу списка, затем поместить следующего ферзя во втором столбце и в строке с номером, равным второму элементу списка, и т.д.
Для N = 8 результат работы этого алгоритма показан на картинке.
Еще несколько примеров:
- 14 ферзей (остаток 2): 2, 4, 6, 8, 10, 12, 14, 3, 1, 7, 9, 11, 13, 5.
- 15 ферзей (остаток 3): 4, 6, 8, 10, 12, 14, 2, 5, 7, 9, 11, 13, 15, 1, 3.
- 20 ферзей (остаток 8): 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 3, 1, 7, 5, 11, 9, 15, 13, 19, 17.
[править] Реализация на C++
#include <iostream> #include <vector> #include <algorithm> int main() { int N, Mod; std::vector < int > L; std::cin >> N; Mod = N % 12; for ( int x = 2; x <= N; x += 2 ) L.push_back( x ); if ( Mod == 3 || Mod == 9 ) { L.push_back( 2 ); L.erase( L.begin() ); } if ( Mod == 8 ) { std::vector < int > V; for ( int x = 1; x <= N; x += 2 ) V.push_back( x ); for ( int i = 1; i < V.size(); i += 2 ) std::swap( V.at( i ), V.at( i - 1 ) ); for ( int i = 0; i < V.size(); ++i ) L.push_back( V.at( i ) ); } else for ( int x = 1; x <= N; x += 2 ) L.push_back( x ); if ( Mod == 2 && N >= 3 ) { std::swap( *std::find( L.begin(), L.end(), 1 ), *std::find( L.begin(), L.end(), 3 ) ); if ( N >= 5 ) { L.erase( std::find( L.begin(), L.end(), 5 ) ); L.push_back( 5 ); } } if ( Mod == 3 || Mod == 9 ) { L.erase( std::find( L.begin(), L.end(), 1 ) ); L.push_back( 1 ); L.erase( std::find( L.begin(), L.end(), 3 ) ); L.push_back( 3 ); } for ( int i = 0; i < L.size(); ++i ) std::cout << L.at( i ) << " "; return 0; }
[править] Реализация на Haskell
position n = evenPosition ++ oddPosition where modN = n `mod` 12 evenPosition | modN == 3 || modN == 9 = [4,6..n] ++ [2] | otherwise = [2,4..n] oddPosition = oneThree $ fiveSeven others where others | modN == 8 = swapPairs [9,11..n] | otherwise = [9,11..n] where swapPairs [] = [] swapPairs [x] = [x] swapPairs (y:x:xs) = x:y:swapPairs xs fiveSeven | modN == 2 && n >= 5 = ([7] ++) . (++ [5]) | modN == 8 = ([7,5] ++) | n < 7 = ([5,7..n] ++) | otherwise = ([5,7] ++) oneThree | modN == 3 || modN == 9 = (++ [1,3]) | modN == 8 || (modN == 2 && n >= 3) = ([3,1] ++) | n < 3 = ([1] ++) | otherwise = ([1,3] ++) main = print $ map position [8,14,15,20] -- [[2,4,6,8,3,1,7,5], -- [2,4,6,8,10,12,14,3,1,7,9,11,13,5], -- [4,6,8,10,12,14,2,5,7,9,11,13,15,1,3], -- [2,4,6,8,10,12,14,16,18,20,3,1,7,5,11,9,15,13,19,17]]
[править] Реализация на Erlang
pmut([])-> [[]]; pmut(A) -> [[H|T] || H <- A, T <- pmut(A--[H])]. start() -> N = 8, % size of board / num of queens (N>=4) ListP = pmut(lists:seq(1,N)), [Q || Q <- ListP, length(lists:usort([lists:nth(X,Q)+X || X <- Q])) == N, length(lists:usort([lists:nth(X,Q)-X || X <- Q])) == N]
| Это заготовка статьи о шахматах. Вы можете помочь проекту, исправив и дополнив её. |
| Это заготовка статьи по математике. Вы можете помочь проекту, исправив и дополнив её. |
[править] Ссылки
- Фрактальное решение задачи для досок N-N, если N=(a1)•(a2)•...•(an), где а1 ≥ 4 и а2,...,аn > 4 (А.Ермаков, 2011)

