我目前正在开发一个测试程序,确保主板上的孔彼此不太靠近或者它们不重叠。
为了做到这一点,我保留了所有的漏洞。名为holeInfo的对象中的X,Y坐标和半径以及列表holeInfoList中的对象。
我目前正在使用嵌套的for循环来遍历所有的孔和一个基本的数学公式来检查孔之间的距离。
这是我使用的功能:
public void checkHoleConditions()
{
for (int i = 0; i < holeInfoList.Count - 1; i++)
{
errorType = new List<string>();
for (int j = i + 1; j < holeInfoList.Count; j++)
{
holeMinSpaceFailed = false;
if (failsHoleConditions(holeInfoList[i], holeInfoList[j]))
{
if (holeMinSpaceFailed)
{
errorType.Add("X: " + holeInfoList[j].holeXCoordinate + " Y: " + holeInfoList[j].holeYCoordinate + "R: " + holeInfoList[j].holeDiameter + " too close.");
}
else
{
errorType.Add("X: " + holeInfoList[j].holeXCoordinate + " Y: " + holeInfoList[j].holeYCoordinate + "R: " + holeInfoList[j].holeDiameter + " overlap.");
}
invalidHole = new InvalidHole(holeInfoList[i], errorType);
invalidHoleList.Add(invalidHole);
Console.WriteLine("Hole Error Detected");
}
else
{
Console.WriteLine("Hole Check Successful");
}
}
}
}
public bool failsHoleConditions(HoleInfo holeOne, HoleInfo holeTwo)
{
float holeOneXCoordinate = holeOne.holeXCoordinate;
float holeOneYCoordinate = holeOne.holeYCoordinate;
float holeOneRadius = holeOne.holeDiameter / 2;
float holeTwoXCoordinate = holeTwo.holeXCoordinate;
float holeTwoYCoordinate = holeTwo.holeYCoordinate;
float holeTwoRadius = holeTwo.holeDiameter / 2;
float holeXDifferenceSquared = (float)Math.Pow((holeOneXCoordinate - holeTwoXCoordinate), 2);
float holeYDifferenceSquared = (float)Math.Pow((holeOneYCoordinate - holeTwoYCoordinate), 2);
float distanceBetweenCenters = (float)Math.Sqrt(holeXDifferenceSquared + holeYDifferenceSquared);
float distanceBetweenHoles = distanceBetweenCenters - (holeOneRadius + holeTwoRadius);
if (distanceBetweenHoles <= 0)
{
return true;
}
else if (distanceBetweenHoles <= minimumSpace)
{
holeMinSpaceFailed = true;
return true;
}
return false;
}
目前,我的程序在大约2.5分钟内对254个列表对象进行测试。考虑到这是一个测试程序,2.5分钟只有254个孔是很长的时间。
为了让测试运行得更快,我可以实现哪些算法?
答案 0 :(得分:3)
这个解决方案的灵感来自物理引擎,以及那里负责碰撞检测的优化。
我不会声称完全知道它是如何工作的,为了更好的解决方案,你应该尝试研究ODE或Bullet Dynamics。
基本上,解决方案是将对象(孔)分隔成岛,并且仅将每个对象的位置与同一岛中的对象进行比较。如果你无法想出一种正确分离岛屿的方法,你可以这样做:
假设我们在一个方形场上有125个物体,5乘5。你可以将它分成25个主要的方形岛,然后是8个交叉岛(长岛,沿着主岛的边缘。这些岛的最小边)应该是您想要计算的最小距离)。群岛可以重叠。您必须解析整个列表一次才能进行此拆分。这意味着到目前为止,我们总共遍历了125个项目 - O(n)
。
接下来,对于每个岛屿(总共33个,O(n^(2/3))
,通过使用相同的嵌套循环找到比它们必须更近的对象。每个岛的总复杂度为O((n / n^(2/3))^2)
= {{ 1}}。对岛屿数量进行计算,我们得出此算法的总复杂度= O(n^(2/3))
,小于最初提出的O(n^(4/3))
。
希望这是有道理的。如果你愿意,我可以写一个Python演示。只是它会有很多代码。
修改强>
或者您可以使用2D物理引擎并将对象绘制为直径等于孔之间的最小距离的圆,然后让它检测碰撞。或者从那里获取相关代码(如果许可证允许),因为整个物理引擎对于任务来说有点过分
https://github.com/VelcroPhysics/VelcroPhysics
编辑2:
254个列表对象
我以为你正在解析254个不同的电路板。我强调的这个解决方案只有在巨大的计算负荷下才有意义。
答案 1 :(得分:1)
holeMinSpaceFailed永远不会为真 不要传递像那样的逻辑
x * x
比Math.Pow
无需采用Sqrt - 只需使用square minimumSpace
像这样的东西。 运行100万300毫秒。您的代码需要几分钟才出错。
static byte HoleTooClose(HoleInfo x, HoleInfo y, float minDistance)
{
float holeSize = (x.Diameter + y.Diameter) / 2;
float deltaX = y.X - x.X;
float deltaY = y.Y - x.Y;
float distanceSquared = deltaX * deltaX + deltaY * deltaY - holeSize * holeSize;
if (distanceSquared <= 0)
{
return 0;
}
float minDistanceSquared = minDistance * minDistance;
if (distanceSquared <= minDistanceSquared)
{
return 1;
}
return 2;
}
internal struct HoleInfo
{
public float Diameter { get; internal set; }
public float X { get; internal set; }
public float Y { get; internal set; }
public HoleInfo (float x, float y, float diameter)
{
X = x;
Y = y;
Diameter = diameter;
}
}
static bool DistanceTooClose(System.Windows.Point x, System.Windows.Point y, Double minDistance)
{
double deltaX = Math.Abs(y.X - x.X);
double deltaY = Math.Abs(y.Y - x.Y);
double distanceSquared = deltaX * deltaX + deltaY * deltaY;
//double distance = Math.Sqrt(distanceSquared);
Double minDistanceSquared = minDistance * minDistance;
return (distanceSquared <= minDistanceSquared);
}
答案 2 :(得分:0)
如果你需要对所有人进行全部测试,一个优化可以是将它们放在nxn矩阵中,而不是列表中。然后,并行执行验证。矩阵越大,核心越多越好。并行也可以在列表上执行,但我不能100%确定.Net是否会确定等待每个线程结束,因为算法是顺序的。
如果您的方法使用欧氏距离,您可以尝试根据此距离首先对列表进行排序。相对于0的sqr(x)+ sqr(y)可以是首先排序的分数。我认为.Net可以轻松处理这种排序。稍后,只需运行算法直到第一个允许的电路。然后,您知道其余部分必须被接受,因此您只会在列表的第一个元素上执行