A Formula for the Number of Days in Each Month
cmcenroe.me
cmcenroe.me
-( 11 x^11)/907200
+( 163 x^10)/181440
-( 37 x^9 )/1260
+( 13481 x^8 )/24192
-( 2055371 x^7 )/302400
+( 240683 x^6 )/4320
-( 28268521 x^5 )/90720
+( 85774775 x^4 )/72576
-(446998571 x^3 )/151200
+( 46351537 x^2 )/10080
-( 221017 x )/56
+1416
That is a nice continuous, infinitely differentiable function.Source: http://www.wolframalpha.com/input/?i=interpolating+polynomia...
To get something more impressive, I would use the discrete Fourier transform. That leads to http://en.wikipedia.org/wiki/Trigonometric_interpolation. That gives you a nice formula with lots of sines and cosines.
We could call it Gloomember, the month that seems to go on and on.
http://boingboing.net/2011/10/11/mixtape-of-the-lost-decade....
[1] http://en.wikipedia.org/wiki/M%C3%B6bius_function [2] http://www.wolframalpha.com/input/?i=30+-+%CE%BC%28n%2B192%2... [3] http://en.wikipedia.org/wiki/Mertens_conjecture
For those that dislike taking powers of x, try this in Python:
def f(x):
return 62648012/4**x - 4*(15662003/4**x) + 28
map( f, xrange( 1, 13))I've been happy with the rhyme for a while. I can't remember it properly (in my head it ends "all the rest have strawberry jam, except my grandmother who rides a bicycle"), but it's obviously enough.
Now that I see it all laid out, though, I wonder if I can do better -- "Odd is 31 days to July, then it flips." Now the people in school counting on their knuckles all makes sense.
Zeller's is based upon calculating the day of the week, but it also calculates days in the month and accounts for leap years. Plus, it's been around since the 19th century and is used in many major programming languages to calculate date (its implementation is sometimes a first year Comp Sci course project for introductory programming): http://en.wikipedia.org/wiki/Zeller%27s_congruence
For a programming example that does this same thing: https://github.com/mwhawkins/JavaZellers
How did this make the front page of HN being a problem which has already been solved and well documented for almost 200 years? Not to knock the author, as they put in some time apparently, but was just curious.
Zeller's Congruence is an algorithm. This is a formula. It made it to the front page because it was a clever hack and people like me love those.
More seriously, any algorithm that doesn't do user I/O is expressible as a fixed formula. What distinction are you trying to draw?
This is probably true for some very weak definition of the terms, but not, I think, in any sense that a random person would recognise as a 'formula'. For example, what is the formula for the algorithm "on input `n`, output the first position in the decimal expansion of `pi` at which a copy of the decimal expansion of `n` begins"?
If you believe that f(x) = |x| is a "formula" giving the distance of a real number from zero, it's just as conditionally defined as (but less infinite than) the infinite lookup table you hypothesize.
On a more fun-fact note, there was a result some years ago expressing pi as an infinite series in powers of 16, with the headlining implication being "it may be possible to calculate the nth digit of pi (in base 16, or other powers of 2) directly, without needing to know the preceding digits" (I don't know the state of things since then, and details may have been jumbled in my memory). I suspect that there are implications for the puzzle you pose.
If you allow a formula to be an infinite look-up table, then the statement "every [always halting] algorithm is a fixed formula" becomes true, but, I think, almost meaningless—it just says that the algorithm produces an output for every input. This is what I meant by saying:
This is probably true for some very weak definition of the terms, but not, I think, in any sense that a random person would recognise as a 'formula'.
> On a more fun-fact note, there was a result some years ago expressing pi as an infinite series in powers of 16, with the headlining implication being "it may be possible to calculate the nth digit of pi (in base 16, or other powers of 2) directly, without needing to know the preceding digits" (I don't know the state of things since then, and details may have been jumbled in my memory). I suspect that there are implications for the puzzle you pose.No 'may' about it—it is possible to find the hexadecimal (and thus binary, 'quaternary', or octal, but not higher) digits using this algorithm. However, the discoverers explicitly disclaim its utility for finding decimal digits of pi in this way, and, indeed, say that no such 'look-ahead' algorithm is known (see p. 55 of http://link.springer.com/article/10.1007%2FBF03024340).
You object to my characterizing the pi-reading function you describe as an infinite lookup table. But it can't be implemented as an "intuitive" algorithm either. No matter how you code a function to solve that problem, it will always be trivial to provide input that your function cannot handle (if you have a representation of pi that you search, then you're relying on an infinite lookup table. If you calculate pi on the fly, you won't be able to handle large numbers as input). So I don't see why a defective (because you don't have enough time, or space, to run it) algorithm tranforming into a defective (because you don't have enough space to store it, but hey, time is no longer a concern) formula is a big problem for the equivalence between algorithms and formulae.
Also, I have to note that you've referred to the following mathematical result:
pi = \sum_{k=0}^\infty (16^{-k} * [4/(8k+1) - 2/(8k+4) - 1/(8k+5) - 1/(8k+6)])
as an "algorithm" instead of a "formula" ;)I'm sorry; you're quite right, and I was wrong.
> No matter how you code a function to solve that problem, it will always be trivial to provide input that your function cannot handle (if you have a representation of pi that you search, then you're relying on an infinite lookup table. If you calculate pi on the fly, you won't be able to handle large numbers as input).
I'm not sure why you say that I won't be able to handle large numbers as input. It might take a long time (like, a universe-endingly long time), but so would printing out the decimal expansion of 10↑↑10 (https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation), and I don't think that anyone would claim that that means that exponentiation algorithms "can't handle" this kind of exponentiation. (I dunno; maybe people would claim that.)
> So I don't see why a defective (because you don't have enough time, or space, to run it) algorithm tranforming into a defective (because you don't have enough space to store it, but hey, time is no longer a concern) formula is a big problem for the equivalence between algorithms and formulae.
My problem is that I think that there is no such equivalence, unless the definition of 'formula' is made so broad as to be essentially synonymous with 'algorithm'—at which point it's true but un-interesting. All I am arguing is (as a constructivist would) that it is worthwhile to maintain meaningful distinctions.
> Also, I have to note that you've referred to the following mathematical result `pi = [elided]` as an "algorithm" instead of a "formula" ;)
I did not mean to do that; I was referring to the process of using that formula (which is, as it were, an 'inert' object) to produce hexadecimal digits as an algorithm.
Zeller's Congruence is quite a bit harder to do in your head.
For mental calculation of day of week, I use a method given in a Martin Gardner book, but this only works for 1900-1999. I'll give a fix to extend it to other centuries.
Let Y be the last two digits of the year, let M be the month (1-12), and D be the day of the month (1-31). Let [X] denote the integer part of X.
Compute w = [Y/12] + Y%12 + [Y%12/4]
Compute w = w + month_offset(M), where month_offset is defined by this table:
M month_offset
1 1
2 4
3 4
4 0
5 2
6 5
7 0
8 3
9 6
10 1
11 4
12 6
Compute w = w + Dw%7 gives the day of the week, with Saturday being day 0. A couple notes.
1. You can reduce mod 7 as you go. For example, to get the day of the week of 1969-07-16, you could think "[69/12] = 5, add 9 (69%12) giving 14 == 0 mod 7. [9/4] = 2, so we are at 2. Add month_offset(7) == 0, giving 0. Add 16 (D), and we have 3 mod 7, so Wednesday".
2. Month_offset is easy to remember if you think of it this way:
1 4 4
0 2 5
0 3 6
1 4 6
That's 12^2, 5^2, 6^2, and 12^2+2. I don't know why that makes it easy, but Gardner suggested it, and I have not forgotten it in something like 40 years, even though I rarely use it.3. If the year is a leap year, subtract 1 for dates in January and February.
4. For years in 2000-2099, subtract 1 from the result. For years in 1800-1899, add 2. If you forget what the century correction is, you can work it out in your head. For instance, to find the correction for 20xx, work out the day of week of 1999-12-31: 99=12x8+3, [3/4]=0, month_offset(12)=6, so 8+3+0+6+31==6 mod 7, or Friday. Now work out 2000-01-01 without a century correction: 0=0x12+0, [0/4]=0, month_offset(1)=1. 2000 is a leap year, so need to subtract 1 in January, so we have 0+0+0+0+1-1+1==1 mod 7, so Sunday. However, it should be Saturday since it comes after Friday, and so the century correction must be -1 for 20xx.
5. Gardner's method is really one of the other well known methods (I forget which one) adjusted to make mental calculation easier.
Addendum: this is described in chapter 7, "Tricks of Lightning Calculators" from the Martin Gardner collection "Mathematical Carnival".
In any year, 4/4, 6/6, 8/8, 10/10, 12/12 fall on the same day, so do 9/5, 5/9, 7/11, 11/7 (mnemonic: 9-to-5, 7-11). If you know what day that is for the year (Friday for 2014, Sat for 2015), you can quickly calculate the day of the week for many of the days.
Jan/Feb/Mar require a little bit thought, but with practice reasonably quick.
April, June, and November
All the rest have 31
Except for the very special one.
...
The knuckles method, for those who don't know: Make a fist. Start with January on your first knuckle. Counting knuckles AND the gaps between knuckles, so each knuckle is 31 days, the gap between is 30 days (or 28/29 for february). Repeat on the last knuckle before reversing.
...
So--
KNUCKLE_gap_KNUCKLE_gap_KNUCKLE_gap_KNUCKLE
JAN_feb_MAR_apr_MAY_jun_JUL (repeat last knuckle and reverse)
AUG (knuckle repeat)_sep_OCT_nov_DEC (ending on your middle knuckle)
ALLCAPS = KNUCKLE = 31 days
Thirty days hath September,
April, June, and November
And all the rest have thirty-one
excepting February alone
And that has twenty-eight days clear
and twenty-nine in each leap year
That way's more explicit, but I like yours better. It reminds me of a homework problem I had in real analysis, which said "show that [the standard infinite series for e] is both strictly increasing and bounded above and therefore converges to a very special real number".On the due date, someone came into class saying "I could show that the series was increasing and bounded above, and therefore converged to a real number, but I didn't know how to show that the limit of the series was, quote, very special." :D
It gives you one less thing to remember, since you don't have to remember to repeat on the last knuckle.
Probably because there is almost a pattern, but not quite.
The knuckle method seems to me to be the "simple" way to count cows--just count the legs and divide by four.
Thirty Days hath December,
March, July and November,
All the rest have 31
Except January.
That's better than the poem in that you can't mix it up, AND it's faster than audiating all of that.
I certainly wouldn't recommend my usual approach though: alternate 31/"not 31" starting from October (Halloween is the 31st) or March (my birthday is close enough to the end of the month for me to remember that March has 31 days.)
A 'nomth' is a month in which a feast occurs (http://en.wikipedia.org/wiki/Feasts_of_Jesus_Christ).
1. alternate 31 and 30
2. reset in month 8
3. alter feb to 28
In my mind using modulus and floor in a mathematical formula is no more valid than using cases. A clever formula would avoid all of them.Admittedly rounding is still cheating but it allows compacter solutions than being spot-on every time. Handling February is left as an exercise for the reader.
Agreed. Using floor it seems like you can construct a function that's effectively defined piecewise, by constructing a floor expression that's zero outside of a specified input domain, and 1 within the domain. Then you multiply the floor expression by the piecewise expression that you'd use to define a piecewise function over that domain.
Nevertheless I found the article to be an interesting read on mathematical reasoning - a thought process for designing functions with particular behavior. Is there a rigorous way of describing why mod and floor seem like cheating in a way, aside from the fact that they can be used to construct piecewise functions? Is the ideal hack in this scenario a continuous, smooth function? (And something more interesting than the result of polynomial interpolation?)
And you can fit a polynomial the same way by (using the example at hand) defining
f(x) = c_1(x-2)(x-3)...(x-12)
+ c_2(x-1)(x-3)...(x-12)
+ c_3(x-1)(x-2)...(x-12) [but the (x-3) factor is missing!]
+ . . .
+ . . .
+ . . .
+ c_12(x-1)(x-2)...(x-11)
And then choosing c_1 such that the c_1 term is equal to 31 when x=1, c_2 such that the c_2 term is equal to 28 when x=2, and so forth. You'll get a well-defined polynomial, impeccably "mathematical" -- in fact, you should get the polynomial listed in Someone's comment (since there should only be one 11th-degree polynomial passing through those 12 points). The traditional way to fit a polynomial to points is by reducing a matrix, linear-algebra style, but this approach is equivalent and, as you can see, amounts to nothing more than defining several functions which are zero except at the point of interest, and then scaling them to take the proper value at the point of interest. Why is that more "valid" than using a modulus?Frankly, I'm much more concerned by the OP using "2 mod x" when 1 is a legal value for x.
int result = (30|m^(m>>3)) - (((m^13)+1)>>4<<1);
def days(month):
return [31,28,31,30,31,30,30,31,30,31,30,31][month]For example, see the Collatz conjecture which only defines two cases (but one could have 12): http://en.wikipedia.org/wiki/Collatz_conjecture
http://www.infoplease.com/spot/history-of-august.html
It just makes it a more interesting formula because of that!
days = 28 + ((0x3bbeecc>>(month*2))&3); 28+((0x3bbeecc>>(m*2))&3)
But here's my own attempt anyway, which wins on number of characters typed =) 30+(m&1^m>7)-m*(m==2) 0 + [anything, 31,28,31,30,31,30,31,31,30,31,30,31]
28 + [anything, 3, 0, 3, 2, 3, 2, 3, 3, 2, 3, 2, 3]
00, 11,00,11,10,11,10,11,11,10,11,10,11
reverse: 11,10,11,10,11,11,10,11,10,11,00,11, 00
0011 1011 1011 1110 1110 1100 1100
0x 3 b b e e c c
We have 4 possible values in the array, so we need 2 bits per value. We have one dummy value + 12 required values, so we need a total of 2*13 = 26 bits which fits easily into a 4 byte integer (either 32 bits unsigned or 31 bits signed). To retrieve one element from the "array", we need to shift our constant 2 bits per month or twice by the numer of months. Then we need to mask everything but the lowest 2 bits (&3).http://timesofindia.indiatimes.com/home/stoi/Why-does-Februa...
// 28 + ((0x3bbeecc>>m>>m)&3);
b8 cc ee bb 03 mov eax,0x3bbeecc
d3 f8 sar eax,cl
d3 f8 sar eax,cl
83 e0 03 and eax,0x3
83 c0 1c add eax,0x1c 04 1c add al, 0x1c
(but i've never written any x86 assembly) b8 cc ee bb 03 mov eax,0x3bbeecc
d3 f8 sar eax,cl
d3 f8 sar eax,cl
24 03 and al, 3
04 1c add al, 28ETA: I think the simplest I can get it is:
days(n) = 28 + (0x1df6 << (1^n&3) >> n) & 3
My intuition is that the 1^n&3 isn't going to be cheaper.f(n) = 28 + ((0x3bbeecc >> n >> n) & 3)
I'm not sure this post is meant to be a model for good implementation advice, though, performance-oriented or otherwise. Just an interesting tidbit.
EDIT: I'm trying to figure out how to write code using registers instead of indexes into memory and it ends up with lots of conditionals instead of basically one instruction. The conversation about speed is... tricky. Can some people who are more expert help out?
days y m = 28 + (m + (m `div` 8)) `mod` 2 + 2 `mod` m + 2 * (1 `div` m)
+ ((2 `div` m) `mod` 2) * (leap y)
where leap y = multiple 4 y - multiple 100 y + multiple 400 y
where multiple int y = ((y + (int - 1)) `mod` int + 1) `div` int function f(x) { return 28 + maybeLeap(x) + (...); }
I remember it's something related with "(x % 4) == 0" and "not ends_with(00)" leap = function(x) {
if ((x % 4) != 0) {
return 0;
}
return Number(
(x % 100) != 0 || (x % 400) == 0
);
}
function(x) { return 28 + leap(x) + (...) };let days = (i ^ (i >> 3) | 30) - 1 / (i * 9 % 17) * 2
echo $(( i = <month>, (i ^ (i >> 3) | 30) - 1 / (i * 9 % 17) * 2 ))function days2(i) { return (i ^ (i >> 3) | 30) - 1 / (i * 9 % 17) * 2 }
1 30.77777777777778 2 28 3 30.8 4 29 5 30.818181818181817 6 29.333333333333332 7 30.833333333333332 8 30.5 9 29.846153846153847 10 30.6 11 29.857142857142858 12 30.666666666666668
> Why is it any better/faster than a simple switch case like this in Clojure
It's not about better or faster. It's fun to think of things like this. :)
The OP didn't claim it was better than anything, it's just a nice exercise in style!
var f = function(i){return new Date(2014,i,0).getDate();};
JSON.stringify([1,2,3,4,5,6,7,8,9,10,11,12].map(f)) == JSON.stringify([31,28,31,30,31,30,31,31,30,31,30,31]);
:P
function days(m) { return 30 + ((m + (m > 6)) % 2) - 2 * (m == 2) }
def days_in_month(month_num):
if month_num in [1, 3, 5, 7]:
return 31
elif month_num in [4, 6, 9, 11]:
return 30
return 28