# Averages of Angles

## Story, or why we are doing this.

None. This exercise is completely pointless ... unless you are Stephen Hawking.

# The Challenge

Given a list of angles, find the average of those angles. For example the average of 91 degrees and -91 degrees is 180 degrees. You can use a program or function to do this.

# Input

A list of degree values representing angle measures. You may assume that they will be integers. They can be inputted in any convenient format or provided as function arguments.

# Output

The average of the inputted values. If there are more than one value found for the average, only one should be outputted. The average is defined as the value for which

is minimized. The output must be within the range of (-180, 180] and be accurate to at least two places behind the decimal point.

Examples:

> 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


As usual with , the submission with the least bytes wins.

Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

var QUESTION_ID=60538,OVERRIDE_USER=32700;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

## Language Name, N bytes


where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

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


If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

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


You can also make the language name a link which will then show up in the leaderboard snippet:

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


Here is a chatroom for any questions about the problem: http://chat.stackexchange.com/rooms/30175/room-for-average-of-angles

• Shouldn't 90,-90 give 180 if 91,-91 gives 180?
– Blue
Oct 12, 2015 at 20:58
• Intuitively the average of -91 and 91 is 0, not 180. Using your definition we have: (180-91)^2+(180- -91)^2 => 81362, while (0-91)^2+(0- -91)^2 => 16562. So 180 surely can not be the average. What am I missing here? Oct 12, 2015 at 20:59
• 91%360=91;-91%360=269;(269+91)/2=180. Never mind, misread. Maybe? I'm not sure now.
– Blue
Oct 12, 2015 at 21:01
• Ok thanks. Still no idea about how finding it Oct 12, 2015 at 21:52
• None of your test cases so far break the incorrect algorithm of simply taking all the angles mod 360°, taking their average, and then subtracting 360° if the result is greater than 180°. You should add a case like [89°, −89°], which should return 0°. Oct 13, 2015 at 4:25

# 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]


This problem seems to have generated quite a lot of confusion. Intuitively, the idea is to cut the circle of angles at some point, unwrap the circle to a line, compute the arithmetic mean on that line, and then wrap the result back to the circle. But there are many different points where you could choose to cut the circle. It is not enough to arbitrarily pick one, such as 0° or 180°. You need to try them all and see which one results in the smallest sum of squared distances. If your solution is significantly less complicated than this, it is probably wrong.

• @AndreasKaseorg I think you can save one byte by changing s**2 to s*s Oct 13, 2015 at 7:55
• See my comment on the question. May 13, 2018 at 16:27
• @msh210 Not sure why you’re directing this comment at me specifically. My solution already works that way. May 13, 2018 at 17:27
• It was partially in reply to the last sentence of this answer post. May 13, 2018 at 18:13

## Python 3, 85 bytes

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


Takes advantage of the the answer only needing to be accurate to two decimal points by trying all possible angles with increments of 1/200 of a degree. This takes less than a second on my machine.

Because Python doesn't let us conveniently list arithmetic progressions of floats, we represent the possible angles as whole number [0,72000), which convert to an angle as (-180,180] as x -> 180 - x/200. We find the one of these that gives the minimum sum of squared angular differences.

For two angles with an angular displacement of d, the squared angular distance is found by transforming to an equivalent angle in (-180,180] as 180-(d+180)%360, then squaring. Conveniently, the angle given by x/200 is already offset by 180 degrees.

• Using increments of 1/200 is actually problematic. For the test case [1, 3, 3], this solution returns 2.335 and is rounded to 2.34 while the correct answer should be 2.33.
– Joel
Aug 27, 2019 at 0:08
• @Joel I'm not sure where you're getting the rounding from, it looks like the decimal digits 2.33 are right in that example. In any case, would changing the 200 to 400 or to 2000 (and 72000 correspondingly) make it work despite rounding? Also, looking at this old problem again, I think I might see a better way.
– xnor
Aug 27, 2019 at 0:24
• Here you may actually use an increment of $0.01$. This is because the target function to optimize is basically a piecewise quadratic function. So if the minimum $m=\mathrm{argmin}_x f(x)$ lies in $[s,s+0.01]$ and $f(s)<f(s+0.01)$, it is more or less guaranteed that $|m-s|<|m-s+0.01|$ and ${\rm 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)$, ${\rm round}(m)=s+0.01$. Note that for an arbitrary $f$ this property may not be guaranteed.
– Joel
Aug 27, 2019 at 0:44
• Here is a TIO link for you to test.
– Joel
Aug 27, 2019 at 0:55
• 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
Aug 27, 2019 at 0:59

# 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]).

• Yes, it returns 180. Oct 16, 2015 at 15:13

# 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.

• f(-91,91) should return 180. Oct 13, 2015 at 0:08
• 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. Oct 13, 2015 at 3:48

# 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.  # Scala, 96 bytes Modified from @Roman Czyborra's answer. Golfed version. Try it online! x=>(-179 to 180).minBy(a=>x.map(i=>pow((a-i%360)match{case x if x>180=>x-360;case y=>y},2)).sum)  Ungolfed version. Try it online! import scala.math._ object Main { def main(args: Array[String]): Unit = { println(a(List(0, 15, 45, 460))) } def a(x: List[Int]): Int = { (-179 to 180).minBy(a => x.map(i => pow((a - i % 360) match { case x if x > 180 => x - 360 case y => y }, 2)).sum) } }  • the precision should be 2 decimal places (1 3 3 => 2.33) May 23, 2023 at 19:24 # 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 • 1°, 183° should result in −88°, not 92°. Oct 13, 2015 at 4:09 • @AndersKaseorg try again now. Oct 13, 2015 at 4:25 • Nope, never mind. Back to the drawing board again... Oct 13, 2015 at 4:27 # 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> # julia, 101 bytes This version works to machine precision. The idea is that If each entry of the input vector $$\x\in \mathbf{R}^n\$$ satisfies $$\x_i \in (0, 360]\$$, and if $$\x\$$ is sorted in ascending order, then the angle average is the arithmetic mean of $$x^{(k)} :=x + (\overbrace{360, \dotsc, 360}^{k\text{ entries}}, 0, \dotsc, 0)$$ for some $$\k \in \{1, \dotsc, n\}\$$ Hence, the problem reduces to finding $$k^* = \underset{k=1, \dotsc, n}{\mathrm{argmin}} \sum_{i=1}^n (x_i^{(k)} - \bar{x}^{(k)})^2.$$ The corresponding arithmetic mean is then $$\bar{x}^{(k^*)} = \bar{x} + k^* \cdot \frac{360}{n}$$ Finally, we put everything on the $$\(-180, 180]\$$ scale by adding $$\180\$$ to the input, computing everything mod $$\360\$$ and subtracting $$\180\$$ at the end. Expects a Vector{Float64} and returns a Float64. y->(| =mod1;~=sum;s=360;x=sort(y.+.5s.|s);n=~y.^0;~x+argmin(k->(x[k]+=s;~(x.-~x/n).^2),1:n)s)/n|s-.5s  Attempt This Online! • you can save 1 byte by having |,~=mod1,sum outside the function ATO May 23, 2023 at 19:19 # Haskell, 134 bytes, with fractional precision a x=(/100)$fromIntegral$snd$minimum$map(\a->(sum$map((^2).(\x->last$x:[x-36000|x>18000]).(mod36000).(a-).(100*))x,a))[-17999..18000]  Try it online! # Haskell, 97 bytes, with integer precision a x=snd$minimum$map(\a->(sum$map((^2).(\x->last\$x:[x-360|x>180]).(mod360).(a-))x,a))[-179..180]


Try it online!

• the precision should be 2 decimal places (1 3 3 => 2.33) May 23, 2023 at 19:23
• i know i cheated as all supplied test cases were integer-valued. May 24, 2023 at 4:51

# Julia 1.7, 61 bytes

p=180
!y=argmin(i->sum(j->((i-j%2p+p)%2p-p)^2,y),.01-p:.01:p)


Attempt This Online!

argmin(f, itr) needs julia 1.7 or later