如何修改递归算法以找到最短路径?

时间:2013-07-25 10:38:21

标签: c# recursion artificial-intelligence unity3d path-finding

https://vimeo.com/70999946

我实现了一个递归路径查找算法。该递归算法基于连接在一起的预先设定的节点而工作。每个节点有四个包含更多方向的指针:Top,Button,Left和Right。递归算法简单地遍历每个节点并逐个寻找这四个方向中的每一个以到达其最终目的地;例如,考虑以下7个节点:A,B,C,D,E,F,G,H。

    A (Button->D, Right->B)
    B (Right->C, Left->B)
    C (Left->B)
    D (Button->G, Right->E, Top->A)
    E (Right->F, Left->D)
    F (Left->E)
    G (Right->H, Top->D)
    H (Left->G)

这些节点在进入整体视图时将显示下图。

A—B—C
|
D—E—F
|
G—H

在此示例中,假设walker已启动节点为节点A,并希望将节点H作为其最终目标。节点A按顺序查看自己的Right,Button,Left和Top;它的右边指向节点B,因此他选择去节点B;相同模式的节点B选择向右移动,节点C.当步行者到达节点C时;当它的右边,顶部和按钮被阻塞时,节点C恢复到节点B.同样,节点B恢复节点A.步行器再次返回起始点。然后节点A根据订单转到其按钮节点;这意味着它进入节点D.节点D进入其右侧节点E,然后进入节点F.节点F被阻止;它返回到节点E和节点D.然后,节点D根据步行器顺序选择其按钮,节点G.从那里节点G进入节点H.最后,步行者到达其最终目的地。

Pseudocode: Recursive Path Finding Algorithm
ArrayList findPath(GameObject currentPoint , GameObject targetPoint , ArrayList InputArrayList)
{
1-Duplicate InputArrayList as tempArrayList

2-If the currentPoint equals to target Point return inputArrayList
//*** End Condition found target

3-If the Right side of the currentPoint is empty goto step 4
3.1- Add currentPoint to tempArrayList
//*** Call Right
3.2- tempArrayList = findPath(currentpoint.Right, targetPoint, tempArrayList);
3.3- If tempArrayList is not null return tempArrayList
4-If the Button side of the currentPoint is empty goto step 5
4.1- Add currentPoint to tempArrayList
//*** Call Button
4.2- tempArrayList = findPath(currentpoint.Button, targetPoint, tempArrayList);
4.3- If tempArrayList is not null return tempArrayList
5-If the Left side of the currentPoint is empty goto step 6
5.1- Add currentPoint to tempArrayList
//*** Call Left
5.2- tempArrayList = findPath(currentpoint.Left, targetPoint, tempArrayList);
5.3- If tempArrayList is not null return tempArrayList
6-If the Top side of the currentPoint is empty goto step 7
6.1- Add currentPoint to tempArrayList
//*** Call Top
6.2- tempArrayList = findPath(currentpoint.Top, targetPoint, tempArrayList);
6.3- If tempArrayList is not null return tempArrayList
7-Return null;
//*** End Condition does not found target
}

注意:实际代码位于C#中,您可以从此link下载。

案例研究中问题的出现: 当你理解这段代码时;它有一个弱点,以说明它;考虑以下节点的整体视图,假设起始节点是节点A,最终目的地是节点H.

A—B—C
|
D—E—F—I
|   | |
G—H—J—K

虽然最佳路径解是(A,D,G,H),但解释的递归路径寻找算法找到(A,D,E,F,I,K,J,H)作为其解;这真的看起来机器人是一个愚蠢的机器人:D!

图1:递归路径查找算法 enter image description here

图2:具有学习能力的递归路径查找算法 enter image description here

我通过为节点添加学习能力来解决问题。您可以从此link中看到问题的详细信息。但是,我想知道是否有人可以修改递归算法以找到最短路径。

谢谢,

3 个答案:

答案 0 :(得分:4)

为什么不简单地将其与DijkstraA* search进行比较?

请注意,通过使用递归而不是循环,您可能会在1025次递归时获得StackOverflow。

答案 1 :(得分:3)

当你想要做的事情是depth-first search时,你正在做breadth-first search。后者需要queue而不是递归。维基页面很好地解释了如何实现它,所以我在此不再重复。

从那里开始实施A*并不会有太大的作用,这会加速你的结果。这将需要优先级队列而不是队列; C#在基础库中没有优先级队列,但幸运的是,我是optimized C# priority-queue implementation的作者,专门用于寻路。

另外,既然你提到了Unity,我会指出专为Unity构建a number of pathfinding libraries。这可能是最安全的路线,因为视频游戏中的有效寻路并非易事。

答案 2 :(得分:1)

这是您需要的深度优先(更快写)版本。我不建议这个路径寻找的答案。 BlueRaja和Geoffrey的答案更加通用,稳定,并且更好地用于寻路。但我想回答OP的直接问题。

为了简化我的示例,边缘具有成本/权重1.最短路径==路径w /到达目标的最少节点数(技术上length == |path| - 1,因为我在计算节点而不是边缘但是无论如何,这个想法是一样的)。这段代码不会处理周期,这就是为什么其他算法更好的原因。

public class PathNode {
    public string Id;
    public PathNode[] Children = new PathNode[4];
}

public class PathFinder : MonoBehaviour {
public List<PathNode> shortestPath = null;

/// <summary>
/// Sets shortest path to `candidatePath` if `candidatePath` is shorter
/// or no shortest path yet.
/// </summary>
public void SetShortestPath(List<PathNode> path){
    if(shortestPath == null){
        shortestPath = new List<PathNode>(path);
        return;
    }
    if(path.Count < shortestPath.Count)
        shortestPath = new List<PathNode>(path);
    }

/// <summary>
/// depth-first shortest path
/// </summary>
public void FindShortestPath(PathNode target, List<PathNode> path){
    PathNode next = path[path.Count-1]; //get next node from path
    if(target == next){
        SetShortestPath(path);
        return;
    }
    // dfs - iterate children
    foreach (PathNode node in next.Children){
        if(node!=null){
            path.Add(node);             
            FindShortestPath(target,path);
            path.Remove(node);          
        }
    }
}
public PathNode ExampleEdgeCreation(){
    PathNode a = new PathNode{Id="A"};
    a.Children[(int)Direction.Left] = new PathNode{Id="B"};
    return a;
}
}

假设PathNode.Children[0] == PathNode.Children[Left]等我在代码中枚举了它们,但我想保持这个小例子为SO。