【问题标题】:Recursive tree search递归树搜索
【发布时间】:2011-05-05 11:39:04
【问题描述】:

考虑到 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

任何建议/指针将不胜感激,因为我很迷茫!

【问题讨论】:

  • 您可能想要查看 LinkedList 数据结构。您是否曾与您的教授核对过,以验证您是否偶然使用 peeudocode 走在正确的轨道上?由于我以前从未做过这样的项目,究竟是什么使移动有效,只是网格上的一个正方形与骑士所在的正方形相连?
  • 你是否被要求列出所有的旅行,或者计算到达每个广场的路数,或者究竟是什么?如果你被要求列出所有的旅行,你列出它们的顺序是否重要?

标签: c# algorithm


【解决方案1】:

好的,正如 cmets 所暗示的那样,可能的移动序列将会非常多。 这是我的想法,所以请耐心等待。

非递归版本:您需要一个职位列表(称为职位列表),这将是您的最终答案,我将该列表称为路线列表。

为每个起始位置创建一个列表,并将它们全部放入路线列表中。

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

【讨论】:

  • 在我看到你编辑的帖子之前发表了评论,让我看看,我会重新评论
  • 非常感谢,这种迭代方式似乎确实有效(尽管一旦达到一定数量的序列,内存使用量就会很大,但出于显而易见的原因 - 存在大量递归继续)。
  • @Gary,我已经编辑了我的答案以包含一个使用 IEnumerable 的版本。根据您的使用情况,它可能会消除内存使用问题。
【解决方案2】:

虽然深度优先搜索会起作用,但我认为您可以通过广度优先搜索做得更好(更快),因为您实际上不需要生成移动,您只需要计算移动的数量。

如果您在进行第 (n-1) 次运动时可以创建一个包含可能分支计数的矩阵,那么您可以使用它来计算第 n 次运动的可能分支计数。

在第 0 次迭代中,矩阵只是一个矩阵,因为你还没有移动你的棋子:

    table0

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

让我们将表格命名为 table0,因为这是第 0 步。要从table0 中获取table1,请执行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),具体取决于移动的数量。注意:你需要检查这个算法是否真的有效,我没有考虑它是否正确计算计数。

【讨论】:

  • 我非常喜欢这种方法。为了使它适用于任何大小,您可以循环遍历表中的每个点,并为该点的每个可能移动增加目标点。我不确定这正是 OP 所追求的,因为这只会给你在所有路线上遇到每个位置的次数。我有一种唠叨的感觉,这个想法可以用来解决问题,但我无法完全理解它。
【解决方案3】:

所以这可以通过 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 值计算以位置为中点的路径数,但我将把它作为练习留给读者:)

【讨论】:

    猜你喜欢
    • 2017-09-26
    • 2014-01-02
    • 2021-07-03
    • 2019-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    相关资源
    最近更新 更多