角度の比較と違いの解決


27

角度を比較して、それらの間の距離を把握したいと思います。このアプリケーションでは、度単位で作業していますが、ラジアンや卒業生でも動作します。角度の問題は、モジュラー演算、つまり0〜360度に依存することです。

1つの角度が15度、1つの角度が45度であるとします。差は30度で、45度の角度は15度の角度よりも大きくなります。

しかし、これは、たとえば345度と30度の場合に破綻します。それらは適切に比較されますが、それらの差は正しい45度ではなく315度です。

どうすれば解決できますか?アルゴリズムコードを書くことができます。

if(angle1 > angle2) delta_theta = 360 - angle2 - angle1;
else delta_theta = angle2 - angle1;

しかし、私は比較/分岐を避け、完全に算術に依存するソリューションを好むでしょう。


この問題について、与えられた角度が範囲[0,360]または(-infinite、+ infinite)にあると仮定できますか?たとえば、アルゴリズムは-130度と450を比較する際にも機能しますか?
エガルシア

角度はその範囲に正規化されていると仮定します。
トーマスO

回答:


29

以下は、単純化された、ブランチレス、比較フリー、最小/最大なしのバージョンです。

angle = 180 - abs(abs(a1 - a2) - 180); 

入力が十分に制約されているため、モジュロを削除しました(指摘してくれたMartinに感謝します)。

2つの腹筋、3つの減算。


モジュロは必要ありません。入力値は[0,360]の範囲に制限されます(元の投稿に対するThomasのコメントを参照)。きれいです。
マーティンソイカ

ああ、はい、あなたは正しいです。試してみたところ、入力があまり厳しくありませんでした。
JasonD

しかし、どちらが左側にあるかを判別できるように、差の兆候を保持したい場合はどうでしょうか?
ジェイコブフィリップス

9

それらは適切に比較されますが、それらの差は正しい45度ではなく315度です。

315が間違っていると思う理由は何ですか?一方の方向では315度、もう一方の方向では45度です。2つの可能な角度のうち、どちらか小さい方を選択する必要があり、これには本質的に条件が必要と思われます。ラップアラウンド演算(つまり、モジュラス演算子を使用)では解決できません。1つの角度を徐々に大きくすると、それらの間の角度は180に達し、その後減少し始めるためです。

両方の角度を確認して測定する方向を決定するか、両方の方向を計算してどちらの結果を求めるかを決定する必要があると思います。


申し訳ありませんが、明確にする必要があります。逆にした場合、30-345は-315で、負の角度はあまり意味がありません。私は2つの間の最小の角度を探していると思います。すなわち45度は315よりも小さい
トーマス・O

2
ただし、「逆」はありません。2つの角度と2種類の回転を使用して、一方を他方に一致させることができます。負の角度は完全に理にかなっています-結局のところ、それは任意の軸からの回転の単なる尺度です。
キロタン

最小の角度が必要な場合、abs(a1%180-a2%180)はその角度を提供します。ただし、方向はわかりません。腹筋を削除すると、あなたのA1、A2「に」「から行く」最小の角度与えるだろう
チューイーガムボール

2
@Chewy、ハァッ?180と0との差が0ではない、と181と0との差が1 ...ない
ダッシュトム・バン

1
@ dash-tom-bangその通りです。私は何を考えていたのか分かりませんが、今それをもう一度見るとまったく正しくありませんでした。以前のコメントを無視してください。
歯ごたえガムボール

4

両方のブランチを実行し、比較結果にどちらかを選択させるコツが常にあります。

delta_theta = (angle1 > angle2) * (360 - angle2 - angle1)
              + (angle2 > angle1) * (angle2 - angle1);

比較せずにそれを行う方法はわかりませんが、通常は比較ではなくブランチがコードを遅くし、長くします。少なくとも私の意見では、これはMartinの答えよりも読みやすい(どんな優れたCプログラマーもブランチレスの同等物として認識し、何をしているのかを見るだろう)が、効率も悪い。

しかし、私がコメントで言ったように、分岐のないアルゴリズムは、深いパイプラインと悪い予測を備えたプロセッサーに適しています-マイクロコントローラーには通常小さなパイプラインがあり、デスクトップPCには通常良い予測があるので、ゲームコンソールをターゲットにしている場合を除き、分岐バージョン命令数を減らす場合、おそらく最適なルートです。

いつものように、プロファイリング(システムのop-countingと同じくらい簡単かもしれません)は、あなたに本当の答えを与えます。


2

trueが-1に評価され、falseが0に評価され、 '〜'、 '&'、および '|'であると仮定します はビット単位ではなくandおよびor演算子であり、2の補数演算を使用しています。

temp1 := angle1 > angle2
/* most processors can do this without a jump; for example, under the x86 family,
   it's the result of CMP; SETLE; SUB .., 1 instructions */
temp2 := angle1 - angle2
temp1 := (temp1 & temp2) | (~temp1 & -temp2)
/* in x86 again: only SUB, AND, OR, NOT and NEG are used, no jumps
   at this point, we have the positive difference between the angles in temp1;
   we can now do the same trick again */
temp2 := temp1 > 180
temp2 := (temp2 & temp1) | (~temp2 & (360 - temp1))
/* the result is in temp2 now */

+1は賢いためですが、マイクロコントローラーでは、これはおそらく分岐バージョンよりもパフォーマンスがはるかに悪いためです。

マイクロコントローラに少し依存しますが、はい、通常それは価値がありません。(短い)条件付きジャンプは通常、十分に高速です。また、3行目と5行目は、このようなxor(^)操作を使用して少し高速に書き換えることができますが、わかりやすくするために現在の形式のままにしておきます:temp1:= temp2 ^((temp2 ^ -temp2)& 〜temp1)、temp2:= temp1 ^((temp1 ^(360-temp1))&〜temp2)
マーティン・ソイカ

1

これはどうですか?

min( (a1-a2+360)%360, (a2-a1+360)%360 )

負の数のモジュロは負の結果を返すため、負の差を避けるために360を追加します。次に、2つの可能な結果のうち小さい方を取得します。

暗黙の決定がまだありますが、私はそれを避ける方法を知りません。基本的に、時計回りまたは反時計回りの差を計算して2つの角度を比較しますが、これら2つの差のうち小さい方を明示的に求めているようです。それらを比較せずにその結果を得る方法がわかりません。つまり、「abs」、「min」、「max」などの演算子を使用しません。


分岐命令なしでintのmin、max、absを計算する方法はいくつかありますが、これはマイクロコントローラであるため、分岐はおそらく最速の方法です。 graphics.stanford.edu/~seander/bithacks.html#IntegerAbs

1

あなたの質問はそれらについて言及していませんが、私はあなたの角度計算の質問は2つのベクトル間の最小角度を知りたいということから生じるという仮定に取り組んでいきます。

その計算は簡単です。AとBがベクトルであると仮定します。

angle_between = acos( Dot( A.normalized, B.normalized ) )

ベクトルがなく、このアプローチを使用したい場合は、を行うことで角度を指定して単位長さのベクトルを作成できますnew Vector2( cos( angle ), sin ( angle ) )


1
私が取り組んでいるプロセッサは小さなマイクロコントローラです。角度間の差を得るためだけにトリガー関数を使用してベクトルを生成することは意味がありません。すべてのサイクルは貴重です。
トーマスO

1
マイクロコントローラーでは、ブランチを使用する方が良いとは思いませんが、ブランチを本当に避けたい場合は、答えにはあまり算術的ではありません。
JasonD

分岐は2サイクルで、加算/減算などは1サイクルですが、分岐は追加のプログラムメモリも占有します。重要ではありませんが、いいでしょう。
トーマスO

私はあなたの答えが正しく、私のものが間違っていると感じますが、なぜそうなのか私は頭を悩ませることができません。:)
キュロタン

1

基本的にJasonDの答えと同じですが、絶対値関数の代わりにビット演算を使用します。

これは、16ビットの短い整数があることを前提としています!

short angleBetween(short a,short b) {
    short x = a - b;
    short y = x >> 15;
    y = ((x + y) ^ y) - 180;
    return 180 - ((x + y) ^ y);
}


0

算術以上の分岐と「複雑な」演算を排除することだけに関心があるので、これをお勧めします。

min(abs(angle1 - angle2), abs(angle2 - angle1))

absすべての角度がプラスであるにもかかわらず、まだそこにいる必要があります。それ以外の場合、最も否定的な結果が常に選択されます(abとbaを比較するとき、肯定的な一意のaとbに対して常に1つの否定的な答えが常に存在します)。

注:これは、angle1とangle2の間の方向を保持しません。AIの目的で必要になる場合があります。

これはCeeJayの答えに似ていますが、すべてのモジュールを排除します。サイクルコストが何であるかはわかりませんがabs、1または2であると推測minします。コストが何であるかを言うのも難しいです。たぶん3?したがって、減算ごとに1サイクルと一緒に、この行には約4〜9のコストが必要です。


0

haveからwantへの観点から、符号付き(+/-)形式でより小さい相対角度を取得します

  • 最大 180度| PIラジアン
  • -反時計回りの場合に署名
  • +時計回りに署名

PITAU = 360 + 180 # for readablility
signed_diff = ( want - have + PITAU ) % 360 - 180

ラジアン

PI = 3.14; TAU = 2*PI; PITAU = PI + TAU;
signed_diff = ( want - have + PITAU ) % TAU - PI

根拠

モジュロを回避するソリューションを探して、これを見つけた後にこのスレッドに出会いました。これまでのところ、私は何も見つけていません@ jacob-phillipsがこのコメントを尋ねたように、このソリューションは遠近法記号を保存するためのものです。最短の符号なし角度が必要な場合は、より安価なソリューションがあります。


0

それは古い質問ですが、私は同じ場合に遭遇しました-符号付きの角度差を取得しなければならず、できれば枝と重い数学なしで。これが私がやったことです:

int d = (a - b) + 180 + N * 360; // N = 1, 2 or more.
int r = (d / 360) * 360;
return (d - r) - 180;

制限は、「b」は「a」と比較して「N」回転を超えてはならないということです。確認できず、追加の操作を許可できる場合は、これを最初の行として使用します。

int d = ((a % 360) - (b % 360)) + 540;

この投稿の13番目のコメントからアイデアを得ました:http : //blog.lexique-du-net.com/index.php? post/ Calculate- the- real-difference-between-two-angles-keeping-the-符号


-1

言えると思います

angle1=angle1%360;
angle2=angle2%360;
var distance = Math.abs(angle1-angle2);
//edited
if(distance>180)
  distance=360-distance;

もちろん、角度は度で測定されることを考慮してください。


1
これで問題が解決するとは思わない。345パーセント360 == 345、およびABS(345から30まで)は依然として315である
グレゴリー・エイブリー・ウィアー

@グレゴリー:大丈夫!、間違えてごめんなさい。返信を編集しています。この新しい返信を確認してください。:)
ヴィシュヌ

1
ところで、angle1 = angle1%360; angle2 = angle2%360; var distance = Math.abs(angle1-angle2); var distance = Math.abs(angle1-angle2)%360と同じ-少し遅い。
マーティンソイカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.