我写了一个递归公式,该公式可以找到以网格模式排列的两个节点之间的路径。我的代码有两个问题。首先是有时将起始节点的位置从一个数字更改为另一个数字,但是我通过在递归后重新分配它来解决此问题,因此这没什么大不了的。第二个问题是它的运行速度令人难以忍受。生成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 }));
}
重申一下,我想知道是否可以进行优化,因为它的运行速度太慢了。
答案 0 :(得分:0)
因此,我找到了一种惊人的算法的实现,该算法可以在12秒内生成10,000个7x7。您可以找到实现here。作者是内森·克莱斯比(Nathan Clisby),它基于论文“长紧密聚合物中的二级结构”。这个想法是在两点之间产生一条非随机路径,然后根据用户的意愿随机改变该路径多次。假设经过足够的迭代,该算法可以产生几乎与我在问题中发布的算法一样随机的路径。
去计算机科学家的路!