2 Açı arasındaki en küçük fark


137

Bir koordinat etrafında -PI -> PI aralığında 2 açı verildiğinde, aralarındaki 2 açının en küçüğünün değeri nedir?

PI ve -PI arasındaki farkın 2 PI değil sıfır olduğunu dikkate alarak.

Misal:

Merkezden çıkan 2 çizgi ile bir daire düşünün, bu çizgiler arasında 2 açı vardır, içeride yaptıkları açı daha küçük açı ve dışarıda yaptıkları açı, daha büyük açı. Eklendiğinde her iki açı da tam bir daire oluşturur. Her açının belirli bir aralığa sığabileceği göz önüne alındığında , rollover dikkate alınarak daha küçük açı değeri nedir?


2
Ne demek istediğini anlamadan önce 3 defa okudum. Lütfen bir örnek ekleyin veya daha iyi açıklayın ...
Kobi

Merkezden çıkan 2 çizgi ile bir daire düşünün, bu çizgiler arasında 2 açı vardır, içeride yaptıkları açı daha küçük açı ve dışarıda yaptıkları açı, daha büyük açı. Eklendiğinde her iki açı da tam bir daire oluşturur. Her açının belirli bir aralığa sığabileceği göz önüne alındığında, rollover dikkate alınarak daha küçük açı değeri nedir
Tom J Nowell


2
@JimG. bu aynı soru değildir, bu soruda diğer soruda kullanılan P1 açısı yanlış cevap, diğer daha küçük açı olacaktır. Ayrıca, açının yatay eksende olduğuna dair bir garanti yoktur
Tom J Nowell

Yanıtlar:


193

Bu, herhangi bir açı için işaretli bir açı verir:

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

Birçok dilde dikkat edin, moduloişlem temettü ile aynı işarete sahip bir değer döndürür (C, C ++, C #, JavaScript, tam liste burada ). Bu, aşağıdaki modgibi özel bir işlev gerektirir :

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

Ya da:

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

Açılar [-180, 180] içindeyse bu da işe yarar:

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

Daha ayrıntılı bir şekilde:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180

Daha basit ve daha anlamlı bir şekilde yüksek sesle okunuyor, aynı şey etkili olsa da, ilk bti açıyı anlıyor, ikinci bölüm her zaman 2 olası açının daha küçük olmasını sağlıyor
Tom J Nowell

1
biri% 360 yapmak istese de, örneğin 0 açısına ve hedef açıya 721 sahip olsaydım, doğru cevap 1 olurdu, yukarıda verilen cevap 361 olurdu
Tom J Nowell

1
Daha kısa, ancak potansiyel olarak daha pahalı olsa da, ikinci yaklaşımın ikinci ifadesine eşdeğerdir a -= 360*sgn(a)*(abs(a) > 180). (Bunu düşünün, dalsız uygulamalarınız varsa sgnve abso zaman bu karakteristik aslında iki çarpma ihtiyacını telafi etmeye başlayabilir.)
mmirate

1
"Herhangi bir açı için işaretli açı" örneği, bir istisna dışında çoğu senaryoda işe yarar. double targetA = 2; double sourceA = 359;'A' senaryosunda 3.0 yerine -357,0 olacak
Stevoisiak

3
C ++ 'da kayan noktalı modulo kullanmak için std :: fmod (a, 360) veya fmod (a, 360) kullanabilirsiniz.
Joeppie

145

x, hedef açıdır. y kaynak veya başlangıç ​​açısıdır:

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

İşaretli delta açısını döndürür. API'nize bağlı olarak atan2 () işlevi için parametrelerin sırasının farklı olabileceğini unutmayın.


13
x-ysize açı farkı verir, ancak istenen sınırların dışında olabilir. Birim çember üzerindeki bir noktayı tanımlayan bu açıyı düşünün. Bu noktanın koordinatları (cos(x-y), sin(x-y)). aralığı [-PI, PI] dışında, atan2o noktanın açısını (eşdeğerdir x-y) döndürür .
Maksimum


2
tek satırlık basit bir çözüm ve benim için çözüldü (seçilen cevap değil;)). ancak ters rengi, maliyetli bir işlemdir.
Mohan Kumar

2
Benim için en zarif çözüm. Yazık ki pahalı olabilir.
16'da

Benim için de en şık çözüm! Sorunumu mükemmel bir şekilde çözdü (bana iki olası dönüş yönünden / açılarından daha küçük olan işaretli dönüş açısını veren bir formüle sahip olmak istedim ).
Jürgen Brauer

41

İki açınız x ve y ise, aralarındaki açılardan biri abs (x - y) olur. Diğer açı (2 * PI) - abs (x - y) 'dir. Yani 2 açının en küçüğünün değeri:

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

Bu size açının mutlak değerini verir ve girişlerin normalleştirildiğini varsayar (yani: aralık içinde [0, 2π) ).

Açının işaretini (yani: yönünü) korumak ve aralığın dışındaki açıları kabul etmek [0, 2π)istiyorsanız, yukarıdakileri genelleştirebilirsiniz. Genelleştirilmiş sürüm için Python kodu:

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

O Not %negatif değerler dahil olduğunda operatör bazı işaret ayarlamaları taşıma gerekebilir eğer öyleyse, özellikle, her dilde aynı davranmaz.


1
@bradgonesurfing Bu doğrudur / doğruydu, ancak adil olmak gerekirse, testleriniz orijinal soruda belirtilmeyen şeyler, özellikle normalleştirilmemiş girişler ve işaretlerin korunması için kontrol edildi. Düzenlenen cevaptaki ikinci sürüm testlerinizi geçmelidir.
Laurence Gonsalves

İkinci versiyon da benim için çalışmıyor. Örneğin 350 ve 0'ı deneyin. -10 döndürmeli ancak
-350

@kjyv Açıkladığınız davranışı yeniden oluşturamıyorum. Tam kodu gönderebilir misiniz?
Laurence Gonsalves

Ah, özür dilerim. Tam olarak python rad ve derece ile sürümünüzü test ettik ve iyi çalıştı. Yani benim C # çevirimde bir hata olmalıydı (artık yok).
kjyv

2
Python 3'ten itibaren tau'yu doğal olarak kullanabileceğinizi unutmayın! Sadece yaz from math import tau.
mhartl

8

İmzalı yanıtı sağlama zorluğuna yol açıyorum:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)

1
Ah ... cevap bu arada bir Python fonksiyonudur. Üzgünüm, bir an Python modundaydım. Umarım bu tamam.
David Jones

Yeni formülü üst kattaki koduma ekleyeceğim ve ne olacağını göreceğim! (teşekkür ederim ^ _ ^)
Tom J Nowell

1
Eminim PeterB'nin cevabı da doğrudur. Ve kötü huysuz. :)
David Jones

4
Ama bu hiç bir trig fonksiyonu
içermiyor

Java için eşdeğer formül nedir? açılar derece ise.
Soley



2

C ++ 'ta her açıdan ve hem radyan hem de derece için etkili bir kod:

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

Trigonometrik fonksiyonların hesaplanmasına gerek yoktur. C dilinde basit kod:

#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, radyan cinsinden

dif = difangrad(a,b);

derece cinsinden dif = a - b olsun

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

Günah yok, cos yok, bronzlaşma yok, .... sadece geometri !!!!


7
Böcek! # PIV2'yi "(M_PI + M_PI)" olarak değil "M_PI + M_PI" olarak tanımladığınızdan, çizgi arg = arg - PIV2;genişler arg = arg - M_PI + M_PIve hiçbir şey yapmaz.
canton7
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.