Haskell impelmentation (arithmetics only) of the Bertrand Paradox
Last active
February 17, 2025 01:07
-
-
Save Malix-Labs/6c1503369d7d7845d3973eeba25902c9 to your computer and use it in GitHub Desktop.
Bertrand Paradox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import System.Random | |
import Control.Monad | |
import Data.Fixed | |
import Text.Printf | |
-- Type alias for a Point (for simplicity, in 2D) | |
type Point = (Double, Double) | |
-- Radius of the circle (assume unit circle for simplicity) | |
circleRadius :: Double | |
circleRadius = 1.0 | |
-- Side length of the inscribed equilateral triangle in a unit circle is sqrt(3) | |
triangleSideLength :: Double | |
triangleSideLength = sqrt 3.0 | |
-- Distance between two points | |
distance :: Point -> Point -> Double | |
distance (x1, y1) (x2, y2) = sqrt ((x2 - x1)^2 + (y2 - y1)^2) | |
-- Generate a random point on the circumference of a unit circle | |
randomPointOnCircle :: RandomGen g => g -> (Point, g) | |
randomPointOnCircle gen = | |
let (angle, gen') = randomR (0, 2 * pi) gen | |
x = circleRadius * cos angle | |
y = circleRadius * sin angle | |
in ((x, y), gen') | |
-- Generate a random point within the circle (uniform distribution over the area) | |
randomPointInCircle :: RandomGen g => g -> (Point, g) | |
randomPointInCircle gen = | |
let (r, gen') = randomR (0, circleRadius) gen -- Incorrect: leads to non-uniform distribution | |
(angle, gen'') = randomR (0, 2 * pi) gen' | |
x = r * cos angle | |
y = r * sin angle | |
in ((x, y), gen'') | |
-- Correct method for uniform distribution within the circle using squared radius | |
randomPointInCircleUniform :: RandomGen g => g -> (Point, g) | |
randomPointInCircleUniform gen = | |
let (rSquared, gen') = randomR (0, circleRadius^2) gen | |
r = sqrt rSquared | |
(angle, gen'') = randomR (0, 2 * pi) gen' | |
x = r * cos angle | |
y = r * sin angle | |
in ((x, y), gen'') | |
-- Method 1: Random Endpoints | |
-- Choose two points randomly on the circumference and form a chord. | |
bertrandMethod1 :: RandomGen g => Int -> g -> ([Double], g) | |
bertrandMethod1 numChords gen = runMethod gen numChords $ \g -> do | |
let (p1, g') = randomPointOnCircle g | |
(p2, g'') = randomPointOnCircle g' | |
chordLength = distance p1 p2 | |
return (chordLength, g'') | |
-- Method 2: Random Radius | |
-- Choose a radius, then choose a point randomly on that radius, | |
-- and construct a chord perpendicular to the radius at that point. | |
bertrandMethod2 :: RandomGen g => Int -> g -> ([Double], g) | |
bertrandMethod2 numChords gen = runMethod gen numChords $ \g -> do | |
let (r, g') = randomR (0, circleRadius) gen -- Distance from center along the radius | |
chordLength = 2 * sqrt (circleRadius^2 - r^2) | |
return (chordLength, g') | |
-- Method 3: Random Midpoint | |
-- Choose a point randomly within the circle as the midpoint of the chord. | |
-- The chord is the unique chord with this point as its midpoint. | |
bertrandMethod3 :: RandomGen g => Int -> g -> ([Double], g) | |
bertrandMethod3 numChords gen = runMethod gen numChords $ \g -> do | |
let (midPoint, g') = randomPointInCircleUniform g | |
-- Distance from center to midpoint is simply the distance from origin (0,0) to midpoint | |
r = distance (0,0) midPoint | |
chordLength = 2 * sqrt (circleRadius^2 - r^2) | |
return (chordLength, g') | |
-- Helper function to run a Bertrand method simulation multiple times | |
runMethod :: RandomGen g => g -> Int -> (g -> IO (Double, g)) -> IO ([Double], g) | |
runMethod initialGen numChords method = do | |
(chordLengths, finalGen) <- foldM (\(lengths, gen) _ -> do | |
(length, nextGen) <- method gen | |
return (lengths ++ [length], nextGen) | |
) ([], initialGen) [1..numChords] | |
return (chordLengths, finalGen) | |
-- Calculate the probability of a chord being longer than the triangle side | |
calculateProbability :: [Double] -> Double | |
calculateProbability chordLengths = | |
let longerChords = length $ filter (> triangleSideLength) chordLengths | |
totalChords = length chordLengths | |
in fromIntegral longerChords / fromIntegral totalChords | |
-- Perform the Bertrand Paradox simulation for a given method and number of trials | |
simulateBertrand :: String -> (RandomGen g => Int -> g -> ([Double], g)) -> Int -> IO () | |
simulateBertrand methodName method numTrials = do | |
gen <- getStdGen | |
let (chordLengths, _) = runMethod gen numTrials $ \g -> method 1 g >>= \(lengths, nextGen) -> return (head lengths, nextGen) -- slightly less efficient, but keeps interface consistent with foldM | |
let probability = calculateProbability chordLengths | |
printf "Method: %s\n" methodName | |
printf "Number of trials: %d\n" numTrials | |
printf "Probability (Chord > Triangle Side): %0.4f\n\n" probability | |
main :: IO () | |
main = do | |
let numTrials = 100000 -- Number of chords to generate for each method | |
simulateBertrand "Method 1: Random Endpoints" bertrandMethod1 numTrials | |
simulateBertrand "Method 2: Random Radius" bertrandMethod2 numTrials | |
simulateBertrand "Method 3: Random Midpoint" bertrandMethod3 numTrials |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment