如何在没有数学函数(arctan)的点和组织(0,0)之间计算快速通道角度(最接近八个提供值之一)?

时间:2013-03-12 14:55:30

标签: c++ algorithm math mathematical-optimization

如何在没有数学函数(arctan)的点和组织(0,0)之间计算快速航向角度(最接近八个提供值之一)? 我已经将xy坐标系分为8段(0,45,90,135,180,225,270,315),我需要找到点和组织之间的角度(不准确只是上面八个最接近的值)没有重数学函数。 我可以找到像

std::pair<float,float> point;
float angle =arctan(point.second/point.first);
int index =static_cast<int>( (angle+22.5)/45);

然后按索引从数组中读取。 (我增加了22.5度,因为[-22.5, 22.5)=>0, [22.5,67.5)=>45,[67.5,112.5)=>90...) 是否有更快的方法,任何想法(执行时间非常重要)?

4 个答案:

答案 0 :(得分:9)

给定(a,b)a<ba>=0b>=0,是否接近45度或0度?

嗯,tan(theta) = opposite/adjacent,在0度到45度的范围内,棕褐色是单调递增的。

tan(22.5 degrees) =~ 207107/500000。因此,如果a/b > 207107/500000,最近的角度是45度。如果a/b < 207107/500000,则最近的角度为0度。我们甚至可以通过说500000*a < 207107*b而没有浮点数学来做到这一点。

对于任意点(a,b),我们可以通过a和b上的符号找出它所在的象限。我们可以将问题旋转(通过否定)到正 - 正象限,然后在结果角度上反转该旋转(这是一个非常简单的地图)。

对于正正象限中的任意(a,b),如果a>b只是反转a和b,则按上述方式求解,“接近0度”对应于“接近90度”。

上面的一些内容过于冗余,但您应该能够将这些分支转换为整数运算并以数组访问结束。

现在,请注意,在某些系统上,trig函数内在函数可以非常快,比一堆无分支整数运算和数组查找快得多。您的第一步应该是看看是否可以用arctan更快地替换arctan

bool neg_a = a<0;
bool neg_b = b<0;
a *= (1-2*neg_a);
b *= (1-2*neg_b);
bool near_0 = 500000*a<207107*b; // a/b < 207107/500000
bool near_90 = 207107*a>500000*b; // a/b > 500000/207107
bool near_45 = !near_0 & !near_90;
// 3 CW 2     1
//   -+ | ++
//  2-4 | 0-2 CCW
//4 ----+---- 0
//CCW-- | +- CW
//  4-6 | 6-8
// 5    6     7

// 0 1 or 2
int index = near_45 + 2*near_90;
// negating a or b reverses angle
index *= (1-2*neg_a);
index *= (1-2*neg_b);
// base is 4 if a is negative:
index += 4*(neg_a);
// base is 8 if b is negative, and a is not negative:
index += 8*(neg_b&!neg_a);
index &= 7;

return index;

这非常荒谬,但是没有分支。也没有调试。

答案 1 :(得分:1)

您可以通过比较其光线的渐变与八分圆边界的渐变,在22.5,67.5度等来计算该点所处的八分圆。所以:

static const float borders[] = { tan(-3 * PI / 8), tan(-PI / 8), tan(PI / 8), tan(3 * PI / 8) };
static const int angles[] = { 270, 315, 0, 45, 90, 135, 180, 225 };
float gradient = y / x;
int i = std::distance(borders, std::lower_bound(std::begin(borders), std::end(borders), gradient)) + (x < 0 ? 4 : 0);
int angle = angles[i];

答案 2 :(得分:1)

该解决方案基于最大化cos(theta-theta_i),其中theta_i为0,45,90,...,315度。我使用角度差同一性cos(ab)= cos(a)cos(b)-sin(a)sin(b)并使用x = r * cos(a)和y = r * sin(a)来简化不同的表达式,因此不必使用实际的trig函数:

int getIndex_simple(float x, float y){

  float rCosThetaMinusThetaI[8];
  rCosThetaMinusThetaI[0]=x;
  rCosThetaMinusThetaI[1]=(x+y)*M_SQRT1_2;
  rCosThetaMinusThetaI[2]=y;
  rCosThetaMinusThetaI[3]=(y-x)*M_SQRT1_2;
  rCosThetaMinusThetaI[4]=-x;
  rCosThetaMinusThetaI[5]=(-x-y)*M_SQRT1_2;
  rCosThetaMinusThetaI[6]=-y;
  rCosThetaMinusThetaI[7]=(x-y)*M_SQRT1_2;

  int best_i=0;
  float best_rCosThetaMinusThetaI=rCosThetaMinusThetaI[0];
  for(int i=1; i<8; i++)
    if( rCosThetaMinusThetaI[i]>best_rCosThetaMinusThetaI ){
      best_i=i;
      best_rCosThetaMinusThetaI=rCosThetaMinusThetaI[i];
    }

  return best_i;

}

您可以采取一些简单的措施来加快速度。例如,对于i&gt; = 4,rCosThetaMinusThetaI[i] == -rCosThetaMinusThetaI[i-4],因此您只需要四个变量。显然你不必使用数组,我这样做是为了让它易于阅读。此外,由于rCosThetaMinusThetaI[0]==xrCosThetaMinusThetaI[2]==y,您只需要两个变量。因此,我们可以将上述内容重写为一系列八个if语句:

int getIndex_optimized(float x, float y){

  float cosTheta1 = (x+y)*M_SQRT1_2;
  float cosTheta3 = (y-x)*M_SQRT1_2;

  int best_i=0;
  float best_cosTheta=x;
  if( best_cosTheta < cosTheta1 ){ best_i=1; best_cosTheta=cosTheta1; }
  if( best_cosTheta < y         ){ best_i=2; best_cosTheta=y; }
  if( best_cosTheta < cosTheta3 ){ best_i=3; best_cosTheta=cosTheta3; }
  if( best_cosTheta < -x        ){ best_i=4; best_cosTheta=-x; }
  if( best_cosTheta < -cosTheta1){ best_i=5; best_cosTheta=-cosTheta1; }
  if( best_cosTheta < -y        ){ best_i=6; best_cosTheta=-y; }
  if( best_cosTheta < -cosTheta3){ best_i=7; best_cosTheta=-cosTheta3; }

  return best_i;

}

这两个功能是C99代码;你可以使用-std = c99编译它们。你需要包含math.h来获得常量M_SQRT1_2 = 0.7071 ...

答案 3 :(得分:0)

在您使用的8条光线中,当该点距离x轴比y轴更近(或相同距离)时,使用一组四条光线。否则使用其他四个。
在这4个中,当x为正(或零)时使用右两个光线。否则另外两个使用 在这些中,当y为正时使用上部光线。否则使用较低的一个 因此,使用这些简单的测试在查找表中形成索引。

float ClosestAngle(std::pair<float,float> point) {
    float angle[8] = { 247.5 , 112.5f , 292.5 , 67.5f , 202.5 , 157.5 , 337.5 , 22.5f };

    int closerToXAxisThanYAxis = (abs(point.first) <= abs(point.second)) ? 4 : 0;
    int xIsPositive = (point.first >= 0) ? 2 : 0;
    int yIsPositive = (point.second >= 0) ? 1 : 0;

    return angle[closerToXAxisThanYAxis + xIsPositive + yIsPositive];
}