Algorithm 计算无向线段平均方向的算法
我有一组二维无向线段,由两个端点组成。从统计上看,他们中的大多数人或多或少都处在同一个方向上 我想计算的是段集的平均方向(例如,如果该集为全局N/S,它将返回~0°等)。请注意,我不关心返回哪个实际方向(0°或180°也可以) 在[0..180°范围内夹紧每个段的方向并取平均值不起作用(例如,两个段,一个为1°,另一个为-1°:第二个将夹紧到179°,平均值错误,此处90°应为0°) 我还考虑将“标准化线段”端点分为两组,并计算由两组中点组成的线段的方向,但这对于任务来说似乎有点复杂。所谓“标准化线段”,我指的是在单位圆上有端点,在原点上有中点的线段 有已知的算法/公式吗?有求角度平均值的算法/公式(在全圆周范围内) 对于您的情况,似乎可以计算方向向量的余弦平均值(因为Cos(-alpha)=Cos(alpha)),并得到ArcCos(范围为0..Pi)Algorithm 计算无向线段平均方向的算法,algorithm,geometry,Algorithm,Geometry,我有一组二维无向线段,由两个端点组成。从统计上看,他们中的大多数人或多或少都处在同一个方向上 我想计算的是段集的平均方向(例如,如果该集为全局N/S,它将返回~0°等)。请注意,我不关心返回哪个实际方向(0°或180°也可以) 在[0..180°范围内夹紧每个段的方向并取平均值不起作用(例如,两个段,一个为1°,另一个为-1°:第二个将夹紧到179°,平均值错误,此处90°应为0°) 我还考虑将“标准化线段”端点分为两组,并计算由两组中点组成的线段的方向,但这对于任务来说似乎有点复杂。所谓“标准
可能角度应限制在(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
这可以进一步加速到O(n logn)
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