优化的在线查找算法

时间:2018-11-09 14:15:40

标签: algorithm math geometry

我正在寻找一种优化的基于整数的点对点算法,您可以在其中使用开始和结束坐标定义线,并根据x或y输入来查找点。

我知道如何使用dy / dx除法来做到这一点,但我正在寻找一种消除所有除法的算法。

这是我目前正在做的事情:

int mult = ((px - v0.x)<<16) / (v1.x - v0.x);
vec2 result{px, v0.y + (lerpmult*(v1.y - v0.y))>>16};

第一行的划分是我要消除的问题。

2 个答案:

答案 0 :(得分:0)

解决此问题的一个技巧是使用scalar product确定两个向量之间的角度的余弦值:

def line_test(a, b, p):
    v_ap = tuple(m - n for n, m in zip(a, p))
    v_ab = tuple(m - n for n, m in zip(a, b))

    scp = sum(m * n for m, n in zip(v_ap, v_ab))

    return scp > 0 and scp * scp == sum(n * n for n in v_ap) * sum(n * n for n in v_ab) and all(m <= n for m, n in zip(v_ap, v_ab))

two vectors

以上函数的参数是线的端点(ab)和点p(图像中的c),我们要测试。

逐步在每一行中发生以下情况:

  1. v_ap = tuple(m - n for n, m in zip(a, p))
    我们计算从apv_ap)的向量
  2. v_ab = tuple(m - n for n, m in zip(a, b))
    abv_ab)的向量
  3. scp = sum(m * n for m, n in zip(v_ap, v_ab))
    在此行中,计算v_apv_ab的标量积。结果为scp = cos(v_ab, v_ap) * euclidean_length(v_ab) * euclidean_length(v_ap),其中向量的欧式长度定义为sqrt(sum(n * n for n in vector))(向量的几何长度的标准定义)。
  4. return scp > 0 and scp * scp == sum(n * n for n in v_ap) * sum(n * n for n in v_ab) and all(m <= n for m, n in zip(v_ap, v_ab)
    这行很复杂,因此我将其分为几部分:

scp * scp == sum(n * n for n in v_ap) * sum(n * n for n in v_ab)
由于不允许除法,我们也不应该使用平方根,因为它的计算通常涉及除法。因此,我们不用计算平方根,而是将两个向量的欧几里德长度和标量积都取平方,从而消除了平方根计算:

scp = cos(v_ab, v_ap) * euclidean_length(v_ab) * euclidean_length(v_ap) =
    = cos(v_ab, v_ap) * sqrt(sum(n ^ 2 for n in v_ab)) * sqrt(sum(n ^ 2 for n in v_ap))

scp ^ 2 = cos(v_ab, v_ap) ^ 2 * sum(n ^ 2 for n in v_ab) * sum(n ^ 2 for n in v_ap)

如果两个向量指向同一方向,则它们之间的角度的余弦值应为1。因此,如果向量共享相同方向,则标量积的平方将为

euclidean_length(v_ap) ^ 2 * euclidean_length(v_ab) ^ 2

然后将其与实际标量积scp进行比较。

但是,这留下了一个问题:用正方形消除符号,我们将用比较scp > 0进行单独检查。由于欧式长度始终为正,因此仅余弦的符号决定scp的值。负值scp表示v_apv_ab之间的夹角至少为pi / 4,最大为pi * 3/4。但是,scp的符号在平方时会丢失,这意味着我们只能检查两个向量是否平行,而不能检查两个向量是否指向同一方向。通过另外检查scp > 0可以解决此问题。

最后但并非最不重要的一点是,我们必须检查从ap的距离是否比从ab的距离短。这可以通过检查v_ap的长度是否小于v_ab来完成。由于我们已经检查了两个向量指向完全相同的方向,因此足以检查v_ap中的所有元素是否最多与v_ab中的相应元素一样大,这是通过

all(m <= n for m, n in zip(v_ap, v_ab))

答案 1 :(得分:0)

找到的答案如下:

可以说我们的线方程为Ax + By + C = 0。那我们只需要 这三个系数(ABC)。

说这条线穿过点P(P_x, P_y)Q(Q_x, Q_y)。然后 计算上述三个系数很容易。

A = P_y - Q_y,
B = Q_x - P_x,
C = - A P_x - B P_y

一旦有了线方程,就可以轻松计算出xy 分别协调给定的yx

这是我的c++模板:

#include <iostream>
using namespace std;

// point struct
struct pt {
    int x, y;
};

// line struct
struct line {
    int a, b, c;

    // create line object
    line() {}
    line (pt p, pt q) {
        a = p.y - q.y;
        b = q.x - p.x;
        c = - a * p.x - b * p.y;
    }

    // a > 0; is must be true otherwise runtime error will occure
    int getX(int y) {
        return (-b * y - c) / a;
    }

    // b > 0; is must be true otherwise runtime error will occure
    int getY(int x) {
        return (-a * x - c) / b;
    }
};

int main() {
    pt p, q;
    p.x = 1, p.y = 2;
    q.x = 3, q.y = 6;
    line m = line(p, q);
    cout << "for y = 4, x = " << m.getX(4) << endl;
    cout << "for x = 2, y = " << m.getY(2) << endl;

    return 0;
}

输出:

for y = 4, x = 2
for x = 2, y = 4

参考:http://e-maxx.ru/algo/segments_intersection