Рекурсивный поиск по дереву - PullRequest
5 голосов
/ 05 мая 2011

Принимая во внимание сетку 5x5, требуется записать все возможные комбинации ходов коней, от каждого начального квадрата до каждого последующего квадрата.

Я предполагал, что это будет структура, подобная бинарному дереву, но, поскольку каждый квадрат на доске может иметь более 2 потенциальных следующих ходов, я не думаю, что это сработает. Я изучил алгоритмы A * / Pathfinding, но все они требуют, чтобы конечный узел работал с тем, что я вижу, и я не знаю конечный узел, так как он работает каждый раз и будет другим.

Мой псевдокод до сих пор:

For each square on the board
    Check if this key has potential moves
        If Potential moves
            <Some way of selecting a next move (this could be the square we just originated from too!)>
            Register this move into a collection we can check against for subsequent                             moves
            Recursively call function with the square we just landed on 
        Else Continue
End

Буду очень признателен за любые советы / указатели, так как я очень потерян!

Ответы [ 3 ]

3 голосов
/ 05 мая 2011

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

Нерекурсивная версия: Вам нужен список списков позиций (называемый списком позиций), который будет вашим окончательным ответом, я назову этот список списков маршрутов.

Создайте один список для каждой начальной позиции и поместите их все в список маршрутов.

While the routes list has a position list that's less than the required length
{
    Get a position list that's too short.
    Remove it from the routes list
    Create new position lists that are a copy of the list we just removed + a possible next position from the last position in the list.
    Add those lists to the routes list.
}

РЕДАКТИРОВАТЬ: Рекурсивная версия:

using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static int GridSize = 5;

        static void Main(string[] args)
        {
            List<List<Point>> Positions = (from X in Enumerable.Range(0, GridSize)
                                           from Y in Enumerable.Range(0, GridSize)
                                           select new List<Point>() { new Point(X, Y) }).ToList();

            var PossibleRoutes = WalkGraph(Positions, 5);
        }

        static List<List<Point>> WalkGraph(List<List<Point>> RoutesList, int DesiredLength)
        {
            List<List<Point>> Result = new List<List<Point>>();

            foreach (var Route in RoutesList)
            {
                if (Route.Count < DesiredLength)
                {
                    // Extend the route (produces a list of routes) and recurse
                    Result.AddRange(WalkGraph(ExtendRoute(Route), DesiredLength));
                }
                else
                {
                    Result.Add(Route);
                }
            }

            return Result;
        }

        static List<List<Point>> ExtendRoute(List<Point> Route)
        {
            List<List<Point>> NextMoveRoutes = new List<List<Point>>();

            // Itterate through each possible move
            foreach (var NextMove in PossibleMoves(Route.Last()))
            {
                // Create a copy of the route, and add this possible move to it.
                List<Point> NextMoveRoot = new List<Point>(Route);
                NextMoveRoot.Add(NextMove);
                NextMoveRoutes.Add(NextMoveRoot);
            }

            return NextMoveRoutes;
        }

        static List<Point> PossibleMoves(Point P)
        {
            // TODO Generate a list of possible places to move to

            List<Point> Result = new List<Point>();

            Result.Add(new Point(P.X + 1, P.Y + 2));
            Result.Add(new Point(P.X - 1, P.Y + 2));
            Result.Add(new Point(P.X + 1, P.Y - 2));
            Result.Add(new Point(P.X - 1, P.Y - 2));

            Result.Add(new Point(P.X + 2, P.Y + 1));
            Result.Add(new Point(P.X - 2, P.Y + 1));
            Result.Add(new Point(P.X + 2, P.Y - 1));
            Result.Add(new Point(P.X - 2, P.Y - 1));

            Result.RemoveAll(PossibleMove => PossibleMove.X < 0 || PossibleMove.X > GridSize ||
                                             PossibleMove.Y < 0 || PossibleMove.Y > GridSize);

            return Result;
        }
    }
}

РЕДАКТИРОВАТЬ: Ниже приведена версия, использующая IEnumerable, чтобы исключить начальное время вычислений и значительно сократить объем памяти.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static int GridSize = 5;

        static void Main(string[] args)
        {
            IEnumerable<IEnumerable<Point>> Positions = from X in Enumerable.Range(0, GridSize)
                                                        from Y in Enumerable.Range(0, GridSize)
                                                        select new List<Point>() { new Point(X, Y) } as IEnumerable<Point>;

            var PossibleRoutes = WalkGraph(Positions, 100);

            foreach (var Route in PossibleRoutes)
            {
                Console.WriteLine(Route.Select(P => P.ToString()).Aggregate((curr, next) => curr + " " + next));
                //Thread.Sleep(500); // Uncomment this to slow things down so you can read them!
            }

            Console.ReadKey();
        }

        static IEnumerable<IEnumerable<Point>> WalkGraph(IEnumerable<IEnumerable<Point>> RoutesList, int DesiredLength)
        {
            foreach (var Route in RoutesList)
            {
                if (Route.Count() < DesiredLength)
                {
                    // Extend the route (produces a list of routes) and recurse
                    foreach (var ExtendedRoute in WalkGraph(ExtendRoute(Route), DesiredLength))
                        yield return ExtendedRoute;
                }
                else
                {
                    //Result.Add(Route);
                    yield return Route;
                }
            }
        }

        static IEnumerable<IEnumerable<Point>> ExtendRoute(IEnumerable<Point> Route)
        {
            // Itterate through each possible move
            foreach (var NextMove in PossibleMoves(Route.Last()))
            {
                // Create a copy of the route, and add this possible move to it.
                List<Point> NextMoveRoute = new List<Point>(Route);
                NextMoveRoute.Add(NextMove);
                yield return NextMoveRoute;
            }
        }

        static IEnumerable<Point> PossibleMoves(Point P)
        {
            List<Point> Result = new List<Point>();

            Result.Add(new Point(P.X + 1, P.Y + 2));
            Result.Add(new Point(P.X - 1, P.Y + 2));
            Result.Add(new Point(P.X + 1, P.Y - 2));
            Result.Add(new Point(P.X - 1, P.Y - 2));

            Result.Add(new Point(P.X + 2, P.Y + 1));
            Result.Add(new Point(P.X - 2, P.Y + 1));
            Result.Add(new Point(P.X + 2, P.Y - 1));
            Result.Add(new Point(P.X - 2, P.Y - 1));

            Result.RemoveAll(PossibleMove => PossibleMove.X < 0 || PossibleMove.X > GridSize ||
                                             PossibleMove.Y < 0 || PossibleMove.Y > GridSize);

            return Result as IEnumerable<Point>;
        }
    }
}
1 голос
/ 05 мая 2011

Хотя поиск в глубину будет работать, я думаю, что вы можете сделать это лучше (быстрее) с помощью поиска в ширину, так как на самом деле вам не нужно генерировать ходы, вам просто нужно подсчитать количество ходов.

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

На итерации 0 матрица является просто матрицей, поскольку вы еще не переместили свои кусочки:

    table0

   1 1 1 1
   1 1 1 1
   1 1 1 1
   1 1 1 1

Давайте назовем таблицу выше table0, так как это 0-й ход. Чтобы получить table1 из table0, вы делаете table1[r1c1] = table0[r2c3] + table0[r3c2], поскольку r1c1 может быть достигнут только рыцарями в r2c3 и r3c2, еще один пример, чтобы найти table1[r2c2] = table0[r1c4] + table0[r3c4] + table0[r4c3] + table0[r4c1], поскольку r2c2 может быть достигнут только рыцарями в r1c4, r3c4, r4c3, r4c1. И так далее.

Используя этот алгоритм, следующие несколько значений таблицы таковы:

    table1

   2 3 3 2
   3 4 4 3
   3 4 4 3
   2 3 3 2




    table2

   8 10 10  8
  10 10 10 10
  10 10 10 10
   8 10 10  8




    table3

  20 30 30 20
  30 36 36 30
  30 36 36 30
  20 30 30 20




    table4

  72  96  96 72
  96 100 100 96
  96 100 100 96
  72  96  96 72



    table5

  200 292 192 200
  192 336 336 192
  192 336 336 192
  200 192 192 200

Так что в основном в этой игре 4x4 это будет формула для расчета следующей сетки:

last = table from previous iteration
next = create new table of zeros

// corners
next[r1c1] = last[r2c3] + last[r3c2]
next[r1c4] = last[r2c2] + last[r3c3]
next[r4c1] = last[r2c3] + last[r3c2]
next[r4c4] = last[r3c3] + last[r3c3]

// sides, clockwise
next[r1c2] = last[r3c1] + last[r3c3] + last[r2c4]
next[r1c3] = last[r3c2] + last[r3c4] + last[r2c1]
next[r2c4] = last[r1c2] + last[r3c2] + last[r4c3]
next[r3c4] = last[r2c2] + last[r4c2] + last[r1c3]
next[r4c3] = last[r2c2] + last[r2c4] + last[r3c1]
next[r4c2] = last[r2c1] + last[r2c3] + last[r3c4]
next[r3c1] = last[r2c3] + last[r4c3] + last[r1c2]
next[r2c1] = last[r1c3] + last[r3c3] + last[r4c2]

// middle four
next[r2c2] = last[r1c4] + last[r3c4] + last[r4c1] + last[r4c3]
next[r2c3] = last[r1c1] + last[r3c1] + last[r4c2] + last[r4c4]
next[r3c2] = last[r1c1] + last[r1c3] + last[r2c4] + last[r4c4]
next[r3c3] = last[r2c1] + last[r4c1] + last[r1c2] + last[r1c4]

Это займет постоянную память O (1), а количество операций является линейным O (n) в зависимости от количества ходов. Примечание: вам нужно проверить, что этот алгоритм действительно работает, я не особо задумывался о том, правильно ли он рассчитывает счет.

0 голосов
/ 05 мая 2011

Так что это можно сделать с помощью DFS.Я совершенно уверен, что есть более быстрый путь, поскольку DFS генерирует каждый путь, и есть O (2 ^ {count}) путей.Вот некоторый код на Python, позволяющий использовать DFS для каждого пути из каждой начальной точки

def extended_knight_tours(width, height, count):
    start_x, start_y = -1, -1

    def dfs(x, y, moves_left):
        if not (0 <= x < width and 0 <= y < height):
            return 0
        if moves_left == 0:
            if (start_x, start_y) == (x, y):
                return 1
            else:
                return 0

        knight_moves = [(1, 2), (-1, 2), (1, -2), (-1, -2),
                        (2, 1), (2, -1), (-2, 1), (-2, -1)]

        return sum(dfs(x + dx, y + dy, moves_left - 1)
                   for dx, dy in knight_moves)

    ans = 0
    for x in range(width):
        for y in range(height):
            start_x = x
            start_y = y
            ans += dfs(x, y, count)

    return ans

Простой компромисс между временем и пространством, который вы можете сделать, чтобы ускорить это, - просто запомните DFS (не забудьте очистить кеш длякаждая начальная позиция).

Поиграв с этой функцией, я заметил, что для каждого нечетного числа ответ равен нулю.Таким образом, более быстрый алгоритм - это найти количество путей с количеством длин / 2 (не обязательно маршрутов) для каждой начальной позиции.Тогда число путей с положением в качестве средней точки можно рассчитать, используя значения count / 2, но я оставлю это в качестве упражнения для читателя:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...