Skip to content

Instantly share code, notes, and snippets.

@lexi-lambda
Last active January 21, 2016 02:46
Show Gist options
  • Save lexi-lambda/3b2b9bc55206685242b9 to your computer and use it in GitHub Desktop.
Save lexi-lambda/3b2b9bc55206685242b9 to your computer and use it in GitHub Desktop.
Crazier way to compute pi based on random number generation (thanks to @quephird’s https://gist.github.com/quephird/039b0254a5a5696346ae)
module Pi where
import Data.List (genericLength)
import Control.Arrow (Arrow, (<<<), (***), arr)
import System.Random (newStdGen, randoms)
type Point a = (a, a)
chunk2 :: [a] -> [(a, a)]
chunk2 [] = []
chunk2 [_] = error "list of uneven length"
chunk2 (x:y:r) = (x, y) : chunk2 r
both :: Arrow arr => arr a b -> arr (a, a) (b, b)
both f = f *** f
unsplit :: Arrow arr => (a -> b -> c) -> arr (a, b) c
unsplit = arr . uncurry
randomFloats :: IO [Float]
randomFloats = randoms <$> newStdGen
randomPoints :: IO [Point Float]
randomPoints = chunk2 <$> randomFloats
isInUnitCircle :: (Floating a, Ord a) => Point a -> Bool
isInUnitCircle (x, y) = x' + y' < 0.25
where x' = (x - 0.5) ** 2
y' = (y - 0.5) ** 2
lengthRatio :: (Fractional c) => [b] -> [b] -> c
lengthRatio = curry (unsplit (/) <<< both genericLength)
approximatePi :: [Point Float] -> Float
approximatePi points = circleRatio * 4.0
where circlePoints = filter isInUnitCircle points
circleRatio = circlePoints `lengthRatio` points
main :: IO ()
main = do
putStrLn "How many points do you want to generate to approximate π?"
numPoints <- read <$> getLine
points <- take numPoints <$> randomPoints
print $ approximatePi points
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment