Поэтому я придумал эту реализацию для решения тура рыцарей для шахматной доски 8 * 8. Но похоже, что он занимает много времени (так долго, что я должен его остановить). Но если я заменил dx, dy-массивы на один, написанный в комментариях, и программа работает как магия и дает результат. Говорят, что он умело выбирает массив, так что агенту bruteforce повезло быстро найти решение.
Но как же придумать этот массив на первом месте, этот массив (dx, dy) я получил от какого-то другого кода. Так может ли кто-нибудь объяснить мне, почему этот код работает с этими массивами (в комментарии), а не с моим.
#include <cstdio>
using namespace std;
int dx[8] = { 1, 1, 2, 2, -1, -1, -2, -2};
int dy[8] = { 2, -2, 1, -1, 2, -2, 1, -1};
//int dx[8] = { 2, 1, -1, -2, -2, -1, 1, 2 };
//int dy[8] = { 1, 2, 2, 1, -1, -2, -2, -1 };
bool solve(bool arr[8][8],int x,int y,int moves){
if(moves==0)return true;
if(x<0 || y<0 || x>7 || y>7 || arr[x][y])return false;
arr[x][y]=1;
for(int i=0;i<8;i++)
if(solve(arr,x+dx[i],y+dy[i],moves-1)){
printf(" (%d,%d) ",x,y);
return 1;
}
arr[x][y]=0;
return false;
}
int main()
{
bool arr[8][8];
for(int i=0;i<8;i++) for(int j=0;j<8;j++) arr[i][j]=0;
solve(arr,0,0,64);
puts("");
}
Причина, по которой закомментированный массив dx/dy
работает лучше, чем ваш начальный массив, заключается в том, что он выполняет поиск по глубине для решения в другом порядке - порядок, выбранный с учетом конкретного решения и который поэтому способен найти это решение относительно быстро.
Поиск по глубине начинается с корня дерева и проверяет каждый путь к листу. Например, поиск в глубину этого дерева сначала рассмотрим путь, который посещает только a
узлы (a → a → a
), то он будет возвращаться назад немного и исследовать a → a → b
, то a → a → c
и т.д.
Это может занять много времени, если дерево велико и нет решений, которые начинаются с посещения a
, потому что вам приходится тратить много времени на изучение всех путей, начинающихся с a
прежде чем вы сможете перейти к лучшим путям.
Если вам известно, что есть хорошее решение, начинающееся с d
, вы можете немного ускорить процесс, переупорядочив узлы вашего дерева, чтобы начать с изучения путей, начинающихся с d
:
С самого начала вы удалили 7/8th работы, которую должна выполнить ваша программа, потому что вам никогда не придется искать пути, начинающиеся с чего-то другого, кроме d
! Выбирая хороший порядок для остальных узлов, вы можете получить аналогичные ускорения.
Вы можете видеть, что это происходит, если вы посмотрите на результаты своей программы:
(0,7) (1,5) (3,4) (1,3) (0,1) (2,0) (4,1) (6,0) (7,2) (5,3) (7,4)
(6,2) (7,0) (5,1) (4,3) (3,1) (5,0) (7,1) (5,2) (7,3) (6,1) (4,0)
(3,2) (4,4) (2,3) (0,2) (1,0) (2,2) (3,0) (1,1) (0,3) (2,4) (1,2)
(0,4) (1,6) (3,7) (2,5) (3,3) (5,4) (6,6) (4,5) (6,4) (7,6) (5,5)
(4,7) (2,6) (0,5) (1,7) (3,6) (5,7) (6,5) (7,7) (5,6) (3,5) (1,4)
(0,6) (2,7) (4,6) (6,7) (7,5) (6,3) (4,2) (2,1) (0,0)
Первый шаг (чтение снизу) - от (0,0)
до (2,1)
, что соответствует dx=2
и dy=1
- и, конечно, в прокомментированном списке dx/dy
, что первая возможность, которая рассматривается. На самом деле, первые три шага этого решения используют dx=2
и dy=1
, что фактически означает, что вам нужно искать только крошечное небольшое дерево, а не целое.
В шахматах есть восемь действительных рыцарских ходов:
Два массива перечисляют эти восемь ходов.
В двух версиях кода попробуйте двигаться в другом порядке. Так получилось, что одна версия встречается с правильным решением гораздо быстрее, чем другая.
Это все, что есть.