Digits of Pi (#202)

Daniel M. wrote:

This week’s quiz is to write a Ruby program that can compute the first
100,000 digits of π.

There’s the impl on the benchmarks game (shootout):

http://shootout.alioth.debian.org/u32q/benchmark.php?test=pidigits&lang=ruby&id=1

But it doesn’t seem fast enough to get to 100k digits in any amount of
time I’m willing to wait for it. Here’s timings for all the Ruby
versions I have handy, calculating up to 10k digits:

(jruby time is on Java 6 server VM)

JRuby 1.3-dev: 0m47.903s
Ruby 1.9.1: 1m27.527s
Rubinius master: 2m50.545s
MacRuby 0.4: 1m9.832s
IKRuby*: 3m26.861s
Ruby 1.8.7: 1m47.887s

MacRuby 0.5-experimental crashed after only a few digits. IKRuby is
JRuby on CLR (Mono, here) using IKVM.

Seems like we need a faster algorithm!

  • Charlie

On Thu, Apr 30, 2009 at 5:02 AM, Jay A. [email protected]
wrote:
The good new is that I chose the correct algorithm from the beginning.
The bad news is that I was waaaay to stupid to implement it, well
done.
BTW I tested your result, it seems to be correct :).
Cheers
Robert


Si tu veux construire un bateau …
Ne rassemble pas des hommes pour aller chercher du bois, préparer des
outils, répartir les tâches, alléger le travail… mais enseigne aux
gens la nostalgie de l’infini de la mer.

If you want to build a ship, don’t herd people together to collect
wood and don’t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.

Digits of Pi (#202)

This week’s quiz is to write a Ruby program that can compute the first
100,000 digits of pi.

Attached is a solution using a machin formula
(Machin-like formula - Wikipedia) translated into ruby
from here:
http://en.literateprograms.org/Category:Pi_with_Machin's_formula.

$ time ruby pi.rb 100000 > pi.txt

real 1m44.592s
user 1m44.339s
sys 0m0.212s

No effort has been made to optimize, but it seems to have a reasonable
running time.

-----Jay

On Thu, Apr 30, 2009 at 7:21 AM, Robert D. [email protected]
wrote:

On Thu, Apr 30, 2009 at 5:02 AM, Jay A. [email protected] wrote:
The good new is that I chose the correct algorithm from the beginning.
The bad news is that I was waaaay to stupid to implement it, well
done.
BTW I tested your result, it seems to be correct :).
Cheers

Jay’s version seemed to work fine. It took about 5 minutes on my
machine for 100_000.

I haven’t verified the digits yet, though.

Todd

On Thu, Apr 30, 2009 at 10:21 PM, Todd B. [email protected]
wrote:

I haven’t verified the digits yet, though.
I have :wink:

Now that I learnt from Jay not to use BigDecimal :slight_smile: I have
implemented Chen-Lih’s machin formula,
last on this page: Machin-like formula - Wikipedia

But the speed gain is minimal 20~30% so that is definitely not worth
posting a stolen solution with this monster expression :wink:
It run 70s instead of 97s (yes I have top notch hardware :wink: for 100_000
digits.

Cheers
Robert

Todd


Si tu veux construire un bateau …
Ne rassemble pas des hommes pour aller chercher du bois, préparer des
outils, répartir les tâches, alléger le travail… mais enseigne aux
gens la nostalgie de l’infini de la mer.

If you want to build a ship, don’t herd people together to collect
wood and don’t assign them tasks and work, but rather teach them to
long for the endless immensity of the sea.

This week’s quiz sparked quite a discussion!

Dan D. introduced the Bailey-Borwein-Plouffe formula1, a
formula for computing the nth binary digit of ð without computing the
previous digits, though no solutions were provided that incorporated
this formula.

Additionally, there was some talk of finding the first non-trivial
Ruby program encoded in the digits of ð, but choosing an appropriate
encoding also proved to be non-trivial.

Harry K. provided the first solution and made use of BigMath:

require 'bigdecimal'
require 'bigdecimal/math'
include BigMath

puts PI(100_000)

Executing this solution took a little over five minutes on my machine.
It is always good to know what libraries are already available.

Luke C.'s solution uses the Leibniz formula for pi3.

require "rational"
require 'enumerator'
require 'bigdecimal'
require 'bigdecimal/math'
include BigMath

iterations = 10000000000
current = 1
final = BigDecimal.new("4")
other = false

while(current < iterations) do
  current = current + 2
  if(other)
    final = final + Rational(4,current)
  else
    final = final - Rational(4,current)
  end

  other = !other
  print current.to_s + ":"
  puts final.to_f
end

Unfortunately, Leibniz’s formula is very inefficient for either
mechanical or computer-assisted ð calculation. Calculating ð to 10
correct decimal places using Leibniz’ formula requires over
10,000,000,000 mathematical operations3. Luke stated on the mailing
list this algorithm was only able to generate about eight digits in
five minutes.

Jay A. provided a solution using a Machin-like formula4 based
on the implementation from the LiteratePrograms wiki5:

def arccot(x, unity)
    xpow = unity / x
    n = 1
    sign = 1
    sum = 0
    loop do
        term = xpow / n
        break if term == 0
        sum += sign * (xpow/n)
        xpow /= x*x
        n += 2
        sign = -sign
    end
    sum
end

def calc_pi(digits = 10000)
    fudge = 10
    unity = 10**(digits+fudge)
    pi = 4*(4*arccot(5, unity) - arccot(239, unity))
    pi / (10**fudge)
end

digits = (ARGV[0] || 10000).to_i
p calc_pi(digits)

This solution produces 100k digits in around 2 minutes on my machine.

Robet Dober mentions a speed increase when using Hwang Chien-Lih’s
Machin-like formula with additional terms6:

pi = 4*(183*arccot(239, unity) + 32*arccot(1023, unity) -

68arccot(5832, unity) + 12arccot(110443, unity) - 12arccot(4841182,
unity) - 100
arccot(6826318, unity))

This does indeed improve the efficiency of the algorithm, I achieved
15-20% reduction in the time the algorithm took, down to about 100
seconds.

Charles Oliver N. pointed out the Ruby pidigits implementation on
the Computer Language Benchmarks Game7 and provided some benchmarks
for various Ruby implementations:

(jruby time is on Java 6 server VM)

JRuby 1.3-dev:    0m47.903s
Ruby 1.9.1:       1m27.527s
Rubinius master:  2m50.545s
MacRuby 0.4:      1m9.832s
IKRuby*:          3m26.861s
Ruby 1.8.7:       1m47.887s

MacRuby 0.5-experimental crashed after only a few digits. IKRuby

is JRuby on CLR (Mono, here) using IKVM.

These benchmarks only produced 10k digits of ð (for expediency). One
lesson to take away from this is that algorithm choice often has the
biggest impact on performance.

10,000 Digits (Ruby 1.8.6 on my machine)
  Machin-like formula : ~ 1s
  BigMath                   : ~ 2.5s
  pidigits                    : ~ 1m30s
  Leibniz formula       : ~ ?

And sometimes the fastest code is the code you don’t write. Michael
Kohl’s solution:

require 'rubygems'
require 'hpricot'
require 'open-uri'

doc = Hpricot(open('http://www.eveandersson.com/pi/digits/100000'))
puts (doc/'pre').inner_html

It finishes in less than one second for the entire 100,000 digits!

Thank you everyone for your solutions and discussion! ð is a timeless
concept that has fascinated great minds for thousands of years and
will continue to do so. If you are interested in learning more please
follow the references as this summary barely scratches the surface.
Thanks again to all who participated this week.

P.S. If you have any future quiz ideas be sure to submit them:
http://rubyquiz.strd6.com/suggestions.