Algorithm 快速空间划分启发式算法?
我有一个(子)空间,其中填充了Algorithm 快速空间划分启发式算法?,algorithm,time-complexity,computational-geometry,heuristics,Algorithm,Time Complexity,Computational Geometry,Heuristics,我有一个(子)空间,其中填充了N线段。这些线段始终是凸多边形的一部分。可能是这样的: 我想做的是开发一种启发式方法来选择分割空间的线段。然后,选定线段的支撑线将分割空间。有两个相互矛盾的启发因素: 线段应均匀分割空间;完成后,子空间A中的线段数应与子空间B中的线段数相同(平衡) 线段的支撑线应与尽可能少的其他线段相交(分割)(自由) 例如: 蓝线:完美的自由,非常糟糕的平衡 红线:非常糟糕的自由,平庸的平衡 绿线:自由度差,平衡性好 紫线:自由度好,平衡度好 在上面的示例中,组合启发式可能会
N
线段。这些线段始终是凸多边形的一部分。可能是这样的:
我想做的是开发一种启发式方法来选择分割空间的线段。然后,选定线段的支撑线将分割空间。有两个相互矛盾的启发因素:
O(N^2)
操作。我更喜欢在O(N logn)
中运行的东西
有没有关于循环通过线段并给它们打分的O(N logn)
算法的想法?我的一个想法是对这些片段进行三次排序,形成一些象限:
象限中心给出了大多数线段的位置。因此,可以使用它们来找到靠近这个中心的线段,并检查它相对于象限的方向是否正确。以某种方式这将给出一个良好的平衡分数
对于交叉点,我考虑为线段创建边界框,并将它们排序到树中,这样可能会加快交叉点估计
一些额外的提示(我的输入数据在很多时候看起来像什么)
O(N^2)
比较算法,该算法在BSP树构建时会快速加速,使用了一种稍微聪明的观察
- 让每个BSP节点跟踪它在其子空间中仍然保留的线段(以及它必须从中选择下一个拆分器)
- 让每个段都有四个与之关联的值:
、posCount
、negCount
和引入的
。让它也有一个对另一段的保存的
引用,以防它被拆分(否则它是partner
)null
- 使用以下
algo初始化根节点上的拆分器(即所有拆分器):O(N^2)
calcreationcounts(拆分器)
:O(N^2)
- 对于仍保留拆分器的每个节点,选择最大化以下内容的节点:
evaluate(…)
其中treeDepth=floor(log2(splitterCountAtThisNode))
:O(1)
使用我的优化算法使用的以下神奇数字:
#define BALANCE_WEIGHT 437
#define INTRO_WEIGHT 750
#define SAVED_WEIGHT 562
#define EVALUATE_X1 3
#define EVALUATE_X2 31
#define EVALUATE_V1 0.0351639f
#define EVALUATE_V2 0.187508f
- 将此拆分器用作此节点的拆分器,称为SEL。然后,将此节点上的所有拆分器分为三组
、正片
和负片
:残片
distributespliters()
:
我在这里意识到的一件聪明的事情是,通常,特别是使用上述启发式的前几次拆分,会产生非常不平衡的拆分(但非常自由)。问题是你会得到“O(N^2)+O((N-N)^2)”+…
,这在N
很小的时候是很可怕的!相反,我意识到,不必这样做,我们可以努力重新计算最小的分割,它取O(n^2)
,这并不坏,然后简单地迭代每个位分割分割器,从较小的分割部分减去计数,这只取O(Nn)
,这比O(n^2)
好得多!以下是updateRelationCounts()
的代码:
算法updateRelationCounts()
:
我现在已经仔细地测试过了,逻辑似乎是正确的,因为更新正确地修改了
posCount
等,因此它们与再次进行硬计算时相同 看起来像是二进制空间分区树问题。别忘了这方面的任何启发。kd树有一种常见的方法,称为SAH(表面积启发式)。这可以让你了解成本函数是如何在细分过程中找到最优解的。是的,这是用于BSP构造的。不过,这里的数据结构很有用,谢谢!在你的算法中。你在乎线路方向吗?如果是BSP构造,则感觉后续行的方向不应彼此独立拾取。您可能希望下一行或多或少与上一个选项垂直。对吗?
evaluate(posCount, negCount, saved, introduced, treeDepth) {
float f;
if (treeDepth >= EVALUATE_X2) {
f = EVALUATE_V2;
} else if (treeDepth >= EVALUATE_X1) {
float r = treeDepth - EVALUATE_X1;
float w = EVALUATE_X2 - EVALUATE_X1;
f = ((w-r) * EVALUATE_V1 + r * EVALUATE_V2) / w;
} else {
f = EVALUATE_V1;
}
float balanceScore = -f * BALANCE_WEIGHT * abs(posCount - negCount);
float freedomScore = (1.0f-f) * (SAVED_WEIGHT * saved - INTRO_WEIGHT * introduced);
return freedomScore + balanceScore;
}
#define BALANCE_WEIGHT 437
#define INTRO_WEIGHT 750
#define SAVED_WEIGHT 562
#define EVALUATE_X1 3
#define EVALUATE_X2 31
#define EVALUATE_V1 0.0351639f
#define EVALUATE_V2 0.187508f
for all splitters s at this node
s.partner = null
if s == SEL then add s to "remnants"
else
if s is fully on the positive side of SEL
add s to "positives"
else if s is fully on the negative side of SEL
add s to "negatives
else if s intersects SEL
split s into two appropriate segments sp and sn
sp.partner = sn, sn.partner = sp
add sn to "negatives", sp to "positives" and s to "remnants"
else if s coplanar with SEL
add s to "remnants"
// the clever bit
if (positives.size() > negatives.size())
calcRelationCounts(negatives)
updateRelationCounts(positives, negatives, remnants)
else
calcRelationCounts(positives)
updateRelationCounts(negatives, positives, remnants)
add positives and negatives to appropriate child nodes for further processing
updateRelationCounts(toUpdate, removed, remnants) {
for all splitters s in toUpdate
for all splitters vs in removed, then remnants
if vs has a partner
if the partner intersects s
s.posCount++, s.negCount++, s.introduced++
else if the partner is fully on the positive side of s
s.posCount++
else if the partner is fully on the negative side of s
s.negCount++
else if the partner is coplanar with s
s.saved++
else
if vs intersects s
s.posCount--, s.negCount--, s.introduced--
else if vs is fully on the positive side of s
s.posCount--
else if vs is fully on the negative side of s
s.negCount--
else if vs is coplanar with s
s.saved--