2个角度之间的最小差异


137

给定围绕坐标的-PI-> PI范围内的2个角度,它们之间2个角度中的最小角度值是多少?

考虑到PI和-PI之间的差异不是2 PI,而是零。

例:

想象一个圆,有2条线从中心出来,这些线之间有2个角度,它们在内侧形成的角度也称为较小的角度,而在外侧形成的角度也称为较大的角度。将两个角度相加时,将形成一个完整的圆。考虑到每个角度都可以在一定范围内,考虑到翻转,较小的角度值是多少


2
在理解您的意思之前,我阅读了3次。请添加示例,或进行更好的解释...
Kobi

想象一个圆,有2条线从中心伸出,这两条线之间有2个角度,它们在内侧形成的角度也称为较小的角度,而在外侧形成的角度也称为较大的角度。将两个角度相加时,将形成一个完整的圆。考虑到每个角度都可以在一定范围内,考虑到翻转,较小的角度值是多少
Tom J Nowell


2
@吉姆 这不是同一个问题,在另一个问题中使用的角度P1将是错误的答案,在另一个较小的角度将是错误的答案。同样,不能保证角度与水平轴
成正比

Answers:


193

这给出了任何角度的正负号:

a = targetA - sourceA
a = (a + 180) % 360 - 180

注意多种语言的modulo操作返回的值与被除数的符号相同(例如C,C ++,C#,JavaScript,完整列表在此处)。这需要mod像这样的自定义函数:

mod = (a, n) -> a - floor(a/n) * n

或者:

mod = (a, n) -> (a % n + n) % n

如果角度在[-180,180]以内,则也可以:

a = targetA - sourceA
a += (a>180) ? -360 : (a<-180) ? 360 : 0

用更详细的方式:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180

更简单且更有意义,读出声来,但实际上是相同的事情,第一BTI算出的角度,第二部分可以确保其始终的2个可能的角度更小
汤姆Ĵ诺埃尔

1
尽管可能要执行%360,例如,如果我的角度为0且目标角度为721,则正确答案将为1,但以上给出的答案将为361
Tom J Nowell

1
与后一种方法的第二个语句相比,一种更简洁但可能更昂贵的方法是a -= 360*sgn(a)*(abs(a) > 180)。(想一想,如果您有sgnand的无分支实现abs,那么该特性实际上可能开始补偿需要两个乘法的时间。)
移植

1
除了一个例外,“签名角度为任何角度”的示例似乎在大多数情况下都适用。在场景中,double targetA = 2; double sourceA = 359;“ a”等于-357.0而不是3.0
Stevoisiak

3
在C ++中,您可以使用std :: fmod(a,360)或fmod(a,360)来使用浮点模。
Joeppie

145

x是目标角度。y是来源或起始角度:

atan2(sin(x-y), cos(x-y))

它返回带符号的角度。请注意,根据您的API,atan2()函数的参数顺序可能有所不同。


13
x-y给您带来角度上的差异,但可能超出了所需范围。考虑一下在单位圆上定义一个点的角度。该点的坐标为(cos(x-y), sin(x-y))atan2返回该点的角度(等于x-y),但范围为[-PI,PI]。
2013年


2
一线简单的解决方案,并为我解决(不是选定的答案;))。但是棕褐色逆过程是一个昂贵的过程。
Mohan Kumar

2
对我来说,最优雅的解决方案。可惜它可能在计算上昂贵。
focs

对我来说,也是最优雅的解决方案!完美地解决了我的问题(希望有一个公式可以给我带符号的转向角,该角度是两个可能的转向方向/角度中较小的一个)。
尔根·布劳尔

41

如果两个角度分别为x和y,则它们之间的角度之一为abs(x-y)。另一个角度是(2 * PI)-abs(x-y)。因此,两个角度中最小的一个值为:

min((2 * PI) - abs(x - y), abs(x - y))

这将为您提供角度的绝对值,并假定输入已标准化(即:在range内[0, 2π))。

如果要保留角度的符号(即:方向)并且也接受角度范围以外的角度,则[0, 2π)可以将其概括。这是通用版本的Python代码:

PI = math.pi
TAU = 2*PI
def smallestSignedAngleBetween(x, y):
    a = (x - y) % TAU
    b = (y - x) % TAU
    return -a if a < b else b

请注意,%运算符在所有语言中的行为均不相同,尤其是在涉及负值时,因此,如果需要移植,可能需要进行一些符号调整。


1
@bradgonesurfing确实如此,但是为了公平起见,您的测试检查了原始问题中未指定的内容,特别是非规范化输入和符号保留。编辑后的答案中的第二个版本应通过您的测试。
劳伦斯·贡萨尔维斯

第二个版本对我也不起作用。尝试以350和0为例。它应该返回-10,但返回-350
kjyv

@kjyv我无法重现您描述的行为。您可以发布确切的代码吗?
劳伦斯·贡萨尔维斯(Lawrence Gonsalves),

啊对不起 我再次在python中使用rad和degree精确测试了您的版本,并且工作正常。因此,在我对C#的翻译中一定犯了一个错误(不再有)。
kjyv

2
请注意,从Python 3开始,您实际上可以原生使用tau!只是写from math import tau
mhartl

8

我面临着提供签名答案的挑战:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)

1
嗯...答案是Python函数。抱歉,我暂时处于Python模式。希望没事。
大卫·琼斯

我将新公式插入楼上的代码中,看看它会变成什么!(谢谢^ _ ^)
汤姆·J·诺维尔

1
我很确定PeterB的答案也是正确的。和邪恶的hackish。:)
David Jones

4
但这不包含trig函数:)
nornagon 2010年

Java的等效公式是什么?如果角度是度数?
索利2015年



2

在C ++中适用于任意角度和弧度和角度的高效代码是:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

-1

无需计算三角函数。用C语言编写的简单代码是:

#include <math.h>
#define PIV2 M_PI+M_PI
#define C360 360.0000000000000000000
double difangrad(double x, double y)
{
double arg;

arg = fmod(y-x, PIV2);
if (arg < 0 )  arg  = arg + PIV2;
if (arg > M_PI) arg  = arg - PIV2;

return (-arg);
}
double difangdeg(double x, double y)
{
double arg;
arg = fmod(y-x, C360);
if (arg < 0 )  arg  = arg + C360;
if (arg > 180) arg  = arg - C360;
return (-arg);
}

令dif = a-b,以弧度为单位

dif = difangrad(a,b);

令dif = a-b,以度为单位

dif = difangdeg(a,b);

difangdeg(180.000000 , -180.000000) = 0.000000
difangdeg(-180.000000 , 180.000000) = -0.000000
difangdeg(359.000000 , 1.000000) = -2.000000
difangdeg(1.000000 , 359.000000) = 2.000000

没有罪,没有cos,没有棕褐色,.....只有几何!


7
臭虫!由于您将PIV2 #define定义为“ M_PI + M_PI”,而不是“(M_PI + M_PI)”,因此该行将arg = arg - PIV2;扩展为arg = arg - M_PI + M_PI,因此不会执行任何操作。
canton7 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.