我有一组2D无向段,由两个端点组成。据统计,他们中的大多数人或多或少都处于相同的方向。
我想要计算的是段集的平均方向(例如,如果集合是全局N / S,它将返回〜0°等等...) 。请注意,我不关心返回哪个实际方向(0°或180°同样如此)。
在[0..180°范围内夹住每个线段的方向并且取平均值不起作用(例如两个线段,一个1°,另一个-1°:第二个将夹紧到179°并且平均值是错误的,这里是90°,应该是0°)。
我还想到将两组中的“归一化段”端点聚类,并计算由2个聚类中点组成的段的方向,但这对于任务来说似乎有点复杂。 “归一化段”是指在单位圆上有两个终点而在原点上有中间点的段。
是否有已知的算法/公式?
答案 0 :(得分:3)
据我了解,细分的位置并不重要,只有他们的方向。
所以我们可以稍微改变一下这个问题:我们有一组向量,我们想在它们上面加一条线。
我们可以采用不同的标准。常用的是最小二乘法。
对于此标准,解决方案是:
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
注意:对于此实现,方向将按其长度加权,如果要分配相同的权重,方向向量应该被标准化。
此方法有时用于确定指纹识别中的线条方向:http://jmit.us.edu.pl/cms/jmitjrn/22/28_Wieclaw_4.pdf
有几种方法可以理解这种方法。其中一个是几何的:
我们有一组X轴角度为alpha[i]
的矢量。我们不平均这些向量。相反,我们构建角度为2*alpha[i]
的向量,对它们求平均值并取得所得角度的一半。诀窍在于,如果相反的方向相差pi
,并且在加倍之后它们将相差2*pi
,这完全没有区别。
答案 1 :(得分:2)
有is method找到角度的平均值(在完整的圆形范围内)
MeanAngle = ArcTan2(Sum{i=1..n}(Sin(Alpha[i])), Sum{i}(Cos(Alpha[i])))
似乎对于你的情况,你可以计算方向向量的余弦的平均值(因为Cos(-alpha)= Cos(alpha)),并获得ArcCos(范围0..Pi)
MeanAngleWithoutDir = ArcCos(1/n * Sum{i=1..n}(Cos(Alpha[i])))
可能应将角度钳制到(0..Pi)或(-Pi / 2..Pi / 2)范围以避免歧义。
答案 2 :(得分:2)
元注释:此答案计算给定行的“中位数”。 MBo的other answer计算给定行的“平均值”。
让我们以下列方式将问题正式化。 我们给出了一组线,我们想要找到一条线 p ,使得 p 和所有给定线之间的角度之和尽可能小。 这里,两条线之间的角度是它们交叉点处的角度的最小值,或者如果它们是平行的或重合的则为0。 因此,两条线之间的角度始终为0到90度。
为了使事情更容易理解,翻译线条以使它们都通过原点。 显然,这不会影响答案。
为了解决这个问题,让我们研究一下这个总和的衍生物。 假设我们有一个答案行 p 。 让 x 线从 p 顺时针旋转0-90度, y 线从0开始逆时针旋转0-90度em> p ( x + y = n ,给出的总行数)。
现在,顺时针旋转 p 一个小角度α。 答案将减少 x *α并增加 y *α。 所以,如果 x> y ,答案会减少,如果 x<是,它会增加。
有两种情况,数量 x 和 y 会发生变化。
p 行符合其中一行。
q 行与其中一条给定行正交。
在圆上任意两个这样的连续点之间,我们的和的导数将是常数 x - y 。 因此,最小值将是“感兴趣的角度”之一:与某些给定线平行或正交。 这导致 O(n ^ 2)算法:对于每个感兴趣的 O(n)角,只计算 O(n)<中的和/ em>,并选择给出最小总和的角度。
这可以进一步加速到 O(n log n)。
在 O(n)中生成 2 n 感兴趣的角度。
在 O(n log n)中对它们进行排序。
计算答案,以及 x 和 y ,为 O(n)中的第一个角度。
沿着圆圈移动,保持当前答案以及值 x 和 y 。 在每个 O(n)步骤中,可以在 O(1)中进行计算。
答案 3 :(得分:2)
统计上,他们中的大多数人或多或少都是一样的 方向。
这一关键信息对于算法的设计至关重要。如果你知道所有的矢量都在90度锥形内,你可以使用一种非常简单的方法:
如果您需要处理更广泛的发行版,可以稍微修改一下:
这是一种串行算法,但如果您需要更高的性能,可以很容易地将其表示为并行缩减:
任何这些方法都可以轻松加权,因为您只关心点积的符号。
答案 4 :(得分:1)
在O(n)
解决方案下方,还可以为每个细分受众群提供可选的权重。
我们用X轴(a)与重量(w)的角度对每个段进行建模。此时段的方向并不重要,任何模数为180°的值都可以。我们的想法是循环每个细分并跟踪到目前为止计算的平均方向;并以模数180的方向调整此平均值,该方向更接近平均值。
伪码(以度为单位的所有角度):
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
我没有证明结果与迭代顺序无关,但乍一看算法应该是。
对于表现良好的输入数据,无论迭代顺序如何,算法都非常稳定。
然而,一旦输入数据没有明确的主方向,这个算法的结果将很大程度上取决于迭代次序,在难以预测的混沌模式中。 / p>
数值模拟显示,对于标准偏差小于20°(中位数左右)的随机方向,算法似乎总是稳定的。当标准偏差大于20°时,数值不稳定性开始出现,结果很大程度上取决于迭代次序(在20°和30°之间,差异可能小到足以忽略,超过30°的大差异开始出现)。
我没有精确计算出精确的混沌/稳定标准偏差截止值,因此将此20°值作为初始猜测。一个精确的数学解决方案留给读者练习。
在数值模拟结果下(对于0到45°的每个标准偏差,在给定标准偏差的各种随机数据上运行1000倍算法,并测量10次运行之间的平均差值)。
因此,为了获得最佳效果,如果您的输入数据不能保证具有较小的标准偏差,则最好在稳定键上输入数据(首先是更大的重量,或者根据您的输入选择任何其他键)。