Średnie kąty

15

Historia lub dlaczego to robimy.

Żaden. To ćwiczenie jest całkowicie bezcelowe ... chyba że jesteś Stephen Hawking .

Wyzwanie

Na podstawie listy kątów znajdź średnią tych kątów. Na przykład średnia 91 stopni i -91 stopni wynosi 180 stopni. Aby to zrobić, możesz użyć programu lub funkcji.

Wejście

Lista wartości stopni reprezentujących miary kąta. Możesz założyć, że będą to liczby całkowite. Można je wprowadzić w dowolnym dogodnym formacie lub podać jako argumenty funkcji.

Wynik

Średnia z wprowadzonych wartości. Jeśli dla średniej znaleziono więcej niż jedną wartość, należy podać tylko jedną. Średnia jest definiowana jako wartość, dla której

wprowadź opis zdjęcia tutaj

jest zminimalizowane. Dane wyjściowe muszą zawierać się w przedziale (-180, 180] i być dokładne co najmniej do dwóch miejsc za przecinkiem dziesiętnym.

Przykłady:

> 1 3
2
> 90 -90
0 or 180
> 0 -120 120
0 or -120 or 120
> 0 810
45
> 1 3 3
2.33
> 180 60 -60
180 or 60 or -60
> 0 15 45 460
40
> 91 -91
180
> -89 89
0

Jak zwykle w przypadku , przesyłanie z najmniejszą bajtów.

Tabela liderów

Oto fragment kodu, który pozwala wygenerować zarówno zwykłą tabelę wyników, jak i przegląd zwycięzców według języka.

Aby upewnić się, że twoja odpowiedź się pojawi, zacznij od nagłówka, korzystając z następującego szablonu Markdown:

## Language Name, N bytes

gdzie Njest rozmiar twojego zgłoszenia. Jeśli poprawić swój wynik, to może zachować stare porachunki w nagłówku, uderzając je przez. Na przykład:

## Ruby, <s>104</s> <s>101</s> 96 bytes

Jeśli chcesz umieścić w nagłówku wiele liczb (np. Ponieważ twój wynik jest sumą dwóch plików lub chcesz osobno wymienić kary za flagi tłumacza), upewnij się, że rzeczywisty wynik jest ostatnią liczbą w nagłówku:

## Perl, 43 + 2 (-p flag) = 45 bytes

Możesz także ustawić nazwę języka jako link, który pojawi się we fragmencie tabeli wyników:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

Oto czat na wszelkie pytania dotyczące problemu: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles

Numer jeden
źródło
Czy 90, -90 nie daje 180, jeśli 91, -91 daje 180?
Niebieski,
2
Intuicyjnie średnia z -91 i 91 wynosi 0, a nie 180. Zgodnie z twoją definicją mamy: (180-91) ^ 2 + (180- -91) ^ 2 => 81362, podczas gdy (0-91) ^ 2 + ( 0-91) ^ 2 => 16562. Zatem 180 na pewno nie może być średnią. Czego tu brakuje?
edc65,
91% 360 = 91; -91% 360 = 269; (269 + 91) / 2 = 180. Nieważne, źle odczytane. Może? Nie jestem teraz pewien.
Niebieski,
Ok dzięki. Nadal nie mam pojęcia, jak to znaleźć
edc65,
3
Żaden z twoich przypadków testowych do tej pory nie złamał nieprawidłowego algorytmu po prostu biorąc wszystkie kąty mod 360 °, biorąc ich średnią, a następnie odejmując 360 °, jeśli wynik jest większy niż 180 °. Powinieneś dodać znak taki jak [89 °, −89 °], który powinien zwrócić 0 °.
Anders Kaseorg,

Odpowiedzi:

7

Python 3, 129 bajtów

lambda l:min([sum(((b-a)%360)**2for b in l)*len(l)-s*s,180-(180-a-s/len(l))%360]for a in l for s in[sum((b-a)%360for b in l)])[1]

Wydaje się, że ten problem spowodował sporo zamieszania. Intuicyjnie chodzi o wycięcie koła kątów w pewnym punkcie, rozwinięcie koła do linii, obliczenie średniej arytmetycznej na tej linii, a następnie zawinięcie wyniku z powrotem do koła. Ale istnieje wiele różnych punktów, w których można wyciąć koło. Nie wystarczy dowolnie wybrać taki, jak 0 ° lub 180 °. Musisz wypróbować je wszystkie i zobaczyć, który z nich daje najmniejszą sumę kwadratowych odległości. Jeśli twoje rozwiązanie jest znacznie mniej skomplikowane niż to, prawdopodobnie jest złe.

Anders Kaseorg
źródło
1
@AndreasKaseorg Myślę, że możesz zaoszczędzić jeden bajt, zmieniając s**2nas*s
Ioannes,
Zobacz mój komentarz do pytania.
msh210
@ msh210 Nie jestem pewien, dlaczego specjalnie kierujesz ten komentarz do mnie. Moje rozwiązanie już działa w ten sposób.
Anders Kaseorg
Częściowo była to odpowiedź na ostatnie zdanie tego posta z odpowiedzią.
msh210
4

Python 3, 85 bajtów

lambda l:180-min(range(72000),key=lambda x:sum((180-(x/200+i)%360)**2for i in l))/200

Wykorzystuje odpowiedź, która musi być dokładna do dwóch miejsc po przecinku, wypróbowując wszystkie możliwe kąty z przyrostem 1/200o jeden stopień. To zajmuje mniej niż sekundę na moim komputerze.

Ponieważ Python nie pozwala nam wygodnie wypisywać arytmetycznych postępów liczb zmiennoprzecinkowych, reprezentujemy możliwe kąty jako liczbę całkowitą [0,72000), które przekształcają się w kąt (-180,180]jakox -> 180 - x/200 . Znajdujemy jeden z nich, który daje minimalną sumę kwadratowych różnic kątowych.

Dla dwóch kątów z przesunięciem kątowym d, kwadratową odległość kątową wyznacza się przez przekształcenie na równoważny kąt w (-180,180]as 180-(d+180)%360, a następnie kwadrat . Dogodnie kąt podany przez x/200jest już przesunięty o 180stopnie.

xnor
źródło
Korzystanie z przyrostów 1/200jest w rzeczywistości problematyczne. W przypadku testowym [1, 3, 3]to rozwiązanie powraca 2.335i jest zaokrąglane do 2.34poprawnej odpowiedzi 2.33.
Joel
@Joel Nie jestem pewien, skąd bierzesz zaokrąglanie, wygląda na to, że cyfry dziesiętne 2.33są w tym przykładzie. W każdym razie, to zmieniając 200się 400lub 2000(i 72000odpowiednio), aby pracować pomimo zaokrąglenia? Patrząc znów na ten stary problem, myślę, że mogę zobaczyć lepszy sposób.
xnor
0,01m=zarsolmjanxfa(x)[s,s+0,01]fa(s)<fa(s+0,01)|m-s|<|m-s+0,01|rounre(m)=sfafa(s)>fa(s+0,01)fa(s)=fa(s+0,01)rounre(m)=s+0,01fa
Oto link TIO do przetestowania.
Joel
Och, właśnie zdałem sobie sprawę, że masz rację. Jeśli poprawna odpowiedź brzmi, 2.333...a program powraca 2.335, jest poprawny do dwóch miejsc po przecinku bez zaokrąglania. Przepraszam za to.
Joel
3

Oktawa, 97 95 bajtów

p=pi;x=p:-1e-5:-p;z=@(L)sum((p-abs(abs(x-mod(L,360)*p/180)-p)).^2);@(L)x(z(L)==min(z(L)))*180/p

Powoduje to utworzenie anonimowej funkcji, która wyszukuje minimum danej funkcji na siatce, która jest wystarczająco dobra. Jako dane wejściowe funkcja przyjmuje wektory kolumnowe, np [180; 60; -60]. Do testowania musisz nadać tej funkcji nazwę. Możesz więc np. Uruchomić powyższy kod, a następnie użyć ans([180, 60; -60]).

wada
źródło
Tak, zwraca 180.
błąd
2

JavaScript ES6, 87 bajtów

with(Math)f=(...n)=>(t=>180/PI*atan(t(sin)/t(cos)))(f=>n.reduce((p,c)=>p+=f(c*PI/180)))

Przykładowe przebiegi (testowane w przeglądarce Firefox):

f(-91,91)     // -0
f(-90,90)     // 0
f(0,-120,120) // 0
f(0,810)      // 44.999999999999936

Praca w toku

Ta wersja ma nieco inne podejście niż przeciętna matematyka. Raczej kąty są konwertowane na wektory, wektory są dodawane, a następnie obliczany jest kąt wynikowego wektora. Niestety, ta wersja jest bardzo niestabilna z trigiem i będę pracował nad wersją modułową.

Dendrobium
źródło
1
f(-91,91)powinien zwrócić 180.
TheNumberOne
1
Nawet jeśli zostało poprawnie zaimplementowane, metoda dodawania wektora nie może obliczyć określonego wyniku. Dodanie wektora maksymalizuje sumę cosinusów różnic kątowych, zamiast minimalizować sumę kwadratów różnic kątowych.
Anders Kaseorg,
2

CJam,  44  40 bajtów

Ie3_2*,f-:e-2{ea:~f{-P*180/mcmC2#}:+}$0=

Wypróbuj online w interpretatorze CJam .

Przypadki testowe

$ for i in 1\ 3 90\ -90 0\ -120\ 120 0\ 810 1\ 3\ 3 180\ 60\ -60 0\ 15\ 45\ 460 91\ -91 -89\ 89
> do cjam <(echo 'Ie3_2*,f-:e-2{ea:~f{-P*180/mcmC2#}:+}$0=') $i
> echo
> done
2.0
180.0
0.0
45.0
2.33
60.0
40.0
180.0
0.0

Pomysł

Obliczamy odchylenie dla wszystkich potencjalnych średnich od -179,99 do 180,00 z krokami wielkości 0,01 i wybieramy to o najniższym odchyleniu.

W tym celu nie ma znaczenia, czy weźmiemy kątowe odległości stopni lub radianów. Zamiast mapować różnice δ kątów od średnich wejściowych i potencjalnych w [0,360 °) i warunkowo odejmując wynik od 180 ° , możemy po prostu obliczyć arccos (cos (πδ ÷ 180 °)) , ponieważ cos jest zarówno okresowe, jak i równe, i arccos zawsze daje wartość w [0, π) .

Kod

Ie3        e# Push 18e3 = 18,000.
_2*        e# Copy and multiply by 2. Pushes 36,000.
,          e# Push the range [0 ... 35,999].
f-         e# Subtract each element from 18,000. Pushes [18,000 ... -17,999].
:e-2       e# Divide each element by 100. Pushes [180.00 ... -179.99].
{          e# Sort; for each element A of that array:
  ea:~     e#   Push and evaluate the array of command-line arguments.
  f{       e#   For each input angle, push A and the angle; then:
    -      e#     Subtract the angle from A.
    P*180/ e#     Convert from degrees to radians.
    mcmC   e#     Apply cos, then arccos to the result.
    2#     e#     Square.
  }        e#
  :+       e#   Add the squares. This calculates the deviation.
}$         e# A's with lower deviations come first.
0=         e# Select the first element of the sorted array.
Dennis
źródło
1

MATLAB, 151

p=360;n=mod(input(''),p);a=0:0.01:p;m=[];for b=a e=b-n;f=mod([e;-e],p);c=min(f);d=c.^2;m=[m sum(d)];end;[~,i]=min(m);a=a(i);if a>180 a=a-p;end;disp(a);

Ok, więc dopóki nie zrozumiem, na czym polega metodologia, właśnie to wymyśliłem. Jest to trochę włamanie, ale ponieważ pytanie mówi, że odpowiedź musi być poprawna do 2.dp, powinna działać.

Zasadniczo sprawdzam każdy kąt między 0 a 360 (w przyrostach co 0,01), a następnie rozwiązuję formułę w pytaniu dla każdego z tych kątów. Następnie kąt z najmniejszą sumą jest wybierany i konwertowany na zakres od -180 do 180.


Kod powinien zawierać oktawę . Możesz spróbować z tłumaczem online

Tom Carpenter
źródło
1 °, 183 ° powinno dać -88 °, a nie 92 °.
Anders Kaseorg,
@AndersKaseorg spróbuj ponownie teraz.
Tom Carpenter,
Nie, nieważne. Wróć do tablicy kreślarskiej ...
Tom Carpenter
1

JavaScript (ES6) 138

Nie mając najmniejszego pojęcia o algorytmie, wypróbowuje wszystkie możliwe wartości z dokładnością do 2 cyfr (-179,99 do 180,00). W każdym razie dość szybko dzięki testom.

Przetestuj poniższy fragment kodu w przeglądarce zgodnej z EcmaScript 6 (implementacja funkcji strzałek i parametrów domyślnych - AFAIK Firefox)

A=l=>(D=(a,b,z=a>b?a-b:b-a)=>z>180?360-z:z,m=>{for(i=-18000;i++<18000;)l.some(v=>(t+=(d=D(v%360,i/100))*d)>m,t=0)||(m=t,r=i)})(1/0)||r/100

// Test
console.log=x=>O.innerHTML+=x+'\n'

;[[1,3],[89,-89],[90,-90],[91,-91],[0,120,-120],[0,810],[1,3,3],[180,60,-60],[0,15,45,460],[1,183]]
.forEach(t=>console.log(t+' -> '+A(t)))

// Less golfed

A=l=>{
  D=(a,b,z=a>b?a-b:b-a) => z>180?360-z:z; // angular distance
  m=1/0;
  for(i=-18000;i++<18000;) // try all from -179.99 to 180
  {
    t = 0;
    if (!l.some(v => (t+=(d=D(v%360,i/100))*d) > m))
    {
      m = t;
      r = i;
    }  
  }  
  return r/100;
}
<pre id=O></pre>

edc65
źródło