Aproximación del número pi
Una forma de aproximar el número π es usando la siguiente igualdad:
| 1 2 3 |     π         1     1·2     1·2·3     1·2·3·4         --- = 1 + --- + ----- + ------- + --------- + ....     2         3     3·5     3·5·7     3·5·7·9 | 
Es decir, la serie cuyo término general n-ésimo es el cociente entre el producto de los primeros n números y los primeros n números impares:
| 1 2 3 |                Π i       s(n) =  -----------             Π (2*i+1) | 
Definir la función
| 1 |    aproximaPi :: Integer -> Double | 
tal que (aproximaPi n) es la aproximación del número π calculada con la serie anterior hasta el término n-ésimo. Por ejemplo,
| 1 2 3 4 5 6 7 |    aproximaPi 10     == 3.1411060206    aproximaPi 20     == 3.1415922987403397    aproximaPi 30     == 3.1415926533011596    aproximaPi 40     == 3.1415926535895466    aproximaPi 50     == 3.141592653589793    aproximaPi (10^4) == 3.141592653589793    pi                == 3.141592653589793 | 
Soluciones
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import Data.Ratio ((%)) import Data.List (genericTake) import Test.QuickCheck (Property, arbitrary, forAll, suchThat, quickCheck) -- 1ª solución -- =========== aproximaPi1 :: Integer -> Double aproximaPi1 n =    fromRational (2 * sum [product [1..i] % product [1,3..2*i+1] | i <- [0..n]]) -- 2ª solución -- =========== aproximaPi2 :: Integer -> Double aproximaPi2 0 = 2 aproximaPi2 n =    aproximaPi2 (n-1) + fromRational (2 * product [1..n] % product [3,5..2*n+1]) -- 3ª solución -- =========== aproximaPi3 :: Integer -> Double aproximaPi3 n =    fromRational (2 * (1 + sum (zipWith (%) numeradores (genericTake n denominadores)))) -- numeradores es la sucesión de los numeradores. Por ejemplo, --    λ> take 10 numeradores --    [1,2,6,24,120,720,5040,40320,362880,3628800] numeradores :: [Integer] numeradores = scanl (*) 1 [2..] -- denominadores es la sucesión de los denominadores. Por ejemplo, --    λ> take 10 denominadores --    [3,15,105,945,10395,135135,2027025,34459425,654729075,13749310575] denominadores :: [Integer] denominadores = scanl (*) 3 [5, 7..] -- 4ª solución -- =========== aproximaPi4 :: Integer -> Double aproximaPi4 n =    read (x : "." ++ xs)   where (x:xs) = show (aproximaPi4' n) aproximaPi4' :: Integer -> Integer aproximaPi4' n =    2 * (p + sum (zipWith div (map (*p) numeradores) (genericTake n denominadores)))    where p = 10^n -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_aproximaPi :: Property prop_aproximaPi =   forAll (arbitrary `suchThat` (> 3)) $ \n ->   all (=~ aproximaPi1 n)       [aproximaPi2 n,        aproximaPi3 n,        aproximaPi4 n] (=~) :: Double -> Double -> Bool x =~ y = abs (x - y) < 0.001 -- La comprobación es --    λ> quickCheck prop_aproximaPi --    +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es --    λ> aproximaPi1 3000  --    3.141592653589793 --    (4.96 secs, 27,681,824,408 bytes) --    λ> aproximaPi2 3000  --    3.1415926535897922 --    (3.00 secs, 20,496,194,496 bytes) --    λ> aproximaPi3 3000  --    3.141592653589793 --    (3.13 secs, 13,439,528,432 bytes) --    λ> aproximaPi4 3000  --    3.141592653589793 --    (0.09 secs, 23,142,144 bytes) | 
El código se encuentra en GitHub.