-
05-07-2019 - |
题
我有3个点(A、B和X)和距离(d)。我需要测试一个函数,如果X点是更接近于距离d任何一点上的线段AB。
问题是首先是我正确的解决方案然后来了一个更好的(快速)的解决方案。
我第一次通过如下
AX = X-A
BX = X-B
AB = A-B
// closer than d to A (done squared to avoid needing to compute the sqrt in mag)
If d^2 > AX.mag^2 return true
// closer than d to B
If d^2 > BX.mag^2 return true
// "beyond" B
If (dot(BX,AB) < 0) return false
// "beyond" A
If (dot(AX,AB) > 0) return false
// find component of BX perpendicular to AB
Return (BX.mag)^2 - (dot(AB,BX)/AB.mag)^2 < d^2
这个代码最终会正在运行的大型设的P和一个大集/B/d三胞胎的意图,找到所有P的通过至少一A/B/d所以我怀疑有一种方法来减少总体费用的基础上,但是我还没看到呢。
(顺便说一句:我知道,一些重新排序,一些临时性的价值观和一些代数的身份可能会降低的成本上。我只是省略了他们,为了清楚起见.)
编辑:这是一个2D问题(但解决方案,概括来3D会很酷
编辑:在进一步的反思,我期待的命中率是50%左右。X点,可以形成一种嵌套的层次,所以我希望能够修剪子树大,因为所有通和所有失败。A/B/d限制的三胞胎将更多的一个伎俩。
编辑:d是在同一个数量级。
编辑:Artelius发布一个不错的解决方案。我不确定我明白到底是什么,他正在为我下一切之前我完全理解它。无论如何另一种思想来想作为一个结果是:
- 第一Artelius'位,预cacluate一个矩阵,将AB为中心吃的来源和与X轴。(2补充说,4muls和2增加了)
- 把它折叠所有入第1象限(2abs)
- 规模在X和Y使中心部分的区域进入一个单元方(2mul)
- 测试如果点是在这场(2试验)就是这样退出
- 测试结束盖(回到无标值
- 翻译在x到的地方结束在原籍国(1添加)
- 广场,并添加(2穆尔1加)
- 比较d^2(1cmp)
我相当肯定,这个节拍我的解决方案。
(如果没有什么更好的到来曾根Artelius获取"奖金":)
解决方案
如果你设置的(A、B、d)在固定的,可以计算出一个对的矩阵,为各翻译的协调系统,以便线基地成为X轴和中点的AB是起源。
我 想想 这是一个简单的方式构造的矩阵:
trans = - ((A + B) / 2) // translate midpoint of AB to origin
rot.col1 = AB / AB.mag // unit vector in AB direction
0 -1
rot.col2 = rot.col1 * ( ) // unit vector perp to AB
1 0
rot = rot.inverse() // but it needs to be done in reverse
然后你只要把每个X和做 rot * (X + trans)
.
该区域的问题实际上是对称的在x轴和y轴现在,所以可以采取的绝对值x的协调,并y坐标.
然后你只需要检查:
y < d && x < AB.mag/2 //"along" the line segment
|| (x - AB.mag/2)^2 + y^2 < d^2 // the "end cap".
你可以做另外一个特技;该矩阵可比例下降的一个因素 AB.mag/2
(记得的的矩阵是只计算一次,每(A、B)含义,它是更好的,找到他们慢,比实际检查本身)。这意味着你检查变为:
y < 2*d/AB.mag && x < 1
|| (x - 1)^2 + y^2 < (2*d/AB.mag)^2
具有替换的两个实例。mag/2恒1,这可能是一个触更快。当然,你可以precalculate 2*d/AB.mag
和 (2*d/AB.mag)^2
为好。
这是否会工作的速度比其他方法取决于投入你给它。但是,如果长AB是长相比,d,我认为这将大大快于方法发布。
其他提示
Hmmmmmmm....什么的点击率?如何经常做点"X"满足近要求?
我认为,现有的算法是良好的,以及任何额外的优化将从调整到真实的数据。例如,如果"X"指符合近测试99%的时间,那么我认为你的最优化的战略应当是相当大的不同,比如果这只有通过测试1%的时间。
顺便说一下,当你到哪里你这个算法与成千上万的点,你应该组织的所有各点进入一个K-维树(或 KDTree).它使计算的"最接近的邻居"简单得多。
你的问题就更复杂一点比一个基本最近的邻居(因为你检查近来的-一个线段,而不只是近来-一点),但我仍然认为KDTree将是方便的。
如果我读了这个正确,然后这几乎是同样作为经典的光球交叉点测试的作用,在射线3D-跟踪。
在这种情况下你有一个领域在位置X的半径d,你在试图找出是否AB线交叉的领域。这一差异的光线跟踪的是,在这种情况下你已经有了一个具体的行AB,而在光线跟踪的线通常是广义的作为 origin + distance * direction
, 和你不在乎有多远沿着无穷线 AB+
它是。
在伪码,从我自己的射示踪剂(根据的算法中给出的"介绍对光线追踪"(ed.Glassner):
Vector v = X - A
Vector d = normalise(B - A) // unit direction vector of AB
double b = dot(v, B - A)
double discrim = b^2 - dot(v, v) + d^2
if (discrim < 0)
return false // definitely no intersection
如果你已经走了这么远,那么有的 一些 机会,你的条件得到满足。你只需找出是否有交叉点(s)在线AB:
discrim = sqrt(discrim)
double t2 = b + discrim
if (t2 <= 0)
return false // intersection is before A
double t1 = b - discrim
result = (t1 < length(AB) || (t2 < length(AB))
这个代码最终会正在运行的大型设的P和一个大集/B/d三胞胎的意图,找到所有P的通过至少一A/B/d所以我怀疑有一种方法来减少总体费用的基础上,但是我还没看到呢。
在这种情况下,当d-a-b,对于给定点X,你可以第一个测试是否属于一个许多领域的半径d和中心Ai或Bi。看看图片:
...... .....
........... ...........
...........................
.......A-------------B.......
...........................
........... ...........
..... .....
前两个测试
If d^2 > AX.mag^2 return true
If d^2 > BX.mag^2 return true
是最快的,如果d-AB他们也是那些有最高的概率要取得成功(鉴于测试成功,在所有)。鉴于X,你可以做所有的"领域的测试",然后所有的最后一人:
Return (BX.mag)^2 - (dot(AB,BX)/AB.mag)^2
如果#组/B/d都很大,而你绝对是在2D,考虑使用 R-树木 (或他们的八当量)其中每一条在R-树是最小的边界框/B/d三倍。这会让你消除具有测试一个很大的A/B/d三倍&集中你的CPU功率仅仅在几个在那里的每个点X是在边界的框/B/d三倍。然后做了更详细的测试就像一个你提的。