Here are some notes on my current program...
I am using a computer which has an Intel Core i7-2600 with 4 cores.
Thanks to hyper-threading this presents as (almost) 8 processors. The
computer has 16GB of RAM. (Although the program is not memory-hungry.)
The current power of two is about 2^1.06 billion. The power of two
increases by about 200 every second - but that rate is continually
decreasing.
The current power of two is held to base 10^6. Each base 10^6 "digit"
is held in a 32-bit integer. This array currently has 54 million
elements. Its length is increased by a million elements whenever it
gets full - about every 20 million powers of two.
The program is written in C# and has a Master thread and 8 Worker
threads.
The Master decides on every iteration whether to just double the
current power of two, or to multiply it by 2,048 (=2^11).
The Master partitions the array of integers into about 500
almost-equal sized chunks and then releases the Workers. The Workers
then go through the list of chunks and - for each chunk - either
double it or multiply it by 2,048. As each Worker processes a chunk it
looks out for zero (base 10^6) digits and records their position in a
global table. When all the chunks have been handled the Master wakes
up.
First of all, the Master handles the chunk-to-chunk carries and - if
the array of integers is now full, allocates another 10^6 integers. On
a x2048 iteration the chunk-to-chunk carry can propagate through
several elements of a chunk, possibly destroying digits previously
recorded as zero or creating new zero digits.
The Master then looks through the table of zero digits. [The Master
must handle the case where one of these digits was previously marked
as zero, but is no longer zero.] The program then examines the digits
adjacent to the zero digit and determines the exact number of
consecutive decimal zeros. If there is a run of 11 (or more) decimal
zeros anywhere then there must be a base 10^6 zero so any runs of at
least 11 decimal zeros will be found. A run of 10 decimal zeros will
be missed if it occurs as "m00000 00000n" as two base 10^6 digits. So,
if no run of 11 zeros is found this means that the longest run of
decimal zeros is ten or less. In this case the next iteration of the
program can multiply the current power of two by 2^11. This happens
about 99.2% of the time, at the moment.
The result of eleven doublings is illustrated here:
"aaaaaaaaaaaa5" is a string of digits which, every time it is doubled,
produces a new zero digit on the right. (If that string is n digits
long it will, in fact, be some odd multiple of 5^n.)
Z....Z is a string of zeros
cccccc is any string
(a fixed width font is best here)
Step Extra zeros
aaaaaaaaaaaa5Z......Z1cccccc
1: aaaaaaaaaaa50Z......Z2cccccc 1
2: aaaaaaaaaa500Z......Z4cccccc 2
3: aaaaaaaaa5000Z......Z8cccccc 3
4: aaaaaaaa50000Z.....Z16cccccc 3
5: aaaaaaa500000Z.....Z32cccccc 4
6: aaaaaa5000000Z.....Z64cccccc 5
7: aaaaa50000000Z....Z128cccccc 5
8: aaaa500000000Z....Z256cccccc 6
9: aaa5000000000Z.. .Z512cccccc 7
10: aa50000000000Z...Z1024cccccc 7
11: a500000000000Z...Z2048cccccc 8
Clearly, 8 zeros is the most be can get from 11 doublings.
So, if Z....Z is a string of ten zeros, and we are looking for 18
zeros, it is safe to multiply by 2^11 (=2048). Note that if we have a
run of 11 zeros and we multiply by 2^11 we might miss a run of 18
zeros which would have appeared at the 2^9 or 2^10 stage but which
then decreases to a run of only 17 zeros during the last one or two
doublings.
The program displays various progress metrics every few seconds. One
of these is a notional "picoseconds per digit". This being k times
10^12 divided by the notional number of digits of powers of two that
it has handled in the last k seconds (including those powers of two
that it skipped over by multiplying by 2048). At the moment this
number is sitting at about 16. This is a metric that can probably be
usefully applied to compare any algorithms which are looking for the
18 zeros. It is likely that the total number of digits to be examined
is of the order of 10^18 - so this picoseconds per digit figure can
lead to a crude time estimate for the complete task: about 6 months.
Other points:
The search for 18 consecutive zeros should start at about 2^918
million. However, actually computing that number in base million is a
challenge. I use a link to the Mathematica kernel running on my
computer to do this. Mathematica takes about 5 minutes to perform the
computation.
The program writes a restart file to disk every hour or if I manually
terminate the program. The program uses .NET serialization and
deserialization to save and restore (parts of) the C# object which
performs the computations. Before serializing the object the program
checks the current power of two for correctness. First it checks that
every base million digit is less than 1 million. Then it divides the
current power of two by a ten digit prime and stores the remainder. It
then computes (2^currentPower) mod thatPrime (by the usual method) and
compares the two results. If they are different we have a problem. In
fact it runs the check twice. Once with the certain prime, and once
with a random integer - just out of paranoia.
The number of chunks (about 500) which I mentioned above is not
determined very scientifically. I started off with just 8 chunks - but
then if one thread gets held back for some reason all the other
threads will just wait for it to finish (and not be able to help it)
before Master can be woken up. I thought that a smaller chunk size
would make this problem less serious.
A different threading approach would be to allocate various ranges of
powers of two to different threads and let each report back after a
day or two and be given a new range.
See...
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_frm/thread/cf40546615f38239#
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_frm/thread/477b516cc60977fd#
for some discussion of a slightly earlier version of the program.
--
Clive Tooth