3 years of computing... the palindrome quest
fourmilab.ch
fourmilab.ch
As of 2006 the 300 million digit mark has been reached.
For anyone interested on today's state of knowledge, according to Wikipedia, there is still no known proof, but someone has reached the 13 Million digit mark without finding a palindrome:
"By May 1, 2006, VanLandingham had reached the 300 million digit mark (at a rate of one million digits every 5 to 7 days). A palindrome has yet to be found."
I got the same termination message (pass 2415836).
* Intel Core 2 Duo, model E6420, 80386 execution model. Intel Core/i7 Q720, x86-64 execution model.
Your 2006 PC is about 20 years newer than the 1987 PC, which means it should calculate it (3 years)/2^(20/1.5) amount of time. That is, based on his computation time, and the amount of time elapsed to the building of your computer, we'd expect your computer take 153 minutes, or 2:32. That's only 11 minutes off of the actual result!
I don't know how widely this is known, but the x86 instruction set actually has native support for BCD(!). AMD removed the instructions in x86_64 to free up some valuable short opcodes.
In any case, it'd be interesting how fast this sort of search would be on a GPU, using OpenCL or so. Vectorising arbitrary-precision BCD arithmetic could be a fun puzzle.
My first guess would be to look at the last digits of the previous chunk, and if it's not clear whether they will produce a carry (i.e. if they sum to 9), calculate both possibilities in parallel until they converge. Once the information is available, fix up the indeterminate pieces. Intuitively, it seems like decimal representation actually is better than binary for this as the probability of producing large sequences of 9s is lower than that of large binary 1 sequences, although only by a constant factor of ln(2)/ln(10) ~ 0.3.
It's certainly not trivial and requires a lot of communication between processors. The longer the numbers, the better, though. 300 million digits sounds like a suitable data set. (~150M bytes packed BCD, 4 copies of this will fit nicely into the currently tyical 1GB of graphics memory)
Then just keep track if at least one carry was generated instead of checking if the resulting number was a palindrome separately. Should be very fast.
while (i--)
*fp++ = *fp >> 4;
I'm sure he tested it and it does the right thing on his compiler/computer, but for anyone thinking of picking up the challenge...It's certainly confusing code, but I'm fairly sure the outcome is well-defined. That doesn't mean it's good code: I'd never write it like that. I find this far easier to understand, and that's how I'd write it in practice:
*fp >>= 4;
++fp;
The postfix while condition isn't one of my favourites either, but not quite as confusing.Not sure what 'may' means though
EDIT:
So to clarify, my suspicion is that the expression could mean either:
1)
*fp = *fp >> 4;
*fp++;
or2)
*fp = *(fp+1) >> 4;
*fp++;The thing is, at some point the compiler must calculate the address where to store the value. The question is then if there is a sequence point between calculating the data to store and the address which to store to. Otherwise there is ambiguity and the result is undefined.
According to http://en.wikipedia.org/wiki/Sequence_point there is no sequence point because of assignment (only initialization which this is not)
Btw was it you that downvoted my comment?
*fp = *fp >> 4;
fp++;
http://c-faq.com/ptrs/unopprec2.html for details.EDIT: formatting
According to the Standard: "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored."
What the second sentence says, is this: if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification. For example, the old standby i = i + 1 is allowed, because the access of i is used to determine i's final value. The example
*(fp++) = *fp >> 4; /* redundant parentheses */
is disallowed because one of the accesses of fp (the one in *fp) has nothing to do with the value which ends up being stored in fp (which happens over in fp++), and so there's no good way to define - either for our understanding or the compiler's - whether the access should take place before or after the modified value is stored.Hence, the quoted statement does invoke undefined behaviour.
http://en.wikipedia.org/wiki/Wait_Calculation
(that equation was published long after this article was written though)