Médias de ângulos

15

História, ou por que estamos fazendo isso.

Nenhum. Este exercício é completamente inútil ... a menos que você seja Stephen Hawking .

O desafio

Dada uma lista de ângulos, encontre a média desses ângulos. Por exemplo, a média de 91 graus e -91 graus é de 180 graus. Você pode usar um programa ou função para fazer isso.

Entrada

Uma lista de valores de graus que representam medidas de ângulo. Você pode assumir que eles serão inteiros. Eles podem ser inseridos em qualquer formato conveniente ou fornecidos como argumentos de função.

Resultado

A média dos valores inseridos. Se houver mais de um valor encontrado para a média, apenas um deve ser gerado. A média é definida como o valor pelo qual

insira a descrição da imagem aqui

é minimizado. A saída deve estar dentro do intervalo de (-180, 180] e ter precisão de pelo menos duas casas atrás do ponto decimal.

Exemplos:

> 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

Como de costume com o , a submissão com o mínimo de bytes vence.

Entre os melhores

Aqui está um snippet de pilha para gerar uma classificação regular e uma visão geral dos vencedores por idioma.

Para garantir que sua resposta seja exibida, inicie-a com um título, usando o seguinte modelo de remarcação:

## Language Name, N bytes

onde Nestá o tamanho do seu envio. Se você melhorar sua pontuação, poderá manter as pontuações antigas no título, identificando-as. Por exemplo:

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

Se você quiser incluir vários números no cabeçalho (por exemplo, porque sua pontuação é a soma de dois arquivos ou você deseja listar as penalidades do sinalizador de intérpretes separadamente), verifique se a pontuação real é o último número no cabeçalho:

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

Você também pode transformar o nome do idioma em um link que será exibido no snippet do placar de líderes:

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

Aqui está uma sala de bate-papo para qualquer dúvida sobre o problema: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles

O número um
fonte
90, -90 não deve dar 180 se 91, -91 dá 180?
Azul
2
Intuitivamente, a média de -91 e 91 é 0, não 180. Usando sua definição, temos: (180-91) ^ 2 + (180- -91) ^ 2 => 81362, enquanto (0-91) ^ 2 + ( 0- -91) ^ 2 => 16562. Então 180 certamente não pode ser a média. O que estou perdendo aqui?
Edc65
91% 360 = 91; -91% 360 = 269; (269 + 91) / 2 = 180. Não importa, mal interpretado. Talvez? Não tenho certeza agora.
Azul
Ok obrigado. Ainda nenhuma idéia sobre como encontrá-lo
edc65
3
Até agora, nenhum dos seus casos de teste quebra o algoritmo incorreto de simplesmente obter todos os ângulos mod 360 °, obter a média e subtrair 360 ° se o resultado for maior que 180 °. Você deve adicionar um caso como [89 °, −89 °], que deve retornar 0 °.
Anders Kaseorg

Respostas:

7

Python 3, 129 bytes

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]

Esse problema parece ter gerado muita confusão. Intuitivamente, a idéia é cortar o círculo de ângulos em algum momento, desembrulhar o círculo em uma linha, calcular a média aritmética dessa linha e depois embrulhar o resultado de volta ao círculo. Mas há muitos pontos diferentes em que você pode optar por cortar o círculo. Não basta escolher arbitrariamente um, como 0 ° ou 180 °. Você precisa tentar todos eles e ver qual deles resulta na menor soma das distâncias ao quadrado. Se sua solução for significativamente menos complicada que essa, provavelmente ela está errada.

Anders Kaseorg
fonte
1
@AndreasKaseorg Eu acho que você pode salvar um byte mudando s**2paras*s
Ioannes
Veja meu comentário sobre a questão.
Msh210 13/0518
@ msh210 Não sei por que você está direcionando esse comentário especificamente para mim. Minha solução já funciona dessa maneira.
Anders Kaseorg
Foi parcialmente em resposta à última frase desta postagem de resposta.
Msh210 13/0518
4

Python 3, 85 bytes

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

Aproveita a resposta que precisa ser precisa com duas casas decimais, tentando todos os ângulos possíveis com incrementos de 1/200grau. Isso leva menos de um segundo na minha máquina.

Como o Python não permite listar convenientemente progressões aritméticas de flutuadores, representamos os ângulos possíveis como número inteiro [0,72000), que são convertidos em um ângulo (-180,180]como x -> 180 - x/200. Encontramos o que apresenta a soma mínima das diferenças angulares ao quadrado.

Para dois ângulos com um deslocamento angular de d, a distância angular ao quadrado é encontrada transformando-se em um ângulo equivalente em (-180,180]como 180-(d+180)%360e depois ao quadrado. Convenientemente, o ângulo dado por x/200já é deslocado em 180graus.

xnor
fonte
Usar incrementos de 1/200é realmente problemático. Para o caso de teste [1, 3, 3], essa solução retorna 2.335e é arredondada para 2.34enquanto a resposta correta deve ser 2.33.
Joel
@ Joel Não sei de onde você está obtendo o arredondamento, parece que os dígitos decimais 2.33estão corretos nesse exemplo. De qualquer forma, a alteração de 200para 400ou para 2000(e 72000correspondentemente) faria com que funcionasse apesar do arredondamento? Além disso, olhando para esse antigo problema novamente, acho que posso ver uma maneira melhor.
xnor
0.01m=argminxf(x)[s,s+0.01]f(s)<f(s+0.01) , que é mais ou menos garantido que |m-s|<|m-s+0,01| e round(m)=s (I use "more or less" because I am not sure about the boundary effect for each segment of f). The same holds for f(s)>f(s+0.01). In the case of a tie f(s)=f(s+0.01), round(m)=s+0.01. Note that for an arbitrary f this property may not be guaranteed.
Joel
Here is a TIO link for you to test.
Joel
Oh, I just realized that you are right. If the correct answer is 2.333... and your program returns 2.335, it is correct until two decimal places without rounding. Sorry for that.
Joel
3

Octave, 97 95 bytes

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

This produces an anonymous function that just searches the minimum of the given function on a grid that is just fine enough. As input the function accepts column vectors, e.g. [180; 60; -60]. For testing you need to give the function a name. So you could e.g. run the code above and then use ans([180, 60; -60]).

flawr
fonte
Yes, it returns 180.
flawr
2

Javascript ES6, 87 bytes

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

Example runs (Tested in Firefox):

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

Work in progress

This version takes a slightly different approach than the average-everything-then-do-modular-math. Rather, the angles are converted to vectors, the vectors are added and the angle of the resulting vector is then computed. Unfortunately, this version is very unstable with the trig and I'll be working on a modular-math version.

Dendrobium
fonte
1
f(-91,91) should return 180.
TheNumberOne
1
Even if it were implemented correctly, a vector addition approach cannot compute the specified result. Vector addition maximizes the sum of cosines of angular differences, rather than minimizing the sum of squares of angular differences.
Anders Kaseorg
2

CJam,  44  40 bytes

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

Try it online in the CJam interpreter.

Test cases

$ 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

Idea

We compute the deviation for all potential averages from -179.99 to 180.00 with steps of size 0.01, and select the one with the lowest deviation.

For this purpose, it doesn't matter if we take the angular distances degrees or radians. Rather than mapping the differences δ of angles from input and potential averages in [0,360°) and conditionally subtracting the result from 180°, we can simply calculate arccos(cos(πδ÷180°)), since cos is both periodic and even, and arccos always yields a value in [0,π).

Code

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
fonte
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, so until I can actually understand what the methodology is, this is what I have come up with. It is a little bit of a hack, but as the question states that the answer must be correct to 2.d.p it should work.

I basically check every angle between 0 and 360 (in 0.01 increments) and then solve the formula in the question for each of those angles. Then the angle with the smallest sum is picked and converted into -180 to 180 range.


The code should with Octave. You can try it with the online interpreter

Tom Carpenter
fonte
1°, 183° should result in −88°, not 92°.
Anders Kaseorg
@AndersKaseorg try again now.
Tom Carpenter
Nope, never mind. Back to the drawing board again...
Tom Carpenter
1

JavaScript (ES6) 138

Not having the faintest idea of an algorithm, this tries all possibile values with 2 digits precision (-179.99 to 180.00). Quite fast with the test cases anyway.

Test running the snippet below in an EcmaScript 6 compliant browser (implementing arrow functions and default parameters - 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
fonte