| I remember a calendar day-of-the-week algorithm that came up
with a good representation for the running total of the
number of days in a month, modulo seven. This was clever,
but having only twelve data points must have helped a lot.
The calendar stuff follows. It is interesting but won't
help for five hundred data points.
Dan
I saw something like this used to encode an almost linear
function used in a day-of-the-week algorithm. Our calendar
has a period of 400 years. Leap year occurs in years
divisible by 4, unless also divisible by 100 when there is
no leap year, unless also divisible by 400 when there is.
So every 400 years, the leap year cycle repeats. It so
happens that these 400 years have a number of days which is
an exact multiple of 7. So after 400 years the next leap
year cycle starts on the same day of the week as the first
previous cycle. This led someone to develop a day of the
week formula.
So number the days of the modulo 7, with Sunday = 0 up to
Saturday = 6. Let y, m, and d be the year, month, and day
numbers. So for today, Thursday November 17, 1988, we have
y = 1988, m = 11, d = 17, and the day of the week is 4.
It is easiest to handle leap day as the last day of the
year, so we slightly massage the y, m, d inputs by treating
March as the first month, and February as the twelfth month
of the previous year. So the first step in the formula is
to make this transformation from y, m, d to Y, M, D:
{ y - 1 if m = 1 or m = 2
Y = {
{ y if 3 <= m <= 12
{ M + 10 if m = 1 or m = 2
M = {
{ M - 2 if 3 <= m <= 12
D = d
Now the day of the week number will be the number of days
past since some reference date added to the day of the week
number of that reference date, modulo 7. The contribution
of Y to the sum will be 365 = 1 mod 7 days per year, plus 1
for every leap year, for a total of
Y + [Y/4] - [Y/100] + [Y/400]
Here the notation [x] means the greatest integer <= x.
The contribution for the day will just be D. Now here is
where the "almost linear" function stuff starts, in
computing the contribution for the adjusted month number M.
For M=1 (March) the contribution will be 0; for M=2 the
contribution will be the number of days in March, modulo 7;
for M=3 the contribution will be the number of days in March
and April, modulo 7; etc. Now write this out, for each next
value of M adding the number of days of the previous month
modulo 7, but not reducing 7's from the sum:
M days days modulo 7 sum
-- ---- ------------- ---
1 0 0 0
2 31 3 3
3 30 2 5
4 31 3 8
5 30 2 10
6 31 3 13
7 31 3 16
8 30 2 18
9 31 3 21
10 30 2 23
11 31 3 26
12 31 3 29
or
M 1 2 3 4 5 6 7 8 9 10 11 12
f(M) 0 3 5 8 10 13 16 18 21 23 26 29
The problem was to express this function in an easy to
compute way. The solution, and I don't know who to credit
this to, is to use:
f(M) = [2.6 M - 2.1] = [2.6M - 2.2]
I had not memorized this, I just worked it out again, it
isn't too hard for this particular example. By the way,
in most programming languages, use truncating integer
arithmetic and let f(M) = (26 * M - 21)/10 or (13 * M - 11)/5
So the overall formula becomes
Y + [Y/4] - [Y/100] + [Y/400] + [2.6M - 2.1] + D + k
where k is the constant that gives the correct value for the
initial day. For today, with expected result 4, we get
y, m, d = 1988, 11, 17 Y, M, D = 1988, 9, 17
4 = 1988 + [1988/4] - [1988/100] + [1988/400] + [2.6 * 9 - 2.2] + 17 + k
(all that modulo 7, of course) or k = 2.
The [... - 2.1] and + 2 can be combined to give
>> f(Y, M, D) = Y + [Y/4] - [Y/100] + [Y/400] + [2.6M - 0.1] + D
You can precompute the result of the Y terms and remember
them for the next year or two. So, for example,
f(1988, M, D) = [2.6M - 0.1] + D + 6 modulo 7
f(1989, M, D) = [2.6M - 0.1] + D modulo 7
Of course, don't forget the transformation from y,m -> Y,M.
Dan
|