我正在尝试提出一种方法,用于在给定区域(在我的例子中为正方形)中生成X量的随机点。造成这种问题的一个原因是每个点必须至少与所有其他点相距Y个单位。
首先想到的是(在c#中)检查新点与所有现有点之间的距离:
while(points.Count < pointsToGenerate)
{
Point newPoint = NewPoint();
bool addPoint = true;
foreach(Point p in points)
{
if((p - newPoint).Length() < minDistance)
{
addPoint = false;
break;
}
}
if(addPoint)
{
points.Add(newPoint);
}
}
现在这肯定会有效,但如果没有找到有效点,这将成为一个永无止境的循环。那么在那里投入一个神奇的数字Z作为尝试的极限?
if(loopCount > 100)
{
break;
}
现在这有一些明显的问题。如果这些点是随机生成的,那么即使存在放置点的位置,loopCount也可以超过Z.它不仅可以,而且会发生!
我能做的是为每个传递创建一个可用点列表,然后选择其中一个随机点。除了一件事:性能,这将完美无缺。我的应用程序中不需要超级性能,但面积为1000 ^ 2。即使我将自己限制为整数,每次通过也要检查很多要点!
所以,我能想到的可能还不够,因此我希望得到一些帮助。有没有更好的方法在区域A中生成X点,点之间的距离最小?
谢谢!
编辑:更好的是,我认为通常会在性能与完美之间实现平衡。我知道,有点模糊。我并不确定我有多少开销可以产生这些点,所以我基本上比任何比我自己的方法更优雅。
〜罗伯特
答案 0 :(得分:3)
要理解你的问题:你是在寻找最佳答案(即:家庭作业),还是寻找一种比创建随机点更好的算法?
在第一种情况下,如果您没有关于A区的先验信息,我担心这是一个非常复杂的问题。而且我认为找到一种比探索每一个案例更快的算法是很困难的。
但是,如果您有关于A的先前信息,那么事情可能会更简单。例如,如果它是凸,您可以利用最佳路面,如果您的空间是无限的是六边形。这意味着你必须将你的点(在X中)放在三角形的末端
所以:
这个算法并不是最优的(除非你定义一个非常好的“有利于凸包上的那个”......)
编辑:E先生的评论提醒我,最佳路面来自circle packing。感谢他的精确度!
然而,我确实有另一种看起来很漂亮的算法,甚至可能是最优的!它不需要A上的任何条件,而且有点贵,但不是太多。是的,我知道,这与我说的相矛盾,但谁在意呢!拥有一个好的算法就足够了。
我们将B命名为目前可用点的集合。并且C是形成B的末端的点。开始时,B = A,如果A是正方形,则C由4个点(角点)组成。你只需递归:
我知道如果你在1000x1000网格中工作,C以4分开始,但在向X添加一个点之后,这意味着C增长到1570分(大致(pi / 2) 1000)。 你必须注意到你从未放入内存B,这是大的(O(n ^ 2),如果A可以放在 n网格中)只有C,而且我相信在任何时候,大小都是C是O(n),其仍然比O(n ^ 2)好得多。并且计算直径仍为O(大小(C))= O(n)
答案 1 :(得分:2)
这是我的想法,我认为你必须将区域划分为边长等于Y的正方形。在这样做之后,你可能会留下一个边长小于y的矩形,除非area = integer * y2。 现在,您可以生成的最大点数是正方形+矩形。因此,如果X大于此值,则可以以失败结束方法。
开始生成点以最后一个(右下角),最小的矩形开始。在那里选择一个随机点,并在其顶部矩形和左边的矩形上找到一个点,使它们与第一个点相距Y,并且只有当它的直接右边和正方形底部正方形/矩形为时,才开始填充矩形/正方形中的点。填充。因此,您在最后填充第一个方格。
在生成随机点时,您只需要担心距离最大两点的距离,即右侧正方形/矩形中的点和直接底部正方形/矩形中的点。当然,如果你得到超过X分,你可以停下来,或者忽略随机的几个点来保留X分
为了让事情更随意,你可以从四边中的任何一边开始潜水Y边的方格(这里是左上角),这样每次起点都不同。
答案 2 :(得分:1)
如果将自己限制为整数是一种选择,那么有一种算法可行。只需跟踪每行或每列中可用的位置数,然后在新点的空白处重新计算每行或每列的值。这里的关键是选择的随机位置仅在可用点中选择,而不是整个网格。
PointsRemaining = X
Points = new CoordinateCollection
Width = 1000
Height = 1000
if (Width > Hight)
Swap(Width, Hight)
Swapped = true
NumberOfLocationsAvailable = new int[Width]
InitializeArrayValues(NumberOfLocationsAvailable, Height)
TotalLocationsAvailable = Width * Height
While (TotalLocationsAvailable > 0 and PointsRemaining > 0)
NextPoint = Random(0, TotalLocationsAvailable)
NextCoordinates = FindCoordinates(NextPoint, NumberOfLocationsAvailable)
NumberLocationsRemoved = RemoveLocationsWithinDistance(NextCoordinates, Points, NumberOfLocationsAvailable, Y)
TotalLocationsAvailable = TotalLocationsAvailable - NumberLocationsRemoved
Points.Add(NextCoordinates)
PointsRemaining = PointsRemaining - 1
if (Swapped)
Points = RotatePoints(Points)
这会给RemoveLocationsWithinDistance
带来很多复杂性,但这不应该太难。它需要一个二维方形阵列的大小为Y的布尔值。
答案 3 :(得分:1)
如果你想要一个随机分布,你可以在O(1)时间内通过将你的网格离散化为大小为y的单元格来回答“距离这一点有少于y的点”。然后,距离另一个点Q小于y的任何点P必须位于与Q相邻的单元格中的9个单元格中的一个中,因此您可以使用哈希表并执行9次检查。此外,每个细胞最多可包含2个点。
使用这样的数据结构,您可以使用拒绝进行随机抽样以填补空间。只要y与总面积相比相对较小,您就会很快成功。
答案 4 :(得分:0)
您可以使用分子动力学技术。
从一个大盒子开始,然后在一些规则网格上将点分散到那里,间距&gt;收率
现在将每个点视为球形粒子,通过排斥性Lennard-Jones等排斥势与其他点相互作用,选择参数使每个粒子的有效直径为Y.
给粒子随机速度,用离散求解器求解运动方程(比如速度 - Verlet,它不必是准确的)。颗粒之间的排斥将使颗粒Y保持分开。当您更新颗粒位置的时间步长时,每次缩小一下盒子直到达到您需要的尺寸。 (哦,可以用周期性边界条件或短距离排斥场来处理该框。)
当然,您必须确保可以将所有颗粒装入盒子中。在2D中,圆的最佳打包是已知的,参见kepler问题。难道我没有听说3D开普勒问题现在已经解决了吗?