The Leap Machine (Puzzle)

Here’s a puzzle for you –

Like the TARDIS, your time machine has a fault.
Like the TARDIS, your time machine has a fault. The fault isn’t a failure of its chameleon circuit, but a quirk in its ability to jump to particular dates. Picture courtesy aussiegall (Flickr), licensed Creative Commons.

You own a time machine with an unusual property: it can only travel to 29th February. It can jump to any 29th February, anywhere at all, in any year (even back before we invented the Gregorian Calendar, and far into the future after we’ve stopped using it), but it can only finish its journey on a 29th of February, in a Gregorian leap year (for this reason, it can only jump to years which are leap years).

One day, you decide to take it for a spin. So you get into your time machine and press the “random” button. Moments later, you have arrived: it is now 29th February in a random year!

Without knowing what year it is: what is the probability that it is a Monday? (hint: the answer is not 1/7 – half of your challenge is to work out why!).

11 replies to The Leap Machine (Puzzle)

  1. The chance is the same as it being a Wednesday or a Friday (which are the most probable) and it’s least likely to be Tuesday, but as you say, interesting to learn why (hence I’m not saying any more!)

    • That’s interesting. I suspect we’re thinking in the same direction, but I didn’t quite get the same answer. What’s the probability you calculate for Monday?

      I’ll post my answer (and how I worked it out, in case I’m wrong!) later in the week.

      • Hm, so I made a “small” mistake in my calculations due to changing from using the Date class to Time in Ruby and it not throwing an exception I was expecting… Grr.

        Having fixed that (and done a sanity check!), I now get Monday and Wednesday having the same (highest) probability, of 15/97, or 0.15463917525773196. I’ll send you my code :-)

        • Nicely done. That’s my answer, too. Here’s my code (also in Ruby):

          puts (0..400).
          collect{|i|Time::new(i,2,29)}.
          reject{|t|t.day==1}.
          collect{|t|t.strftime('%a')}.
          group_by{|d|d}.
          collect{|k,v|[k,v.length]}.
          sort_by{|k,v|v}.reverse.
          collect{|k,v|"%s - %d"%[k,v]}.
          join("\n")

          It’s a little sloppy: I could have taken some more-elegant shortcuts.

          • It’s also broken ;-)

            Your code is checking 401 years (between the year 0 and the year 400, inclusive)

            Changing 0..400 to 0…400 would do the right thing.

            • Ah-hah! Right you are. I’m still correct for the ranges I tested (because the year after and the year before aren’t leap years anyway), but it’s a valid point. I had meant to use:

              (0…400)

                • You’re right that my code makes that mistake. However the fact that it comes up with the wrong answer as a result could be a fault of the interpreter and not my code…

                  The year “0” in ISO 8601 format, as (supposedly) used by Ruby’s Date format, corresponds to 1BCE (there was no “year 0” as zero hadn’t come to the West at the time of the creation of the Gregorian calendar). Should this be a leap year? It depends on how you calculate them. It’s four years from a leap year and not the 25th such leap year in a row (and not-not the 100th potential one), which would say yes. But on the other hand, it doesn’t divide by 4, which would say no.

                  …or rather, it’s the fact that there is no unambiguous definition in the Gregorian calendar about how to handle BCE leap years that means that different interpretations exist. It could be argued either way whether 1BCE was a leap year or not. Ruby says it was, but I’m not sure it’s right…

  2. Also replied on Reddit:

    import collections

    def getDayFromNumber(n):
    if n == 0:
    return 'Tuesday'
    if n == 1:
    return 'Monday'
    if n == 2:
    return 'Sunday'
    if n == 3:
    return 'Saturday'
    if n == 4:
    return 'Friday'
    if n == 5:
    return 'Thursday'
    if n == 6:
    return 'Wednesday'

    yearDay = {}
    days = []
    centuryCounter = 0

    for y in range(1600,1999):
    if y > 1600 and y % 100 == 0:
    centuryCounter = centuryCounter + 1

    if y % 1600 == 0 or (y % 4 == 0 and not y % 100 == 0):
    d = (((y-1600) / 2) + centuryCounter) % 7
    yearDay[y] = getDayFromNumber(d)
    days.append(getDayFromNumber(d))

    print yearDay
    print collections.Counter(days)

  3. This problem is fairly easy to solve if you know about Conway’s “Doomsday Rule” for converting dates to days of the week in your head.

    Following Conway’s Doomsday rule, the last day of Feb is always a doomsday. So then we have do calculate the distribution of doomsdays for the 400-year gregorian cycle. This is done nicely with the code above; I just counted from Wikipedia’s list of Doomsdays, which conveniently highlights leap-years.

    Mon 15
    Tue 13
    Wed 15
    Thu 13
    Fri 14
    Sat 14
    Sun 13

    Total 97

Comments are closed.