在两个随机节点之间找到路径的递归公式的优化

时间:2019-02-05 19:31:00

标签: algorithm

我写了一个递归公式,该公式可以找到以网格模式排列的两个节点之间的路径。我的代码有两个问题。首先是有时将起始节点的位置从一个数字更改为另一个数字,但是我通过在递归后重新分配它来解决此问题,因此这没什么大不了的。第二个问题是它的运行速度令人难以忍受。生成5x5大约需要30秒,而我一直无法生成7x7,这是我的最终目标。我希望有人会看到是否可以进行任何优化。

如下所示,Node类具有Key属性和Value属性。 Key是网格中从0开始的位置。因此,对于5x5,左上节点的Key为0,右下节点的Key为24。每个节点的Up,Down,Left和Right它是连接到的其他节点的属性。如果在该方向上没有节点,则该值为null。例如,在5x5中,Key = 0的节点的Up值为null,Key = 5的节点的Down值为null,Key = 1的节点的Left值为Right,而Node的Key的值为1。仍在5x5中,Key = 6的节点将具有Key = 1的节点的Up,Key = 11的节点的Down,Key = 5的节点的Left,以及Key = 5的节点的Right。 Key =7。Position属性是路径。路径从Position = 1的节点开始,然后再转到Position = 2的节点,依此类推,直到到达末端节点为止,该末端将是NxN板上的N * N位置(例如5x5板上将有一个末端节点) (位置25)。这些节点将添加到名为nodeList-(全局变量)的列表中。这些节点之一随机标记为Start-(布尔值),而另一个节点随机分配为End-(布尔值)。

下一部分是路径。我们想在开始节点和结束节点之间找到一条随机的路径(从1开始),该路径触摸其他每个节点而不触摸相同的节点两次。这是针对游戏的,因此重要的是它是随机的,这样用户就不会在同一块板上玩两次。如果在给定“开始”和“结束”位置的情况下无法做到这一点,则选择新的“开始”和“结束”位置,然后再次运行算法。

class Node
{
    public int Key { get; set; }
    public int? Position { get; set; } = null;
    public Node Up { get; set; } = null;
    public Node Down { get; set; } = null;
    public Node Left { get; set; } = null;
    public Node Right { get; set; } = null;
    public bool Start = false;
    public bool End = false;

    public Node(int key)
    {
        Key = key;
    }
}


public bool GeneratePath()
    {
        var current = nodeList.Where(w => w.Start).FirstOrDefault();
        var start = current;
        int position = 1;

        bool Recurse(Node caller)
        {
            if (current.Position == null)
            {
                current.Position = position;
            }
            if (current.End)
            {
                return true;
            }               


            var directions = GetDirections();

            for (var i = 0; i < 4; i++)
            {
                var done = false;
                if (directions[i] == 0 && current.Up != null && current.Up.Position == null
                    && (!current.Up.End || position == n * n - 1))
                {
                    var temp = current;
                    current = current.Up;
                    position++;
                    done = Recurse(temp);
                }
                else if (directions[i] == 1 && current.Down != null && current.Down.Position == null 
                    && (!current.Down.End || position == n * n - 1))
                {
                    var temp = current;
                    current = current.Down;
                    position++;
                    done = Recurse(temp);
                }
                else if (directions[i] == 2 && current.Left != null && current.Left.Position == null 
                    && (!current.Left.End || position == n * n - 1))
                {
                    var temp = current;
                    current = current.Left;
                    position++;
                    done = Recurse(temp);
                }
                else if (directions[i] == 3 && current.Right != null && current.Right.Position == null 
                    && (!current.Right.End || position == n*n - 1))
                {
                    var temp = current;
                    current = current.Right;
                    position++;
                    done = Recurse(temp);
                }
                if(done)
                {
                    return true;
                }
            }


            current.Position = null;
            position--;

            if(caller == null)
            {
                return false;
            }

            current = caller;

            return false;
        }

        var success = Recurse(null);

        if (success)
        {
            start.Position = 1;
        }

        return success;
    }



private int[] GetDirections()
    {
        List<int> toPerm = new List<int>();
        for (var i = 0; i < 4; i++)
        {
            toPerm.Add(i);
        }

        Random random = new Random();
        var perms = HelperMethods.GetPermutations(toPerm, toPerm.Count);
        var randomNumber = random.Next(0, perms.Count());
        var directions = perms.ElementAt(randomNumber).ToArray();

        return directions;

    }



public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
    {
        if (length == 1) return list.Select(t => new T[] { t });
        return GetPermutations(list, length - 1)
            .SelectMany(t => list.Where(o => !t.Contains(o)),
                (t1, t2) => t1.Concat(new T[] { t2 }));
    }

重申一下,我想知道是否可以进行优化,因为它的运行速度太慢了。

1 个答案:

答案 0 :(得分:0)

因此,我找到了一种惊人的算法的实现,该算法可以在12秒内生成10,000个7x7。您可以找到实现here。作者是内森·克莱斯比(Nathan Clisby),它基于论文“长紧密聚合物中的二级结构”。这个想法是在两点之间产生一条非随机路径,然后根据用户的意愿随机改变该路径多次。假设经过足够的迭代,该算法可以产生几乎与我在问题中发布的算法一样随机的路径。

去计算机科学家的路!