【问题标题】:Shortest distance between two degree marks on a circle?圆上两个度数标记之间的最短距离?
【发布时间】:2012-03-19 08:05:04
【问题描述】:

我正在寻找一个公式来找出圆上两个度数标记之间的最短距离(以度为单位):例如,30 度和 170 度(140 度)。

两度标记几乎可以是任何实数,不一定介于 0 和 360 之间(可以是负数,也可以远大于 360,例如 -528.2 和 740(即 171.8 度))。但是,距离应始终为 = 0 度。

听起来很简单。但是,我一直在尝试为此找到一个好的解决方案,并且我尝试了很多不同的代码,但到目前为止我发现在我尝试过的所有情况下都没有。我在 C++ 中工作。有人有什么想法吗?

【问题讨论】:

  • abs(deg1 - deg2) 会给你带来差异,然后一些简单的模数学使其始终为
  • 根据您的示例,听起来您取两个数字的绝对值,然后从最大值中减去最小值。
  • @RobertHarvey:这实际上是行不通的。想象一下起点是-90。覆盖 4 个点,你有 -90(又名 270)、0、90 和 180。如果你有一个点在 -90,另一个点在 135,那么它们之间的最小距离实际上是 135。
  • @ChrisLively:是的,我意识到你需要模数计算。

标签: c++ trigonometry geometry


【解决方案1】:

您可以在两个角度和两个方向上应用您在此处找到的公式 (http://en.wikipedia.org/wiki/Arc_(geometry))。 所以你找到了两个互补的距离(如果你把它们加起来你会得到周长(或者你可以通过从周长中减去另一条弧的长度来得到一条弧的长度)。

然后您可以比较两个长度以获得不同角度的两个点之间的最小距离。

在 C++ 中,您有 math.h 库:http://www.cplusplus.com/reference/clibrary/cmath/

【讨论】:

  • 试试看。我建议你先在纸上做,然后用 C++ 实现。
【解决方案2】:

当然,您需要为 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);

【讨论】:

  • 这不太对。如果(比如说)a0 并且 b200,那么您的代码将给出 20 而不是 160
  • 感谢您的回答,但我找到了一个反例。假设 a = 10 和 b = 270。a 小于 b,所以答案是 (270 - 10) mod 180。但是 260 mod 180 是 80,但实际距离是 100。
  • 好了,我实际上(对我感到羞耻)测试了这段代码,现在它给了我 171.8 的结果。
  • 也适用于 a=10, b=270, result=100。并且在 a & b 切换时表现正常。
【解决方案3】:
  • 第 1 步:获取“原始”差异。例如,给定-528.2740.0,这是1268.2

    • 一种方式:raw_diff = first &gt; 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 &gt; 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)

【讨论】:

  • 这很好用。如果从第一到第二的最短距离是逆时针的,是否可以修改为返回负距离,如果是顺时针,则返回正数?
  • @neoflash:可以做到,但它比评论大得多。
【解决方案4】:

您还可以使用矢量数学和三角函数;这里的角度将以弧度表示。

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 倍,并且产生的舍入减少了几个数量级。
  • 此外,您可以使用三角恒等式:(sin(a)sin(b)+cos(a)cos(b) = sin(ab) 来推导解:acos(sin(angle1 - 角度2))。
  • sin(a)sin(b)+cos(a)cos(b) 应该是 cos(a-b),而不是 sin(a-b)。 See here.
【解决方案5】:

你可以尝试得到两个角度余数除以360的绝对值。

#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;
}

【讨论】:

  • 等一下,我认为这根本不是你想要的。
【解决方案6】:

我们必须假设一个圆只有 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。mark 2 为 315。

现在你只需取差值的绝对值:

result = abs(mark1 - mark2) = 155

【讨论】:

    【解决方案7】:

    我在寻找时遇到了类似的问题

    • 圆中任意点到任意点的最短距离。 我得到的解决方案如下:

    如果 N = 圆中的点数

                    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
    

    我认为这可以通过稍微调整度数来找到解决方案。

    【讨论】:

      【解决方案8】:

      我一直在寻找这样的微控制器解决方案,用于寻找电动木偶的变速箱,但我没有正确计算三角的咕噜声。

      @ruakh 的回答是一个很好的基础,但我发现标志在某些情况下被错误地翻转。

      这是对我有用的解决方案。此解决方案适用于圆圈中的度数标记,但更改 MAX_VALUE 可以使其适用于测量齿轮编码器脉冲时有用的任意最大范围。

      在 Arduino 上测试。

      #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;
      
      }
      

      【讨论】:

        【解决方案9】:

        对于像我这样的初学者来说,其他答案 - 虽然很可能会给你一个很好的结果 - 有点难以理解发生了什么。因此,这是我检查 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 :

        1. 开始/当前点 (cp) 是否等于目标点 (tp)?如果是,则不给出距离。
        2. 如果不相等,是目标点 > 当前点的度数还是目标点
        3. 基于此,嵌套中的最后一个 if 检查顺时针方向是否比逆时针方向少(反之亦然)。

        同样,其他代码较短的帖子也可以正常工作。

        编辑:顺便说一句,shortestDistance 变量是一个类变量,在与此方法相同的类中初始化;如果您要翻录此代码,请确保您放置它的类也有一个 shortestDistance 变量,该变量基于原始类型作为 cp 和 tp 变量(或强制转换)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多