我正在寻找一种公式,用于在圆周上找到两个度数标记之间的最短距离:例如,30度和170度之间的最短距离是140度。
这两个度数标记可以是任意实数,不一定在0到360之间(可以为负数,或者比360大得多,例如-528.2和740(等于171.8度))。然而,距离应始终为<= 180度且>= 0度。
听起来很简单。但是,我一直在尝试寻找一个好的解决方案,并尝试了许多不同的代码,但迄今为止我尝试过的所有情况都没有奏效。我使用的编程语言是C ++。有人有什么想法吗?
我正在寻找一种公式,用于在圆周上找到两个度数标记之间的最短距离:例如,30度和170度之间的最短距离是140度。
这两个度数标记可以是任意实数,不一定在0到360之间(可以为负数,或者比360大得多,例如-528.2和740(等于171.8度))。然而,距离应始终为<= 180度且>= 0度。
听起来很简单。但是,我一直在尝试寻找一个好的解决方案,并尝试了许多不同的代码,但迄今为止我尝试过的所有情况都没有奏效。我使用的编程语言是C ++。有人有什么想法吗?
步骤1:获取“原始”差异。例如,给定 -528.2
和 740.0
,得到的结果是 1268.2
。
raw_diff = first > second ? first - second : second - first
raw_diff = std::fabs(first - second)
步骤2:减去 360.0
的倍数,以获得介于 0.0
(包括)和 360.0
(不包括)之间的值。
mod_diff = std::fmod(raw_diff, 360.0)
步骤3:如果该值大于 180.0
,则从 360.0
中减去它。
dist = mod_diff > 180.0 ? 360.0 - mod_diff : mod_diff
dist = 180.0 - std::fabs(mod_diff - 180.0)
最好将其作为一系列语句阅读:
double raw_diff = first > second ? first - second : second - first;
double mod_diff = std::fmod(raw_diff, 360.0);
double dist = mod_diff > 180.0 ? 360.0 - mod_diff : mod_diff;
但是如果需要的话,把这一切放进一个表达式中也不难:
180.0 - std::fabs(std::fmod(std::fabs(first - second), 360.0) - 180.0)
#define MAX_VALUE 360
float shortestSignedDistanceBetweenCircularValues(float origin, float target){
float signedDiff = 0.0;
float raw_diff = origin > target ? origin - target : target - origin;
float mod_diff = fmod(raw_diff, MAX_VALUE); //equates rollover values. E.g 0 == 360 degrees in circle
if(mod_diff > (MAX_VALUE/2) ){
//There is a shorter path in opposite direction
signedDiff = (MAX_VALUE - mod_diff);
if(target>origin) signedDiff = signedDiff * -1;
} else {
signedDiff = mod_diff;
if(origin>target) signedDiff = signedDiff * -1;
}
return signedDiff;
}
您也可以使用向量数学和三角函数;这里的角度应该是弧度制。
float angle(float angle1, float angle2)
{
float x1=cos(angle1);
float y1=sin(angle1);
float x2=cos(angle2);
float y2=sin(angle2);
float dot_product = x1*x2 + y1*y2;
return acos(dot_product);
}
fabs
+fmod
方法可以运行得快约 20 倍,并且产生数量级更少的舍入误差。 - ruakhfloat find_shortest_angle(float first, float second)
{
//first = 350., second = 10.; //example rollover 350 to 10 = 20 deg
first = 10., second = 350.; //example rollover 10 to 350 = -20 deg
float diff, sweep_angle;
diff = first-second;
if(diff<-180)
sweep_angle= -(360+diff);
else if(diff>180)
sweep_angle= 360-diff;
else
sweep_angle= -diff;
return sweep_angle;
}
我曾经遇到过一个类似的问题,需要找到圆中任意两点之间的最短距离。我的解决方案如下:
0 -> N-1
j before n/2 after (n-j)
1 -> N-1
(j-1) before [(n/2)+1] after n-j+1
2 -> N-1
(j-2) before [(n/2)+2] after n-j+2
and so on
其中j是第二个点,i是第一个点
这里是解决方案的Python代码。
for i in range(0, n):
for j in range(i,n):
if j < n/2+i:
s_rt = j-i
else :
s_rt = n-j+i
我认为可以通过对角度进行微调来找到解决方案。
当然,你需要导入math库,以使用fmod和fabs函数。
double a = -528.2;
double b = 740.0;
double diff = (a > b ? a - b : b - a);
double mod_diff = fmod(diff, 360);
double result = (mod_diff < 180 ? mod_diff : 360 - mod_diff);
a
是0
,而b
是200
,你的代码会给出20
而不是160
。 - ruakh你可以将你在这里找到的公式(http://en.wikipedia.org/wiki/Arc_(geometry))应用于两个角度和两个方向。 因此,您可以找到两个互补的距离(如果将它们相加,您将获得周长(或者您可以通过将另一个弧的长度从周长中减去来获得一个弧的长度)。
您可以比较这两个长度以获取不同角度之间两点之间的最小距离。
在C++中,您有math.h库:http://www.cplusplus.com/reference/clibrary/cmath/
对于像我这样的初学者,其他答案虽然很可能给出一个好的结果,但是很难理解发生了什么。因此,这是我的方法来检查当前点(cp)和目标点(tp)之间哪个方向(顺时针或逆时针)最短。它还将最短距离值分配给shortestDistance变量。根据我特别需要此方法的内容,重要的是我返回一个布尔值,指示最短距离是顺时针还是逆时针方向。以下是该方法:
public boolean checkIfClockwiseIsShortest(int cp, int tp) {
boolean clockwiseIsShortest = false;
if (cp != tp) { // if current point and target point are not the same...
if (tp > cp) { // if current point is less than target point AND..
if ((tp - cp) <= ((360 - tp) + cp)) {
clockwiseIsShortest = true;
shortestDistance = (tp-cp);
System.out.println("Case A: " + shortestDistance +" degrees clockwise");//[CAN REMOVE]
} else if ((tp - cp) > ((360 - tp) + cp)) {
clockwiseIsShortest = false;
shortestDistance = ((360 - tp) + cp);
System.out.println("Case B: " + shortestDistance+" degrees counter-clockwise");//[CAN REMOVE]
}
} else { // BUT if target point < current point
if ((cp - tp) <= ((360 - cp) + tp)) {
clockwiseIsShortest = false;
shortestDistance = (cp-tp);
System.out.println("Case C: " + shortestDistance+" degrees counter-clockwise");//[CAN REMOVE]
} else if ((cp - tp) > ((360 - cp) + tp)) {
clockwiseIsShortest = true;
shortestDistance = ((360 - cp) + tp);
System.out.println("Case D: " + shortestDistance+" degrees clockwise");//[CAN REMOVE]
}
}
}
return clockwiseIsShortest;
}
请注意,cp是整数度数的起点,tp是整数度数的目标点;类型可以更改为double以获得更高的精度。
它考虑了穿过0度标记的顺时针或逆时针方向。
它检查的3个if:
同样,其他代码较短的帖子也可能完全正常。
编辑:顺便说一下,shortestDistance变量是类变量,在与此方法相同的类中初始化;如果您要使用此代码,请确保您放置它的类也具有基于与cp和tp变量相同的原始类型的shortestDistance变量(或将其强制转换)。
#include <iostream>
#include <cmath>
using namespace std;
int degree_difference(int a, int b)
{
return abs(a%360-b%360);
}
int main()
{
int result = degree_difference(-235, 15);
cout << "Difference: " << result << endl;
return 0;
}
我们必须假设一个圆只有360度,否则就会变得棘手起来。
因此,你首先要做的是使每个标记都在0到360之间。为了做到这一点,你可以将两个标记分别除以360取模运算。如果结果小于0,则加上360。
假设我们的点是520和-45。
mark1 = ((520 % 360) >= 0) ? (520 % 360) : 360 - (520 % 360);
mark2 = ((-45 % 360) >= 0) ? (-45 % 360) : 360 - (-45 % 360);
mark1 将为 160。mark2 将为 315。
现在,你只需取差的绝对值:
result = abs(mark1 - mark2) = 155
abs(deg1 - deg2)
会给出差异,然后进行一些简单的模数学运算使其始终小于180即可。 - Marc B