Greetings fellow monks :)
I lead a small team of people that work a 24/7 shift roster. One of my less exciting duties is to maintain the roster and provide weekly updates. Whilst I was preparing this weeks update (which I do each Friday), I noticed something interesting which eventually led to quite a fascinating (for me, at least) discovery.
Each week, as I update the roster I save a copy, giving it a filename indicating the day/date. This week, as I was saving the file I noticed that in 2007, Friday has occurred on every day of the month. ie. (1 .. 31).
So this got me to wondering...
- Was this true of every day of the week?
- How does this vary from year to year, and is there any pattern involved?
So naturally, I wrote some code to find out :)
The result I got was quite surprising, and at first I thought there must be a silly bug in my code that was giving an erroneous result. But I've done some manual (Eyeball MK-II) checking, which does appear to verify it. Although, I am still half-expecting somebody to point out an error and make me look _really_ silly :)
In early iterations of the code I wrote, I was just testing a single year at a time. But as I kept getting similar results no matter which year I gave it, I eventually decided to test all years from 1 .. 2500.
The code I used is given below, but before I get to that - a couple of things to ponder....
- Without running the code or writing your own, can you predict the result?
- How would you have written this code? (golfers, please step forward)
#!/usr/bin/perl -l
use strict;
use warnings;
use Date::Calc qw/Days_in_Month Day_of_Week/;
my @days_of_week = qw/Mon Tue Wed Thu Fri Sat Sun/;
for my $year (1 .. 2500) {
my %days_of_month;
for my $month_of_year(1 .. 12) {
for my $day_of_month(1 .. Days_in_Month($year,$month_of_year))
+ {
my $dow = Day_of_Week($year,$month_of_year,$day_of_month);
# Day_of_Week uses a 1-based index, hence the --$dow
$days_of_month{$day_of_month}{$days_of_week[--$dow]}++;
}
}
for my $day_of_month (sort {$a <=> $b} keys %days_of_month) {
next if scalar keys %{$days_of_month{$day_of_month}} == 7;
my @missing_days;
for (@days_of_week) {
push (@missing_days, $_) if !defined $days_of_month{$day_o
+f_month}{$_};
}
print "In $year, Day $day_of_month does not fall on ",
join(", ", @missing_days);
}
}
Note that I deliberately haven't described the result, as I think that would spoil the fun. To find out, you'll either have to run the above code, or write your own :p
Cheers,
Darren :)
Re: How preparing the weekly shift roster led to a fascinating discovery...
by talexb (Chancellor) on Dec 14, 2007 at 16:06 UTC
|
Regarding testing years 1 to 2500, I'm not sure it's absolutely necessary to go through that many iterations. There are only 14 possible calendars anyway: the year can start on one of seven weekdays, and it's either a leap year or it isn't. Seven times two is fourteen different calendars.
Alex / talexb / Toronto
"Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds
| [reply] |
|
heh, yes... very good point. I've always been amused (and often quite frustrated) at my own ability to overlook the most obvious logic when my mind becomes focussed on one particular aspect of a problem. Something to do with trees and forests, I think :)
However, I should point out that in order to see the full pattern that emerges it is necessary to test for ~400 years. This makes sense of course, given the (Gregorian Calendar) Leap Year Rules.
The output from the following (modified from my original) code gives an indication of the pattern:
#!/usr/bin/perl -l
use strict;
use warnings;
use Date::Calc qw/Days_in_Month Day_of_Week/;
my @days_of_week = qw/Mon Tue Wed Thu Fri Sat Sun/;
my $cnt;
my @cnt;
for my $year (1 .. 2500) {
my %days_of_month;
for my $month_of_year(1 .. 12) {
for my $day_of_month(1 .. Days_in_Month($year,$month_of_year))
+ {
my $dow = Day_of_Week($year,$month_of_year,$day_of_month);
$days_of_month{$day_of_month}{$days_of_week[--$dow]}++;
}
}
for my $day_of_month (sort {$a <=> $b} keys %days_of_month) {
next if scalar keys %{$days_of_month{$day_of_month}} == 7;
my @missing_days;
for (@days_of_week) {
push (@missing_days, $_) if !defined $days_of_month{$day_o
+f_month}{$_};
}
$cnt++;
if ($missing_days[0] eq 'Sun') {
push @cnt, $cnt;
if ($cnt == 11) {
print "$year:@cnt";
@cnt = ();
}
$cnt = 0;
}
}
}
However, it's not so much the pattern that I found interesting - but the fact that every year it's _always_ the same day of the month (31st) that doesn't occur on every day of the week.
Cheers,
Darren :) | [reply] [d/l] |
|
It might be related to the fact that there are only seven 31s in a year compared to 11 for 29 and 30 and 12 for all lower numbers :-) (I originaly thought that it's because there are only 6 31s, but that was before I counted them.) In either case it's easier to miss one of seven days if you only have seven attempts than if you have 11 or 12.
| [reply] |
|
#!/usr/bin/perl
use strict;
use warnings;
use Date::Calc qw/Days_in_Month Day_of_Week/;
my @days_of_week = qw/NULL Mon Tue Wed Thu Fri Sat Sun/;
my $i=0;
# Our 14 calendars are in these years, this century
# see: http://www.koshko.com/calendar/calendar-lookup-gregorian.shtml
for (6, 7, 1, 2, 3, 10, 11, 12, 24, 8, 20, 4, 16, 0) {
my $year=$_+2000;
$i++;
my %days_of_month;
for my $month_of_year(1 .. 12) {
my $sday = Day_of_Week($year,$month_of_year,1); # only need to cal
+l this once
for my $day_of_month(1 .. Days_in_Month($year,$month_of_year)) {
$sday = 1 if($sday>7);
my $dow = $sday++;
$days_of_month{$day_of_month}{$dow}++;
}
}
for my $day_of_month (sort keys %days_of_month) {
next if(scalar keys %{$days_of_month{$day_of_month}} == 7);
my @missing_days;
for (1..7) {
push (@missing_days, $days_of_week[$_]) if !defined $days_of_mon
+th{$day_of_month}{$_};
}
print "Calendar $i: In $year, Day $day_of_month does not fall on "
+, join(", ", @missing_days), "\n";
}
}
| [reply] [d/l] |
Re: How preparing the weekly shift roster led to a fascinating discovery...
by CountZero (Bishop) on Dec 14, 2007 at 20:07 UTC
|
Sometimes we run away with ourselves ...You don't have to test all years, nor 400 years, not even 14 years or 7! Considering just one year (any year you wish) is enough. This might seem surprising, but it really isn't. As long as you don't start your year on the 1st of January, but on the 1st of March. Suppose, the 1st of March is a Monday then:
- 31st March is a Wednesday (30 days further, so 30 mod 7 = 2; 1 + 2 = 3 => Wednesday)
- 31st of May is a Monday (91 days after 1st of March: 91 mod 7 = 0; 1 + 0 = 1 => Monday)
- 31st of July is a Saturday (152 days after 1st of March; 152 mod 7 = 5)
- 31st of August is a Tuesday (183 days after 1st of March, 183 mod 7 = 1)
- 31st of October is a Sunday (244 days after 1st of March, 244 mod 7 = 6)
- 31st of December is a Friday (305 days since 1st of March, 305 mod 7 = 4)
- 31st of January is a Monday (336 since 1st of March of the previous year, 336 mod 7 = 0)
As you can see there is never a "+3" in the above sequence, meaning that the 31st will miss "day + 3" (where day 1 is the day of 1st of March).Because of leap years the last day of January can at the most shift one day, but as it is on the "+0" spot, it can never make it to the "+3" spot, hence "day +3" is always missing for the 31st. QED
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James
| [reply] |
|
| [reply] |
|
Yes, but it doesn't matter for the problem at hand. January of the previous year can at most be one day off its normal, non-leap day, but it can never become a "+3".To explain in a bit more detail what you have already found: As a normal year has 52 weeks and one day, January of the same year must thus be a "-1" (or "+6") compared to the day of the 1st of March of the same year. In a leap year, (52 weeks and two days) January 31st will be a "-2" (or "+5") day, but it can never ever be a "+3" day.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James
| [reply] |
|
mmm, yes.. that is a nice bit of deductive reasoning, but...
What if you hadn't of know beforehand that the 31st was the _only_ one that missed out?
I mean, once glide let the cat out of the bag and the result was obvious, then it becomes much easier to apply a bit of backwards logic to explain the phenomena.
Of course, I didn't know what the result would look like until I'd written some code to test it. And once the code was written, it became a simple matter to modify it to test for as many years as I wished.
By the way..
Because of leap years the last day of January ..
Don't you mean February?
Cheers,
Darren :)
| [reply] |
|
What if you hadn't of know beforehand that the 31st was the _only_ one that missed out? Then I would never have given the matter a single thought.But ... consider this: someone finds a curious phenomenon and reports it to his fellow scientists. These start running a number of experiments which prove or disprove this phenomenon in a variety of circumstances. Then someone steps in and formulates a theory and finally someone proves (based upon the existing body of scientific knowledge) that the theory is correct. I think we just have witnessed the scientific method at work in our Monastery!
Don't you mean February? No no, I really mean January. February never has 31 days, so by starting my "year" in March (as did the old Romans -- why do you think September really means the seventh month?) my reasoning does not have to take into account leap years but for the month of January (which as lidden said can only be a "+5" of "+6" day but never a "+3".) thus making my proof much easier and clearer.Shall we --in line with good scientific practice and hubris-- call this theory the "McDarren-CountZero-lidden Theory of the Missing Weekday of the 31st"?
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James
| [reply] |
|
| [reply] |
Re: How preparing the weekly shift roster led to a fascinating discovery...
by glide (Pilgrim) on Dec 14, 2007 at 15:20 UTC
|
and the result is ... (only a small part)
In 2455, Day 31 does not fall on Thu
In 2456, Day 31 does not fall on Sat
In 2457, Day 31 does not fall on Sun
In 2458, Day 31 does not fall on Mon
In 2459, Day 31 does not fall on Tue
In 2460, Day 31 does not fall on Thu
In 2461, Day 31 does not fall on Fri
In 2462, Day 31 does not fall on Sat
In 2463, Day 31 does not fall on Sun
In 2464, Day 31 does not fall on Tue
In 2465, Day 31 does not fall on Wed
In 2466, Day 31 does not fall on Thu
In 2467, Day 31 does not fall on Fri
In 2468, Day 31 does not fall on Sun
In 2469, Day 31 does not fall on Mon
In 2470, Day 31 does not fall on Tue
| [reply] [d/l] |
|
|