2011-05-05 223 views
5

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

任何意見/指針將不勝感激,因爲我很失落!

+0

您可能希望查看LinkedList數據結構。你有沒有與你的教授覈對,以確認你是否在正確的軌道與偷窺代碼碰巧?因爲我之前從未做過這樣的項目,究竟是什麼使移動成爲有效的,只是連接到正方形的網格上的正方形是正確的? – 2011-05-05 11:56:08

+0

您是否被要求列出所有旅行團,或者統計到達每個廣場的方式數量,或者究竟是什麼?如果你被要求列出所有的旅行團,那麼你列出他們的順序是否有關係? – 2011-05-05 12:01:33

回答

3

好的,會有極端數量的可能的動作序列,正如評論所暗示的那樣。 這是從我的頭頂,我是如此裸露....

非遞歸版本:你需要的位置列表的列表(稱爲位置列表),這將是你最後的答案,我會將該列表稱爲路由列表。

爲每個起始位置創建一個列表,並將它們全部放入路由列表中。

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

在我看到您編輯的文章之前發表了評論,讓我看看它,然後我會重新發表評論 – Gary 2011-05-05 13:06:03

+0

非常感謝您,這種迭代方式確實可行(儘管一旦您達到一定數量的序列,但出於顯而易見的原因 - 有一噸的遞歸正在進行)。 – Gary 2011-05-05 14:04:24

+0

@加里,我編輯了我的答案,包括使用IEnumerable的版本。根據您的使用情況,它可能會刪除內存使用問題。 – 2011-05-05 14:19:54

1

雖然深度優先搜索會的工作,我認爲你可以做到這一點更好(快)與廣度優先搜索,因爲你實際上並不需要生成動作,你只需要數數的舉動。

如果您可以在第(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)。注意:你需要檢查這個算法是否真正起作用,我還沒有考慮過它是否正確計算了計數。

+0

我很喜歡這種方法。爲了使其適用於任何規模,您可以遍歷表格中的每個點,併爲該點可能的移動中的每個點遞增目標點。我不確定這是OP之後的情況,因爲這隻會給你在所有路線上遇到的每個位置的次數。我有一個嘮叨的感覺,這個想法可以適應解決這個問題,但我不能很好地解決這個問題。 – 2011-05-05 15:20:38

0

所以這可以通過DFS來完成。我相當肯定,自從DFS生成每條路徑後,有更快的方法,並且有O(2^{count})個路徑。這裏是剛剛DFS從每一個出發點的每一條路徑一些Python代碼

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 

一個簡單的空間與時間的權衡,你可以加快這只是memoize的的DFS(記得要清除緩存爲每個啓動位置)。

從玩這個功能,我注意到,每個奇數的答案是零。因此,一個可能更快的算法是找到每個起始位置的長度計數/ 2(不一定是路由)的路徑的數量。然後,可以使用count/2值計算位置爲中點的路徑數量,但我會將其作爲練習留給讀者:)