2つの角度の最小差


137

座標の周りに-PI-> PIの範囲の2つの角度が与えられた場合、それらの間の2つの角度の最小値は何ですか?

PIと-PIの差は2 PIではなくゼロであることを考慮に入れてください。

例:

中心から2本の線が出ている円を想像してください。これらの線の間には2つの角度があり、それらが内側で作る角度は小さい角度、外側が作る角度は大きい角度です。両方の角度を合計すると、完全な円になります。各角度が特定の範囲内に収まる場合、ロールオーバーを考慮して、より小さい角度値は何ですか


2
あなたが何を言っているのか理解する前に私は3回読んだ。例を追加するか、よりよく説明してください...
Kobi

中心から2本の線が出ている円を想像してください。これらの線の間には2つの角度があり、それらが内側で作る角度は小さい角度、外側で作る角度が大きい角度です。両方の角度を合計すると、完全な円になります。各角度が特定の範囲内に収まる場合、ロールオーバーを考慮して、より小さい角度値は何
ですか


2
@JimG。これは同じ質問ではありません。この質問では、他の質問で使用された角度P1は不正解です。それは、他の小さな角度です。また、角度が水平軸と一致することは保証されません
トム・J Nowell

回答:


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番目の部分は常に2つの可能な角度のうち小さい方を確認します
Tom J Nowell

1
たとえば、角度0と目標角度721がある場合、%360を実行したいかもしれませんが、正しい答えは1になります。上記の答えは361になります
Tom J Nowell

1
より簡潔ですが、潜在的に高価ですが、後者のアプローチの2番目のステートメントと同等ですが、ですa -= 360*sgn(a)*(abs(a) > 180)。(あなたがの無店舗の実装をした場合には、考えてみればsgnabs特性かもしれないが、実際には2回の乗算を必要とする補償するために開始すること、その後、。)
mmirate

1
「任意の角度の符号付き角度」の例は、1つの例外を除いて、ほとんどのシナリオで機能するようです。シナリオでは、double targetA = 2; double sourceA = 359;「a」は3.0ではなく-357.0になります
Stevoisiak

3
C ++では、std :: fmod(a、360)またはfmod(a、360)を使用して浮動小数点モジュロを使用できます。
ジョッピー

145

xは目標角度です。yはソースまたは開始角度です。

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

符号付きデルタ角度を返します。APIによっては、atan2()関数のパラメーターの順序が異なる場合があることに注意してください。


13
x-y角度の違いが得られますが、望ましい範囲外である可能性があります。この角度が単位円上の点を定義していると考えてください。その点の座標は(cos(x-y), sin(x-y))です。範囲が[-PI、PI]の場合を除いて、そのatan2ポイントの角度(と同等x-y)を返します。
2013


2
1行の簡単な解決策と私のために解決されました(選択された答えではありません;))。しかし、タンインバースはコストのかかるプロセスです。
Mohan Kumar

2
私にとっては、最もエレガントなソリューションです。残念なことに、計算コストがかかる可能性があります。
2016

私にとっても最もエレガントなソリューションです!私の問題を完全に解決しました(2つの可能な回転方向/角度から小さい方の符号付き回転角度を与える式が欲しい)。
ユルゲンブラウアー2017年

41

2つの角度がxとyの場合、それらの間の角度の1つはabs(x-y)です。もう1つの角度は(2 * PI)-abs(x-y)です。したがって、2つの角度の最小値は次のとおりです。

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

これにより、角度の絶対値が得られ、入力が正規化されていると想定されます(つまり、範囲内[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これは事実ですが、公平であるように、元の質問で指定されていないもの、特に非正規化された入力と符号保存についてテストをチェックしました。編集済み回答の2番目のバージョンは、テストに合格するはずです。
ローレンスゴンサルベス2015

2番目のバージョンも動作しません。たとえば、350と0を試してください。-10を返す必要がありますが、-350を返します
kjyv

@kjyvあなたが説明した行動を再現することはできません。正確なコードを投稿できますか?
ローレンスゴンサルベス

あ、ごめんなさい。私はあなたのバージョンを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モードでした。大丈夫だと思います。
David Jones、

新しい式を2階のコードに接続して、それがどうなるか見てみましょう!(ありがとう^ _ ^)
トムJノーウェル

1
PeterBの答えも正しいと確信しています。そして邪悪なハック。:)
デビッドジョーンズ

4
しかし、これには
トリガー

Javaの同等の式は何ですか?角度が度単位の場合。
Soley、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を「(M_PI + M_PI)」ではなく「M_PI + M_PI」として#defineしているため、線arg = arg - PIV2;はに拡張されarg = arg - M_PI + M_PI、何も行われません。
canton7 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.