Algorithm 计算无向线段平均方向的算法

Algorithm 计算无向线段平均方向的算法,algorithm,geometry,Algorithm,Geometry,我有一组二维无向线段,由两个端点组成。从统计上看,他们中的大多数人或多或少都处在同一个方向上 我想计算的是段集的平均方向(例如,如果该集为全局N/S,它将返回~0°等)。请注意,我不关心返回哪个实际方向(0°或180°也可以) 在[0..180°范围内夹紧每个段的方向并取平均值不起作用(例如,两个段,一个为1°,另一个为-1°:第二个将夹紧到179°,平均值错误,此处90°应为0°) 我还考虑将“标准化线段”端点分为两组,并计算由两组中点组成的线段的方向,但这对于任务来说似乎有点复杂。所谓“标准

我有一组二维无向线段,由两个端点组成。从统计上看,他们中的大多数人或多或少都处在同一个方向上

我想计算的是段集的平均方向(例如,如果该集为全局N/S,它将返回~0°等)。请注意,我不关心返回哪个实际方向(0°或180°也可以)

在[0..180°范围内夹紧每个段的方向并取平均值不起作用(例如,两个段,一个为1°,另一个为-1°:第二个将夹紧到179°,平均值错误,此处90°应为0°)

我还考虑将“标准化线段”端点分为两组,并计算由两组中点组成的线段的方向,但这对于任务来说似乎有点复杂。所谓“标准化线段”,我指的是在单位圆上有端点,在原点上有中点的线段

有已知的算法/公式吗?

有求角度平均值的算法/公式(在全圆周范围内)

对于您的情况,似乎可以计算方向向量的余弦平均值(因为Cos(-alpha)=Cos(alpha)),并得到ArcCos(范围为0..Pi)


可能角度应限制在(0..Pi)或(-Pi/2..Pi/2)范围内,以避免歧义。

元注:此答案计算给定直线的“中值”。 MBo计算给定行的“平均值”


让我们用下面的方式将问题形式化。 我们得到了一组线,我们想要找到一条线p,使得p和所有给定线之间的角度之和是可能的最小值。 这里,两条直线之间的角度是它们交点处的最小角度,如果它们平行或重合,则为0。 因此,两条直线之间的角度始终为0到90度

为了简化推理,请翻译线条,使它们都通过原点。 显然,这不会影响答案


为了解决这个问题,让我们研究上述总和的导数。 假设我们有一个回答行p。 设x线与p顺时针成0-90度角,y线与p逆时针成0-90度角(x+y=n,给出的线总数)

现在,顺时针旋转p一个小角度α。 答案将减少x*α,增加y*α。 因此,如果x>y,答案将减少,如果x 有两种情况下,数量x和y发生变化

  • 线p与给定的一条线重合

  • 线q与给定线之一正交

  • 在圆上任意两个这样的连续点之间,我们和的导数将是常数x-y。 因此,最小值将位于“感兴趣的角度”之一:与某些给定直线平行或正交。 这导致了O(n^2)算法:对于每个O(n)个感兴趣的角度,只需计算O(n)中的和,然后选择给出最小和的角度


    这可以进一步加速到O(n logn)

  • 生成O(n)中的2n个感兴趣角

  • 按O(n log n)对它们进行排序

  • 计算O(n)中第一个这样的角度的答案,以及x和y

  • 沿圆圈移动,保持当前答案和值x和y。 在每个O(n)步骤中,可以在O(1)中进行计算

  • 下面是一个
    O(n)
    解决方案,该解决方案还可以容纳每个分段的可选重量

    我们通过其与X轴(a)的角度和权重(w)对每个分段进行建模.此时分段方向并不重要,任何180°模的值都可以。方法是对每个分段进行循环,并跟踪到目前为止计算的平均方向;并使用更接近平均值本身的180模方向调整该平均值

    伪代码(所有角度以度为单位):

    aa=0
    ww=0
    对于分段中的a、w:
    //计算[-180°…+180°范围内角度之间的增量[
    da=a-aa
    如果da<-180:
    da+=360
    如果da>=180:
    da-=360
    //可选方向交换,[-90°…+90°内的增量[
    如果da<-90:
    da+=180
    如果da>=90:
    da-=180
    //以下公式也确保aa=模数180
    //当ww=0时(第一次迭代)。
    aa+=da*w/(w+ww)
    ww+=w
    //夹紧结果为[0°…+360°][
    如果aa>=360:
    aa-=360
    如果aa<0:
    aa+=360
    //将最终结果aa夹紧至[0..+180°[(可选步骤)
    如果aa>180:
    aa-=180
    
    我没有证明结果与迭代顺序无关,但乍一看,算法应该是这样的


    该算法对输入迭代顺序的依赖性 对于行为良好的输入数据,无论迭代顺序如何,该算法都非常稳定

    然而,一旦输入数据没有明确的主方向,该算法的结果将在难以预测的混沌模式下强烈依赖于迭代顺序

    数值模拟表明,对于标准偏差小于20°(中值附近)的随机方向,算法似乎总是稳定的。当标准偏差大于20°时,数值不稳定性开始出现,结果强烈依赖于迭代顺序(20°和30°之间的差异可能小到可以忽略,超过30°的差异开始出现)

    我没有精确地计算出准确的混沌/稳定标准偏差截止值,因此将此20°值作为初始猜测。读者可以练习一个精确的数学解

    下面是数值模拟的结果(对于0到45°的每个标准偏差,运行1000次算法)
    MeanAngle = ArcTan2(Sum{i=1..n}(Sin(Alpha[i])), Sum{i}(Cos(Alpha[i])))
    
    MeanAngleWithoutDir = ArcCos(1/n * Sum{i=1..n}(Cos(Alpha[i])))
    
    aa = 0
    ww = 0
    for a, w in segments:
        // Compute delta between angles in range [-180°..+180°[
        da = a - aa
        if da < -180:
           da += 360
        if da >= 180:
           da -= 360
        // Optional direction swap, delta in [-90°..+90°[
        if da < -90:
           da += 180
        if da >= 90:
           da -= 180
        // The following formula also make sure aa = a mod 180
        // when ww = 0 (first iteration).
        aa += da * w / (w + ww)
        ww += w
        // Clamp result to [0°..+360°[
        if aa >= 360:
           aa -= 360
        if aa < 0:
           aa += 360
    // Clamp final result aa to [0..+180°[ (optional step)
    if aa > 180:
        aa -= 180
    
    double dvx=0,dvy=0;
    for(const auto &direction:directions)
    {
        dvx+=2*direction.dx*direction.dy;
        dvy+=squared(directions.dx)-squared(directions.dy);
    }
    return std::atan2(dvx,dvy)/2;//or may be +pi/2