6
\$\begingroup\$

In speedcubing, an Ao5 (Average of 5) is computed for a set of five times by removing the fastest and slowest times, and then computing the mean of the remaining three times.

A "rolling ao5" is computed for a sequence of at least five times by computing the ao5 for every set of consecutive five times (some sets will overlap). The best rolling ao5 is the lowest time out of all of these.

Related very old and much more involved question: Calculate the average and standard deviation, rubiks cube style

Input

An array of between 5 and 100 times inclusive, where each time is a number with exactly two decimal places, between 0.01 and 60.00. The array may be given as input in any reasonable format (list, csv string, etc.)

Output

The best rolling Ao5, where the mean must be rounded to the nearest hundredth and outputted with exactly two decimal places (including trailing zeros).

Test cases

Input on first line, output on second line.

7.16 5.04 4.67 6.55 4.99
5.53
10.00 10.00 10.00 10.00 10.00 7.00 6.00
9.00
34.87 33.24 33.31 33.63 27.04 29.14 29.74 31.65 27.42 30.13 28.31 32.50 32.61 36.38 28.60 33.85 28.42 36.00 32.41 35.35 29.47 31.02 28.87 35.08
28.77
\$\endgroup\$
10
  • 13
    \$\begingroup\$ Nice challenge, except that the strict output format is a bit annoying :-/ \$\endgroup\$ – Luis Mendo Apr 26 at 22:04
  • \$\begingroup\$ Can we take the length of the array as an additional input? \$\endgroup\$ – Delfad0r Apr 26 at 22:06
  • 4
    \$\begingroup\$ @qwr, it is. In order to not disadvantage languages that have no native way of determining the length. \$\endgroup\$ – Shaggy Apr 26 at 22:38
  • 1
    \$\begingroup\$ I would add/change some test cases, as the best rolling Ao5 in all of them are just the first 5 elements. \$\endgroup\$ – dingledooper Apr 27 at 3:02
  • 1
    \$\begingroup\$ ok I changed the test cases to be more interesting. lmk if you have any other good test cases \$\endgroup\$ – qwr Apr 28 at 1:53

20 Answers 20

5
\$\begingroup\$

Factor, 72 bytes

[ 5 clump [ 3 [1,b] [ < ] kth-objects mean ] map infimum "%.2f"sprintf ]

Try it online!

Explanation:

It's a quotation (anonymous function) that takes a sequence of numbers from the data stack as input and leaves a string on the data stack as output.

  • 5 clump Split a sequence into groups of 5 with overlapping. This is the 'rolling' part.
  • [ ... ] map Apply a quotation to each element of a sequence, collecting the results into a sequence of the same size.
  • 3 [1,b] [ < ] kth-objects Take the second, third, and fourth elements of a sequence (from a sorted standpoint). This is shorter than trying to sort it and then chop off both ends.
  • mean Take the mean.
  • infimum Take the smallest element from a sequence.
  • "%.2f"sprintf Return the result as a string rounded to two decimal places.
\$\endgroup\$
5
\$\begingroup\$

R, 93 88 86 80 79 77 bytes

Simple reference solution. Uses trim feature of min as Giuseppe pointed out.

x=scan()
sprintf("%.2f",min(sapply(5:sum(x|1),function(i)mean(x[i-0:4],.2))))

-2 bytes by Giuseppe by replacing length(x) and cleaning up unnecessary character in sprintf.

Try it online!

80 78 bytes

Similar answer but with a for loop.

x=scan()
a={}
for(i in 5:sum(x|1))a=min(a,mean(x[i-0:4],.2))
sprintf("%.2f",a)

Try it online!

\$\endgroup\$
1
4
\$\begingroup\$

JavaScript (ES6), 97 bytes

f=a=>Math.min(([,x,y,z,q]=[a.pop(),...a.slice(-4)].sort((a,b)=>a-b),x+y+z)/3,q?f(a):z).toFixed(2)

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Nice, forgot about toFixed! \$\endgroup\$ – A username Apr 26 at 22:25
4
\$\begingroup\$

MATL, 20 bytes

5YCS6LY)YmX<'%.2f'YD

Try it online! Or verify all test cases.

Explanation

        % Implicit input
5YC     % Matrix with sliding blocks of length 5 as columns
S       % Sort each column
6L      % Push [2 1j-1]. When used as an index, this is interpreted as "2:end-1"
Y)      % Index the rows as specified. This removes first and last rows
Ym      % Mean of each column
X<      % Mininum
'%.2f'  % Push this string: format specification, 'sprintf'-style
YD      % Convert to string with specified format
        % Implicit display

Direct translation to an anonymous function in Octave (with the Image package):

@(x)sprintf('%.2f',min(mean(sort(im2col(x,[1 5]))(2:end-1,:))))
\$\endgroup\$
4
\$\begingroup\$

R, 86 82 bytes

function(x,l=sum(x|1))sprintf("%.2f",min(apply(matrix(x,l+1,5),1,mean,.2)[5:l-5]))

Try it online!

R's mean function helpfully can be used to calculate a trimmed mean.

Outgolfed by qwr.

\$\endgroup\$
8
  • \$\begingroup\$ You can replace trim with t. Actually you might be able to get rid of the named argument entirely \$\endgroup\$ – qwr Apr 26 at 22:17
  • \$\begingroup\$ A bit embarrassing to be outgolfed by the reference implementation! I'll edit in a bit, on mobile atm \$\endgroup\$ – Giuseppe Apr 26 at 22:21
  • \$\begingroup\$ Only when I used the trim argument of mean \$\endgroup\$ – qwr Apr 26 at 22:22
  • \$\begingroup\$ also can't you use x=scan() instead of a function? tio.run/##TY7BasQwDETv/… \$\endgroup\$ – qwr Apr 27 at 2:24
  • 1
    \$\begingroup\$ @qwr I did check just in case it had a ... that gets passed on to other functions (like apply and the *apply functions), but alas, it does not. \$\endgroup\$ – Giuseppe Apr 28 at 18:58
3
\$\begingroup\$

Retina 0.8.2, 83 bytes

\.

\d+
$*
M!&`\b(1+,){4}1+
%(O`1+
^1+|,|1+$

)M`1{2,3}
O#`
1G`
+`^..?$
0$&
..$
.$&

Try it online! Link includes test cases. Explanation:

\.

Multiply the input by 100.

\d+
$*

Convert to unary.

M!&`\b(1+,){4}1+

Extract all overlapping sets of five consecutive values.

%(`
)`

For each set, ...

O`1+

... sort in order, ...

^1+|,|1+$

... delete the fastest and slowest times, ...

M`1{2,3}

... and take the mean, rounding to nearest, converting to decimal.

O#`

Sort the means.

1G`

Take the fastest.

+`^..?$
0$&
..$
.$&

Divide it by 100.

\$\endgroup\$
3
\$\begingroup\$

Python 2, 103, 89, 76, 73 bytes

Saved 14 bytes thanks to qwr!

Saved 7 bytes thanks to okie's suggestion to use a list comprehension.

Saved another 6 bytes by switching to a lambda function.

Saved 3 bytes after removing the extra brackets and comma and fixing the first slice. Thanks to caird, okie, and att! Also removed a pair of extra parentheses.

lambda t:"%.2f"%min(sum(sorted(t[i:i+5])[1:4])/3for i in range(len(t)-4))

The program iterates through all the consecutive runs of 5. The runs of 5 are sorted and the first and last elements are removed using slicing. The program sums the values remaining in the run and divides by 3 to find the average. Finally, percent formatting is used return the min value.

Try it online!

\$\endgroup\$
7
  • 1
    \$\begingroup\$ rounding is not needed when using format specifier. And you can use x directly: tio.run/… \$\endgroup\$ – qwr Apr 26 at 23:54
  • \$\begingroup\$ Thank you! I am not very familiar with percent formatting. Also didn't realize that there was another sorted function. \$\endgroup\$ – qwatry Apr 27 at 0:00
  • \$\begingroup\$ Make a with comprehension list will save bytes: tio.run/##BcHBCoMwDADQ@74iCEICmcNaPQj7ktLDYHULzLTUeNjX1/… \$\endgroup\$ – okie Apr 27 at 0:09
  • 1
    \$\begingroup\$ You can remove the [...] brackets: Try it online! \$\endgroup\$ – caird coinheringaahing Apr 27 at 0:27
  • 1
    \$\begingroup\$ , after 3 is not required \$\endgroup\$ – okie Apr 27 at 1:59
2
\$\begingroup\$

Jelly, 25 bytes

ṡ5ṢṖḊƊ€ÆmṂ×ȷ2+.ḞDṙ-2;”.ṙ2

Try it online!

Note to self: add formatting options to my new golfing language to avoid this ^ clownery

\$\endgroup\$
2
\$\begingroup\$

J, 30 bytes

0j2":[:<./5(3%~1#.1 2 3{/:~)\]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

V (vim), 140 bytes

o<esc>qq15wlh0y15wO<esc>pj0d3w@qqdk@qqt:s/ /\r/g
<c-o>:.,.4$sor n
:.,.4j
d3w$3bhd$:s/ /+/g
C<c-r>=(<c-r>")/3
<esc>k0@tq@t:%s/^\n
:sor n
{0C<c-r>=printf("%.2f",<c-r>")
<esc>o<esc>d}

Try it online!

I was lucky that vim had printf for this challenge, I'd finished most of it before I realized the output spec (phew)

\$\endgroup\$
2
  • \$\begingroup\$ maybe vim can be shorter by working with the values *100 and then inserting the decimal place at the end \$\endgroup\$ – qwr Apr 27 at 5:30
  • \$\begingroup\$ i tried doing *100 and rounding but printf was shorter and simpler for the output format. \$\endgroup\$ – Razetime Apr 27 at 5:31
2
\$\begingroup\$

APL (Dyalog Extended), 18 bytes

2⍕2⌷⌊⌿3+/↑∧¨5,/⎕÷3

Try it online!

Full program. Example with the second testcase:

⎕÷3 input divided by 3:

3.667 3.333 3.333 3.333 3.000 3.333

5,/ groups of 5 adjacent values:

┌─────────────────────────┬─────────────────────────┐
│3.667 3.333 3.333 3.333 3│3.333 3.333 3.333 3 3.333│
└─────────────────────────┴─────────────────────────┘

↑∧¨ sort each group and arrange the result in a matrix:

3 3.333 3.333 3.333 3.667
3 3.333 3.333 3.333 3.333

3+/ in each row, take sums of 3 adjacent values:

9.667 10 10.33
9.667 10 10   

⌊⌿ get the minimum for each column:

9.667 10 10

2⍕2⌷ select the second value and format with 2 decimal places:

10.00
\$\endgroup\$
1
\$\begingroup\$

Charcoal, 29 bytes

﹪%.2f∕⌊EE⁻Lθ⁴✂θι⁺⁵ι¹⁻Σι⁺⌊ι⌈ι³

Try it online! Link is to verbose version of code. Explanation:

           θ                    Input array
          L                     Length
         ⁻                      Subtract
            ⁴                   Literal 4
        E                       Map over implicit range
              θ                 Input array
             ✂                  Sliced
               ι                From current index
                ⁺⁵ι¹            Take five consecutive values
       E                        Map over slices
                      ι         Current slice
                     Σ          Sum
                    ⁻           Subtract
                         ι      Current slice
                        ⌊       Minimum
                       ⁺        Plus
                           ι    Current slice
                          ⌈     Maximum
      ⌊                         Take the minimum
     ∕                          Divide by
                            ³   Literal `3`
﹪%.2f                           Round to two decimal places
                                Implicitly print
\$\endgroup\$
1
  • \$\begingroup\$ Just comes and casually outgolfs everyone by 3x as much. I would upvote this if I had any left. \$\endgroup\$ – A username Apr 26 at 22:37
1
\$\begingroup\$

JavaScript (Node.js), 131 128 125 124 154 138 120 bytes

x=>Math.min(...x[f='slice'](0,-4).map((_,i)=>x[f](i,i+5).sort((a,b)=>a-b)[f](1,4)).map(([a,b,c])=>(a+b+c)/3)).toFixed(2)

Too long. Will golf.

Explanation

x=>                              // declare function
    Math.min(...                // smallest of
       x[f='slice'](0,-4)        // slice of last 4 to avoid NaN / undefined, defining f as 'slice' to save bytes
       .map((_,i)=>              // for each, by index (current value doesn't matter for now)
         x[f](i,i+5)             // remember f is slice, so replace each with its chunk of 5 (x is untrimmed, slice doesn't modify original
         .sort((a,b)=>a-b)       // sort numerically
         [f](1,4)                // remove first and last
       ).map(([a,b,c])=>         // for each, taking all 3 elements
         (a+b+c)/3               // take average
       )              
     )                           // end of min, so now have smallest
   ).toFixed(2)                  // round + add 0s

Try it online!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ This fails for the third test case \$\endgroup\$ – caird coinheringaahing Apr 26 at 21:57
  • \$\begingroup\$ @cairdcoinheringaahing Working on it \$\endgroup\$ – A username Apr 26 at 21:59
  • 1
    \$\begingroup\$ @cairdcoinheringaahing Oops I was using Math.max instead of Math.min. It works now. \$\endgroup\$ – A username Apr 26 at 22:01
  • 1
    \$\begingroup\$ Unfortunately, from the question: "with exactly two decimal places (including trailing zeros)". For the third test case, yours outputs 28.3 \$\endgroup\$ – caird coinheringaahing Apr 26 at 22:05
  • \$\begingroup\$ Damn it. Hold on while I patch in a regex. \$\endgroup\$ – A username Apr 26 at 22:06
1
\$\begingroup\$

Haskell, 106 bytes

printf"%.2f".g
g(h:t)|length t<4=99|y<-h:take 4t=min((sum y-maximum y-minimum y)/3)$g t
import Text.Printf

Try it online!

The relevant function is f, which takes as input the array of times as a list of Doubles. f returns a string representation of the best rolling AoE.

Having to import Text.Printf for this seems... wrong..., but I couldn't find any shorter way.

\$\endgroup\$
1
\$\begingroup\$

Red, 153 bytes

func[b][head insert at tail to""round/to 100 * first sort
collect[loop(length? b)- 4[keep average copy/part next
sort copy/part b 5 3 b: next b]]1 -2"."]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Ruby, 58 56 bytes

->a{"%.2f"%a.each_cons(5).map{|b|b.sort[1,3].sum/3}.min}

Try it online!

Explanation

It's quite straightforward actually:

  • get all sets of 5 consecutive elements using each_cons(5)
  • sort them, then get the 3 elements in the middle to discard min and max
  • calculate average
  • get minimum average value and format with 2 decimal places
\$\endgroup\$
1
\$\begingroup\$

Japt v2.0a0, 17 bytes

+4 bytes for the dumb output format :/

ã5 ®ÍÅÔÅx÷3Ãrm x2

Try it

ã5 ®ÍÅÔÅx÷3Ãrm x2     :Implicit input of array
ã5                    :Sub-arrays of length 5
   ®                  :Map
    Í                 :  Sort
     Å                :  Slice off first element (the minimum)
      Ô               :  Reverse
       Å              :  Slice off first element (the maximum)
        x             :  Reduce by addition
         ÷3           :  Divide by 3
           Ã          :End map
            r         :Reduce by
             m        :  Minimum
               x2     :Force to 2 decimal places
\$\endgroup\$
1
\$\begingroup\$

Pari/GP, 109 bytes

This doesn't work on Tio because their version of Pari/GP is apparently too old to have the vital function strsplit(). But it works as advertised on mine, version 2.13.0

A(s)=s=[eval(x)|x<-strsplit(s," ")];printf("%.2f",vecmin(vector(#s-4,i,vecsum(vecsort(s[i..i+4])[2..4])/3)))

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 55 bytes

Min@BlockMap[#~TrimmedMean~.2&,#,5,1]~NumberForm~{4,2}&

Try it online!

Technical note: the TIO link includes SetOptions[$Output, FormatType -> OutputForm] in the header. I am not including this in the score, because it's not necessary in an actual Mathematica notebook: those are in OutputForm by default.

Explanation

BlockMap[f,#,5,1] applies f to every block of 5 consecutive numbers. Here, f is #~TrimmedMean~.2&, which takes the mean after dropping the lowest and highest 20%.

We take the minimum of the results, and then ~NumberForm~{4,2} puts the answer in the required format: at most 4 digits of precision, with 2 digits after the decimal.

\$\endgroup\$
0
\$\begingroup\$

Perl 5 -MList::Util=sum,min,max -a, 81 67 bytes

printf"%.2f",(min map{sum(@b=@F[$_..$_+4])-min(@b)-max@b}0..@F-5)/3

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.