-
-
Notifications
You must be signed in to change notification settings - Fork 126
Spigot Algorithms
This is a description of how to use Spigot algorithms to compute mathematical constants. It is based on Rabinowitz and Wagon's paper "A Spigot Algorithm for the Digits of π", but also adds several innovations and generalizations that might perhaps not be in the literature.
This is probably the most efficient way to solve the holes for the mathematical constants described in this article for languages that don't have built-in bignum support, and in particular seems most efficient for Assembly.
If you do have built-in bignum support, then it's probably more efficient to compute
Note that depending on how exactly you implement and optimize the loops in your language, you might need to replace
The idea of spigot algorithms is to express numbers as this sum, known as a "mixed-base representation", which is an extension of the customary base-N notation (the special case of
where
Or more formally:
Note that if
The key advantage is that it is easy to print this number in base 10 (or any base) with this algorithm, which is just a minor adaptation of the algorithm to print arbitrary-precision fractional bignums, as long as this condition always holds for any possible intermediate value produced by the algorithm:
In case that
# initialize a0 and a_j for the constant to print, make sure that a_j < d_j
print(to_string(a0) + ".")
for _ in 0..OUTPUT_DIGITS {
# set A = 10 (A - a0), and reduce to maintain |a_j| < d_j
C := 0 # actually any value works if P is big enough
for j in P..1 {
t = a_j * 10 + C
q = t // d_j
r = t % d_j
a_j := r
C := q * n_j
# note it's also possible to multiply by n_j at the start of the loop
# and in a0 = C (but usually n_1 = 1, so the latter is a no-op)
}
a0 = C
# here we still have |a_j| < d_j due to the modulus operation,
# and a_j >= 0 is preserved if a_j >= 0 and n_j >= 0 initially
print(a0)
}
This algorithm works because
This can be expressed by
We have that
For most other constants such as
The Rabinowitz paper presents "predigits" as a mechanism to handle this, and while it works it's not efficient for code golfing purposes (and probably not for any other purpose either) since it requires a lot of extra code.
Instead, we modify the constant by adding a "buffer" term:
The idea is that instead of printing digits right away, we hold them in the buffer term
More formally, we express the constant
We then have that as required:
Now the condition that needs to hold becomes:
This is equivalent to
Depending on the language used, it may be most efficient to set
Hence
Hence
From symbolic computation:
Hence
Setting
Buffering can also supports cases where the
In this case, we need to calculate
Hence
Hence
The Euler-Mascheroni constant
This identity holds for exponential integrals:
Rearranging and setting
We have
The digits of
For the second part, we have
However, we can't directly use
So, instead, we use the simpler mixed-base representation
For this purpose, we manipulate the expression like this:
This can be computed by starting with
All this together allows to compute the Euler-Mascheroni constant with spigot algorithms. For 1000 digits,