Peter
Peter Campbell Smith

Bad luck and mathematica

Weekly challenge 227 — 24 July 2023

Week 227 - 24 Jul 2023

Task 1

Task — Friday 13th

You are given a year number in the range 1753 to 9999.

Write a script to find out how many dates in the year are Friday 13th. Assume that the current Gregorian calendar applies.

Examples



Input:  $year = 2023
Output: 2

... as 13th January and 13th October are Fridays.

Analysis

To start with, note that there are only 14 different patterns of days within a year. 1 January can be any of the 7 days of the week, and any year can be a leap year or not. So we have can create the following table showing the number of Friday 13ths in each of these types of year:

1 Jan isNot leapLeap
Sun23
Mon22
Tue21
Wed12
Thu32
Fri11
Sat11

For example, 2024 is a leap year starting on Monday and it contains Friday 13 September and December. 2026 is a non-leap year starting on a Thursday, and it has Friday 13 February, March and November.

So first we have to establish whether the supplied year is a leap year - which is easy.

Then, slightly harder, we need to find out what day of the week the year starts on. I considered a number of ways of doing this but in the end settled on a loop from 1753 to the target year. We know from this helpful site that 1 January 1753 was a Monday. We can then step forward year by year knowing that:

  • in a year which follows a leap year, the day of week for 1 January will advance by 2 (eg Monday to Wednesday)
  • and in any other year it will advance by 1 (both modulo 7, of course).

Granted, for 9999 we will have to execute the loop 8246 times, but it's a very simple calculation, and even on my quite slow machine it runs in milliseconds.

(Disclosure: I submitted this challenge - before I tried solving it).

Try it 

Example: 2000

Script


#!/usr/bin/perl

use v5.16;    # The Weekly Challenge - 2023-07-24
use utf8;     # Week 227 task 1 - Friday 13th
use strict;   # Peter Campbell Smith
use warnings; # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

friday_13th(2023);
friday_13th(1945);
friday_13th(2012);
friday_13th(1753);
friday_13th(9999);

sub friday_13th {
    
    my ($target, $offset, $thirteenths);
    
    # 1 Jan is:      sun     mon     tue     wed     thu     fri     sat
    $thirteenths = [[2, 3], [2, 2], [2, 1], [1, 2], [3, 2], [1, 1], [1, 1]];
    
    $target = $_[0];
    $offset = -1;
    
    # 1 Jan moves forward 1 day, or 2 days if previous year was leap
    $offset += is_leap($_- 1) ? 2 : 1 for 1753 .. $target;

    say qq[\nInput:  $target\nOutput: ] . $thirteenths->[$offset % 7]->[is_leap($target)];
}

sub is_leap {  # returns 1 for leap year, 0 for non-leap
    
    #                      non-century year      century year 
    return ($_[0] % 100 ? ($_[0] % 4 ? 0 : 1) : ($_[0] % 400 ? 0 : 1));
}   


Output


Input:  2023
Output: 2

Input:  1945
Output: 2

Input:  2012
Output: 3

Input:  1753
Output: 2

Input:  9999
Output: 1