Der kleinste Unterschied zwischen 2 Winkeln

137

Welchen Wert hat der kleinste der beiden Winkel zwischen zwei Winkeln im Bereich -PI -> PI um eine Koordinate?

Berücksichtigt man, dass die Differenz zwischen PI und -PI nicht 2 PI, sondern Null beträgt.

Beispiel:

Stellen Sie sich einen Kreis vor, bei dem zwei Linien aus der Mitte herauskommen. Zwischen diesen Linien befinden sich zwei Winkel, der Winkel, den sie innen bilden, auch bekannt als der kleinere Winkel , und der Winkel, den sie außen bilden, auch bekannt als der größere Winkel. Beide Winkel ergeben zusammen einen vollen Kreis. Da jeder Winkel in einen bestimmten Bereich passen kann, wie hoch ist der Wert für kleinere Winkel unter Berücksichtigung des Überschlags

Tom J Nowell
quelle
2
Ich habe dreimal gelesen, bevor ich verstanden habe, was du meinst. Bitte fügen Sie ein Beispiel hinzu oder erklären Sie es besser ...
Kobi
Stellen Sie sich einen Kreis vor, bei dem zwei Linien aus der Mitte herausragen. Zwischen diesen Linien befinden sich zwei Winkel, der Winkel, den sie innen bilden, auch bekannt als der kleinere Winkel, und der Winkel, den sie außen bilden, auch bekannt als der größere Winkel. Beide Winkel ergeben zusammen einen vollen Kreis. Angesichts der Tatsache, dass jeder Winkel in einen bestimmten Bereich passen kann, was ist der kleinere Winkelwert unter Berücksichtigung des Überschlags
Tom J Nowell
2
@ JimG. Dies ist nicht die gleiche Frage. In dieser Frage wäre der Winkel P1, der in der anderen Frage verwendet wird, die falsche Antwort. Es wäre der andere, kleinere Winkel. Es gibt auch keine Garantie dafür, dass der Winkel mit der horizontalen Achse liegt
Tom J Nowell

Antworten:

193

Dies ergibt einen vorzeichenbehafteten Winkel für alle Winkel:

a = targetA - sourceA
a = (a + 180) % 360 - 180

Beachten Sie, dass die moduloOperation in vielen Sprachen einen Wert mit dem gleichen Vorzeichen wie die Dividende zurückgibt (wie C, C ++, C #, JavaScript, vollständige Liste hier ). Dies erfordert eine benutzerdefinierte modFunktion wie folgt:

mod = (a, n) -> a - floor(a/n) * n

Oder so:

mod = (a, n) -> (a % n + n) % n

Wenn die Winkel innerhalb von [-180, 180] liegen, funktioniert dies auch:

a = targetA - sourceA
a += (a>180) ? -360 : (a<-180) ? 360 : 0

Ausführlicher:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180
bennedich
quelle
Einfacher und sinnvoller, laut vorzulesen, obwohl praktisch das Gleiche. Zuerst wird der Winkel ermittelt, der zweite Teil stellt sicher, dass der Winkel immer kleiner ist
Tom J Nowell,
1
Obwohl man vielleicht eine% 360 machen möchte, z. B. wenn ich den Winkel 0 und den Zielwinkel 721 hätte, wäre die richtige Antwort 1, die Antwort von oben wäre 361
Tom J Nowell
1
Ein prägnanteres, wenn auch möglicherweise teureres Äquivalent zur zweiten Aussage des letzteren Ansatzes ist a -= 360*sgn(a)*(abs(a) > 180). (Denken Sie darüber nach, wenn Sie verzweigungslose Implementierungen von sgnund haben abs, dann könnte diese Eigenschaft tatsächlich anfangen, die Notwendigkeit von zwei Multiplikationen zu kompensieren.)
mmirate
1
Das Beispiel "Vorzeichenbehafteter Winkel für jeden Winkel" scheint in den meisten Szenarien mit einer Ausnahme zu funktionieren. Im Szenario double targetA = 2; double sourceA = 359;'a' ist gleich -357,0 statt 3,0
Stevoisiak
3
In C ++ können Sie std :: fmod (a, 360) oder fmod (a, 360) verwenden, um Gleitkomma-Modulo zu verwenden.
Joeppie
145

x ist der Zielwinkel. y ist die Quelle oder der Startwinkel:

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

Es gibt den vorzeichenbehafteten Delta-Winkel zurück. Beachten Sie, dass die Reihenfolge der Parameter für die Funktion atan2 () je nach API unterschiedlich sein kann.

Peter B.
quelle
13
x-ygibt Ihnen den Unterschied im Winkel, aber es kann außerhalb der gewünschten Grenzen liegen. Stellen Sie sich diesen Winkel vor, der einen Punkt auf dem Einheitskreis definiert. Die Koordinaten dieses Punktes sind (cos(x-y), sin(x-y)). atan2Gibt den Winkel für diesen Punkt zurück (der äquivalent zu ist x-y), außer dass sein Bereich [-PI, PI] ist.
Max
3
Dies besteht die Testsuite gist.github.com/bradphelan/7fe21ad8ebfcb43696b8
Bradgonesurfing
2
eine einzeilige einfache Lösung und für mich gelöst (nicht die ausgewählte Antwort;)). Aber Tan Inverse ist ein kostspieliger Prozess.
Mohan Kumar
2
Für mich die eleganteste Lösung. Schade, dass es rechenintensiv sein könnte.
Focs
Für mich auch die eleganteste Lösung! Ich habe mein Problem perfekt gelöst (wollte eine Formel haben, die mir den vorzeichenbehafteten Drehwinkel gibt, der aus den beiden möglichen Drehrichtungen / -winkeln der kleinere ist).
Jürgen Brauer
41

Wenn Ihre beiden Winkel x und y sind, ist einer der Winkel zwischen ihnen abs (x - y). Der andere Winkel ist (2 * PI) - abs (x - y). Der Wert des kleinsten der beiden Winkel ist also:

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

Dies gibt Ihnen den absoluten Wert des Winkels und setzt voraus, dass die Eingänge normalisiert sind (dh innerhalb des Bereichs [0, 2π)).

Wenn Sie das Vorzeichen (dh die Richtung) des Winkels beibehalten und auch Winkel außerhalb des Bereichs akzeptieren möchten, können [0, 2π)Sie das Obige verallgemeinern. Hier ist Python-Code für die verallgemeinerte Version:

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

Beachten Sie, dass sich der %Operator nicht in allen Sprachen gleich verhält, insbesondere wenn negative Werte betroffen sind. Wenn also eine Portierung vorgenommen wird, sind möglicherweise einige Vorzeichenanpassungen erforderlich.

Laurence Gonsalves
quelle
1
@bradgonesurfing Das ist / war wahr, aber um fair zu sein, haben Ihre Tests auf Dinge überprüft, die in der ursprünglichen Frage nicht spezifiziert wurden, insbesondere nicht normalisierte Eingaben und Zeichenerhaltung. Die zweite Version in der bearbeiteten Antwort sollte Ihre Tests bestehen.
Laurence Gonsalves
Die zweite Version funktioniert auch bei mir nicht. Versuchen Sie zum Beispiel 350 und 0. Es sollte -10 zurückgeben, aber
-350 zurückgeben
@kjyv Ich kann das von Ihnen beschriebene Verhalten nicht reproduzieren. Können Sie den genauen Code posten?
Laurence Gonsalves
Ah, es tut mir leid. Ich habe genau Ihre Version mit rad und Grad in Python erneut getestet und es hat gut funktioniert. Also muss ein Fehler in meiner Übersetzung nach C # gewesen sein (habe es nicht mehr).
kjyv
2
Beachten Sie, dass Sie ab Python 3 Tau nativ verwenden können! Schreib einfach from math import tau.
Mhartl
8

Ich stelle mich der Herausforderung, die unterschriebene Antwort zu geben:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)
David Jones
quelle
1
Ah ... die Antwort ist übrigens eine Python-Funktion. Entschuldigung, ich war für einen Moment im Python-Modus. Hoffe das ist okay.
David Jones
Ich werde die neue Formel oben in meinen Code einfügen und sehen, was daraus wird! (Danke ^ _ ^)
Tom J Nowell
1
Ich bin mir ziemlich sicher, dass auch die Antwort von PeterB richtig ist. Und böse hackisch. :)
David Jones
4
Aber dieser enthält keine Triggerfunktionen :)
Nornagon
Was ist die äquivalente Formel für Java? wenn die Winkel in Grad sind.
Soley
6

Für UnityEngine-Benutzer besteht die einfache Möglichkeit darin, nur Mathf.DeltaAngle zu verwenden .

Josh
quelle
1
Hat keine signierte Ausgabe tho
kjyv
2

Ein effizienter Code in C ++, der für jeden Winkel und sowohl für Bogenmaß als auch für Grad funktioniert, lautet:

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);
}
Adriel Jr.
quelle
-1

Trigonometrische Funktionen müssen nicht berechnet werden. Der einfache Code in C-Sprache lautet:

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

sei dif = a - b im Bogenmaß

dif = difangrad(a,b);

sei dif = a - b in Grad

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

Keine Sünde, kein Cos, keine Bräune, ... nur Geometrie !!!!

Uli Gue
quelle
7
Fehler! Da Sie # PIV2 als "M_PI + M_PI" und nicht als "(M_PI + M_PI)" definieren, wird die Zeile arg = arg - PIV2;erweitert arg = arg - M_PI + M_PIund führt zu nichts.
Kanton7