12
\$\begingroup\$

Naismith's rule helps to work out the length of time needed for a walk or hike, given the distance and ascent.

Given a non-empty list of the altitude at points evenly spaced along a path and the total distance of that path in metres, you should calculate the time needed according to Naismith's rule.

Naismith's rule is that you should allow one hour for every five kilometres, plus an additional hour for every 600 metres of ascent.

Input must be taken in metres, which is guaranteed to consist of non-negative integers, and output should consistently be either hours or minutes (but not both), and must be able to give decimal numbers where applicable (floating point inaccuracies are OK).

For example, given:

[100, 200, 400, 200, 700, 400], 5000

For the first two elements [100, 200] you have 100 metres of ascent which is 10 minutes. With [200, 400] you have 200 metres of ascent which is 20 minutes, [400, 200] is not ascending so no time is added for that. [200, 700] is 500 metres of ascent which is 50 minutes, and finally [700, 400] is not ascending. One extra hour is added for the distance of five kilometres. This totals to 140 minutes or 2.333... hours.

Test Cases

[0, 600] 2500 -> 1.5 OR 90
[100, 200, 300, 0, 100, 200, 300] 10000 -> 2.8333... OR 170
[40, 5, 35] 1000 -> 0.25 OR 15
[604] 5000 -> 1 OR 60
[10, 10, 10] 2000 -> 0.4 OR 24
[10, 25, 55] 1000 -> 0.275 OR 16.5
\$\endgroup\$
  • \$\begingroup\$ The test cases outputs all have whole-minute results, is that intentional? Are inputs like [10], 5125 or [10, 25, 55], 1000 valid and required to be handled? \$\endgroup\$ – sundar - Reinstate Monica Aug 7 '18 at 8:44
  • \$\begingroup\$ @sundar Yes, they should. \$\endgroup\$ – Okx Aug 7 '18 at 14:51
  • \$\begingroup\$ [10, 25, 55], 1000 -> 0.275 OR 16.5 \$\endgroup\$ – Khuldraeseth na'Barya Aug 8 '18 at 13:32

14 Answers 14

6
\$\begingroup\$

R,  44  43 42 bytes

function(A,D)sum(pmax(0,diff(A)),D*.12)/10

Try it online!

-1 byte by using pmax as multiple other answers do

Takes inputs as Ascent and Distance, and returns the time in minutes.

function(A,D)                                 # take Ascent and Distance
                        diff(A)               # take successive differences of ascents
                 pmax(0,       )              # get the positive elements of those
                                 D*.12        # multiply distance by 0.12
             sum(               ,     )       # take the sum of all elements
                                       /10    # and divide by 10, returning the result

\$\endgroup\$
  • \$\begingroup\$ You can get 4 more bytes using pryr::f(sum(pmax(0,diff(A)),D*.12)/10) instead of using function \$\endgroup\$ – Shayne03 Aug 7 '18 at 17:50
  • \$\begingroup\$ @Shayne03 that would technically change this answer to "R + pryr" which on the rules of the site counts as a different language than base R, so I'll keep this as it is. Thanks for the suggestion, though! \$\endgroup\$ – Giuseppe Aug 7 '18 at 17:52
  • \$\begingroup\$ The explanation is shaped like a hill \$\endgroup\$ – user70585 Aug 7 '18 at 21:51
3
\$\begingroup\$

JavaScript (ES6), 50 bytes

Saved 1 byte thanks to Giuseppe's answer (dividing by 10 at the end of the process)

Takes input as ([altitudes])(distance). Returns the time in minutes.

a=>d=>a.map(p=n=>d-=(x=p-(p=n))<0&&x,d*=.12)&&d/10

Try it online!

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

05AB1E, 15 bytes

¥ʒ0›}OT÷s₄;6//+

Try it online!

Returns time in minutes.

Explanation

              + # sum of ...
¥ʒ0›}OT÷        # the sum of positive deltas of the altitude divided by 10
        s₄;6//  # the distance divided by 83.33333333 (500/6, or the amount of meters per minute) 
\$\endgroup\$
  • \$\begingroup\$ Almost exactly what I had in mind. Only difference I had was ₄12// instead of ₄;6//. So obvious +1 from me. \$\endgroup\$ – Kevin Cruijssen Aug 7 '18 at 7:22
2
\$\begingroup\$

Haskell, 47 46 bytes

d#l@(_:t)=d/5e3+sum(max 0<$>zipWith(-)t l)/600

Returns the time in hours.

Try it online!

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

Python 2, 62 60 bytes

Saved 2 bytes thanks to ovs.

lambda e,d:sum((a-b)*(a>b)for a,b in zip(e[1:],e))*.1+d*.012

Try it online!

Returns time in minutes.

# add all increasing slope differences together
sum(
    # multiply the difference by 0 if a<b, else by 1
    (a-b)*(a>b)
                # create a list of pairs by index, like [(1,0), (2,1) ...(n, n-1)]
                # then interate thru the pairs where a is the higher indexed item and b is the lower indexed item
                for a,b in zip(e[1:],e)
    )
    # * 60 minutes / 600 meters == .1 min/m
    *.1 
    # 60 minutes / 5000 meters = .012 min/m
    +d*.012
\$\endgroup\$
  • \$\begingroup\$ 60 bytes \$\endgroup\$ – ovs Aug 6 '18 at 17:42
2
\$\begingroup\$

Perl 6, 45 39 37 bytes

6 bytes saved thanks to Jo King.

2 bytes saved thanks to nwellnhof.

(Thanks to both of them, this no longer looks anything like my original submission :—).)

*.&{sum (.skip Z-$_)Xmax 0}/600+*/5e3

Try it online!

The first argument is the list with elevations, the second argument is the length of the trek.

The whole thing is a WhateverCode. If an expression is recognized as such, then each * is one argument.

So, in *.&{sum (.skip Z-$_)Xmax 0}/600, we take the first argument (the first occurence of *), and use a block on it with a method-like construct .&{}. The block takes one argument (the list), which goes into $_, so .skip is that list without the first element. We subtract the original array, element by element, from that, using Z-. Zipping stops as soon as the shorter list is depleted, which is OK.

We then use the cross product operator X. list X(op) list creates all possible pairs where the first element is from the left list and the second from the right, and uses the operator (op) on them. The result is returned as a Seq (a one-shot list). However, the right list has only one element, 0, so it just does * max 0, element by element. That makes sure that we count only ascending parts of the trek. Then we add it up and divide by 600.

Then we add */5e3, where the * occurs for the second time, and so it's the second argument, and divide it by 5000. The sum is then the time in hours. (This is more efficient than the time in minutes since we'd need to multiply, and the * would need to be separated by a space from the WhateverStar *.)

\$\endgroup\$
  • 1
    \$\begingroup\$ 42 bytes using X \$\endgroup\$ – Jo King Aug 6 '18 at 21:18
  • \$\begingroup\$ @JoKing, that's a nice use of X, thanks! \$\endgroup\$ – Ramillies Aug 6 '18 at 21:30
  • 1
    \$\begingroup\$ Actually, we can avoid the last X/ by just dividing the sum by 10. 39 bytes \$\endgroup\$ – Jo King Aug 6 '18 at 22:51
  • \$\begingroup\$ 37 bytes using WhateverCode and .&{} (returns hours). \$\endgroup\$ – nwellnhof Aug 7 '18 at 21:20
2
\$\begingroup\$

oK, 21 bytes

{y+/0|1_-':x}..1.012*

Try it online! Abusing a parsing bug where .1.012 is the same as .1 .012.

              .1.012* /a = [0.1 * input[0], 0.012 * input[1]]
{           }.        /function(x=a[0], y=a[1])
      1_-':x          /  a = subtract pairs of elements from x
    0|                /  a = max(a, 0) w/ implicit map
 y+/                  /  y + sum(a)

-1 thanks to streester.

k, 23 bytes

{.1*(.12*y)++/0|1_-':x}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ {y+/0|1_-':x}..1.012* for 21 bytes? start accumulator with y. \$\endgroup\$ – streetster Aug 7 '18 at 13:24
  • \$\begingroup\$ Indeed! I'd apply a similar enhancement to the k code, but unfortunately the k interpreter that I have (2016.08.09) doesn't like me starting the accumulator with anything in that fashion. :/ \$\endgroup\$ – zgrep Aug 8 '18 at 14:37
1
\$\begingroup\$

Jelly, 12 bytes

×.12;I}»0÷⁵S

Try it online!

-1 thanks to Mr. Xcoder.

\$\endgroup\$
  • 1
    \$\begingroup\$ Doesn't ×.12 work? \$\endgroup\$ – Mr. Xcoder Aug 6 '18 at 16:56
  • \$\begingroup\$ @Mr.Xcoder It does, I was in a hurry. \$\endgroup\$ – Erik the Outgolfer Aug 6 '18 at 18:22
1
\$\begingroup\$

Python 2, 59 bytes

lambda a,d:d/5e3+sum(max(0,y-x)/6e2for x,y in zip(a,a[1:]))

Try it online!

returns hours as a decimal.

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

Pyth, 15 bytes

c+*E.12s>#0.+QT

Full program, expects the set of heights as the first argument, distance as the second. Returns the time in minutes.

Try it online here, or verify all test cases at once here.

c+*E.12s>#0.+QT   Implicit: Q=input 1, E=input 2
           .+Q    Take the differences between each height point
        >#0       Filter to remove negative values
       s          Take the sum
  *E.12           Multiply the distance by 0.12
 +                Add the two previous results
c             T   Divide the above by 10, implicit print
\$\endgroup\$
1
\$\begingroup\$

APL (Dyalog Unicode), 21 20 18 bytes

.1×.12⊥⎕,-+/0⌊2-/⎕

Try it online!

Traditional function taking input (from right to left) as 1st ⎕=Heights/Depths, 2nd ⎕=Distance.

Thanks to @ngn for being a wizard one three bytes.

How it works:

.1×.12⊥⎕,-+/0⌊2-/⎕ ⍝ Function;
                 ⎕ ⍝ Append 0 to the heights vector;
              2-/  ⍝ Pairwise (2) differences (-/);
            0⌊     ⍝ Minimum between 0 and the vector's elements;
          +/       ⍝ Sum (yields the negated total height);
         -         ⍝ Subtract from;
   .12⊥⎕,          ⍝ Distance × 0.12;
.1×                ⍝ And divide by 10;
\$\endgroup\$
  • \$\begingroup\$ thanks for "wizard" :) you don't have to copy the expression multiple times in order to test it, put it in a tradfn instead; ,0 is unnecessary, the for the problematic test should be ,604, not 604 \$\endgroup\$ – ngn Aug 7 '18 at 8:42
  • \$\begingroup\$ See, that's why you're a wizard. The copying the expression multiple times part is totally my fault, I just replaced the and in the old code with and was too lazy to put the tradfn header/footer. The ,0 bit though? Gold. \$\endgroup\$ – J. Sallé Aug 7 '18 at 13:02
0
\$\begingroup\$

Java 8, 89 bytes

a->n->{int s=0,p=0,i=a.length;for(;i-->0;p=a[i])s+=(p=p-a[i])>0?p:0;return s/10+n/500*6;}

Try it online.

Explanation:

a->n->{                   // Method with integer-array and integer parameter and integer return-type
  int s=0,                //  Sum-integers, starting at 0
      p=0,                //  Previous integer, starting at 0
  i=a.length;for(;i-->0;  //  Loop `i` backwards over the array
                 ;        //    After every iteration:
                  p=a[i]) //     Set `p` to the current value for the next iteration
    s+=(p=p-a[i])>0?      //   If the previous minus current item is larger than 0:
         p                //    Add that difference to the sum `s`
        :                 //   Else:
         0;               //    Leave the sum the same
   return s/10            //  Return the sum integer-divided by 10
          +n/500*6;}      //  Plus the second input divided by 500 and then multiplied by 6
\$\endgroup\$
0
\$\begingroup\$

Japt, 39 bytes

0oUl)x@W=UgXÄ -UgX)g ¥É?0:W/#ɘ} +V/(#Ǵ0

Try it online!

Likely can be golfed quite a bit more.

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

Stax, 17 bytes

ü;█y☼òΓ▀ßîP<<╪⌠öß

Run and debug it at staxlang.xyz!

Takes all inputs as floats, though this saves a byte only in the unpacked version. Likely improvable; just now returning to code golf, I'm somewhat rusty.

Unpacked (20 bytes) and explanation:

0!012*s:-{0>f{A/m|++
0!012*                  Float literal and multiplication for distance
      s                 Swap top two stack values (altitudes to the top)
       :-               List of deltas
         {0>f           Filter: keep only positive changes
             {A_m       Map: divide all ascents by 10
                 |++    Add these times to that for horizontal travel
                        Implicit print

0!012*s:-{0>f{A_:m|++ works for integral inputs for 21 bytes unpacked and still 17 packed.

\$\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.