堆栈溢出OCaml递归解决方案骑士在棋盘拼图上的最短路径

时间:2012-03-28 05:53:59

标签: algorithm functional-programming ocaml

问题如下所述:Knight's Shortest Path Chess Question。我尝试使用直观的递归方法解决它,如下所示:

let is_in_list x s = List.exists (fun y -> if y = x  then true else false) s;; (* find out if an element belongs to a list *)

let next_step n = [n+10;n-6;n+6;n-10;n+17;n-17;n+15;n-15];;

let rec legal_next_step = function
  |[]->[]
  |x::s-> 
    if x<0 or x>63 then legal_next_step s
    else x::legal_next_step s;;

let find_next_step n = legal_next_step (next_step n);; (* here is to find all the valid next moves and put them into a list*)

let rec min s = 
  let rec loop result = function
    |[]->result;
    |x::s-> 
      if x < result then loop x s
      else loop result s in
  loop (List.hd s) s;;

let rec find_shortest_path n m =
  if is_in_list m (next_step n) then 1
  else
    1 + min (List.map (fun x -> find_shortest_path x m) (find_next_step n)) ;;

这会导致“堆栈溢出”问题。这是为什么?我的程序是否在堆栈中创建了太多的递归调用层?或者我的代码出现了严重问题。

2 个答案:

答案 0 :(得分:9)

当你写

时,你必须明白
List.map (fun x -> find_shortest_path x m) (find_next_step n))

你将从所有可能的邻居计算所有“来自这里的最短路径” - 然后计算所有这些结果的最小值。

因此代码中存在无限循环:如果从位置A开始并尝试计算其邻居B之一的最短路径,则find_shortest_path从{如果他的第一步是回到B,那么{1}}将不可避免地试图看看这条路有多长。因此,在所有其他可能尝试的移动中,您将计算循环A的“长度”,即无限循环。

有几种方法可以避免这个问题(这与OCaml编程本身无关,这是程序逻辑中的错误,可以在任何语言中显示):

  • 使用breadth-first search代替深度优先搜索,以便逐步探索给定长度的所有路径,停在您找到的最小获胜路径上;如果你愿意,这对应于并行探索所有路径,所以只要你在尝试搜索整个无限路径之前停止(因为你找到了另一个解决方案),就不会有无限路径

  • 标记您已经访问过的地方,以免“返回”(这绝不是到达目的地的最短路径)

    • 如果你使用深度优先搜索,这很微妙,因为这些标记必须是搜索的本地标记(你不能简单地改变布尔矩阵);例如,您可以向A-B-A-B-A-B...函数添加int list参数,这些参数将是当前探索路径的一部分;在尝试计算可能的邻居的最短路径之前,请检查它是否在此列表中。对于更高效的东西,您可以使用集合(find_shortest_path)(对数,而不是线性,成员资格测试),或使用可变布尔矩阵,在选择不同路径时,您需要小心回溯状态更改。

    • 如果你使用广度优先搜索,你可以使用“你已经访问过的地方”的全局布尔矩阵,因为你同时探索到达给定长度的所有路径;所以如果你遇到一个已被标记为已访问的地方,你知道另一条路径在较早的时间内访问过它,所以它已经超前于你了,没有必要尝试从那里获得最短的路径。

所以简短的回答是:你应该学习广度优先搜索。

答案 1 :(得分:2)

嗯,下一次法律行动计算对我来说是错误的。如果我在右下方(比方说7),我不能移动到方形1.这不应该导致循环,但是,它应该得到错误的答案。

我的猜测是,你只是关注一些非常漫长的失败路径。我认为你需要先进行广度优先搜索而不是深度搜索。实质上,您保留一组可到达的正方形,通过每一步的一个移动推进每个当前可到达的点。您的代码总是尝试从每个新位置到达目的地,因此(可能)遵循许多长路径。