Quick Info

Category: Randomized
Time Complexity: O(n)
Space Complexity: O(1)
Accuracy: ~1/√n

Monte Carlo Pi Estimation

Description

Monte Carlo Pi Estimation is a randomized algorithm that approximates the value of π using random sampling. The method uses the relationship between the area of a circle and its circumscribing square to estimate π through random point generation and probability.

How It Works

  1. Consider a quarter circle of radius r inside a square of side r
  2. Generate random points within the square
  3. Count points that fall inside the quarter circle
  4. Ratio of points inside circle to total points ≈ π/4
  5. Multiply ratio by 4 to estimate π

The accuracy of the estimation improves with the number of points generated, following a rate of approximately 1/√n where n is the number of points.

Visualization

Points Inside: 0
Total Points: 0
π Estimate: 0
Error: 0%

Enter number of points and click Start

Complexity Analysis

Time Complexity

  • Point Generation: O(n)
    • Each point requires constant time to generate
    • n is the number of points requested
  • Point Checking: O(n)
    • Each point requires constant time to check
    • Uses Pythagorean theorem for distance

Space Complexity

  • Variables: O(1)
    • Only counters and current point stored
    • No additional data structures needed

Error Analysis

  • Expected Error: O(1/√n)
    • Error decreases with square root of points
    • Doubling accuracy requires 4x points

Advantages and Disadvantages

Advantages

  • Simple to understand and implement
  • Naturally parallelizable
  • Can be stopped at any time
  • Demonstrates probability concepts

Disadvantages

  • Slow convergence rate
  • Results vary between runs
  • Not suitable for high precision
  • Requires good random number generator

Implementation


import random
import math
from typing import Tuple, List
import matplotlib.pyplot as plt

class MonteCarloPi:
    def __init__(self, seed: int = None):
        """Initialize the Monte Carlo Pi estimator."""
        if seed is not None:
            random.seed(seed)
        
    def estimate_pi(self, num_points: int) -> Tuple[float, List[float]]:
        """
        Estimate π using Monte Carlo method.
        
        Args:
            num_points: Number of random points to generate
            
        Returns:
            Tuple containing:
            - Final π estimate
            - List of intermediate π estimates
        """
        points_inside = 0
        estimates = []
        
        for i in range(1, num_points + 1):
            # Generate random point in [0, 1] x [0, 1]
            x = random.random()
            y = random.random()
            
            # Check if point lies inside quarter circle
            if math.sqrt(x*x + y*y) <= 1:
                points_inside += 1
            
            # Calculate current estimate
            pi_estimate = 4 * points_inside / i
            estimates.append(pi_estimate)
        
        return estimates[-1], estimates
    
    def visualize_convergence(self, estimates: List[float]) -> None:
        """Plot convergence of π estimates."""
        plt.figure(figsize=(10, 6))
        plt.plot(estimates, label='π estimate')
        plt.axhline(y=math.pi, color='r', linestyle='--', label='Actual π')
        plt.xlabel('Number of Points')
        plt.ylabel('Estimated π')
        plt.title('Convergence of Monte Carlo π Estimation')
        plt.legend()
        plt.grid(True)
        plt.show()

# Example usage
if __name__ == "__main__":
    # Create estimator with fixed seed for reproducibility
    mc = MonteCarloPi(seed=42)
    
    # Run estimation with different numbers of points
    for points in [1000, 10000, 100000]:
        pi_estimate, estimates = mc.estimate_pi(points)
        error = abs(pi_estimate - math.pi) / math.pi * 100
        
        print(f"\nNumber of points: {points}")
        print(f"π estimate: {pi_estimate:.6f}")
        print(f"Actual π: {math.pi:.6f}")
        print(f"Error: {error:.2f}%")
        
        # Visualize convergence
        mc.visualize_convergence(estimates)

#include <iostream>
#include <random>
#include <chrono>
#include <vector>
#include <cmath>
#include <iomanip>

class MonteCarloPi {
private:
    std::mt19937 gen;
    std::uniform_real_distribution<double> dis;

public:
    MonteCarloPi(int seed = -1) : dis(0.0, 1.0) {
        // Use provided seed or generate from time
        if (seed == -1) {
            seed = std::chrono::system_clock::now()
                .time_since_epoch()
                .count();
        }
        gen.seed(seed);
    }
    
    std::pair<double, std::vector<double>> estimate_pi(int num_points) {
        int points_inside = 0;
        std::vector<double> estimates;
        estimates.reserve(num_points);
        
        for (int i = 1; i <= num_points; ++i) {
            // Generate random point
            double x = dis(gen);
            double y = dis(gen);
            
            // Check if point is inside quarter circle
            if (std::sqrt(x*x + y*y) <= 1.0) {
                points_inside++;
            }
            
            // Calculate current estimate
            double pi_estimate = 4.0 * points_inside / i;
            estimates.push_back(pi_estimate);
        }
        
        return {estimates.back(), estimates};
    }
    
    void print_results(int num_points, double pi_estimate) {
        const double PI = 3.14159265358979323846;
        double error = std::abs(pi_estimate - PI) / PI * 100;
        
        std::cout << std::fixed << std::setprecision(6);
        std::cout << "\nNumber of points: " << num_points << std::endl;
        std::cout << "π estimate: " << pi_estimate << std::endl;
        std::cout << "Actual π: " << PI << std::endl;
        std::cout << "Error: " << error << "%" << std::endl;
    }
};

int main() {
    // Create estimator with fixed seed for reproducibility
    MonteCarloPi mc(42);
    
    // Run estimation with different numbers of points
    for (int points : {1000, 10000, 100000}) {
        auto [pi_estimate, estimates] = mc.estimate_pi(points);
        mc.print_results(points, pi_estimate);
    }
    
    return 0;
}

using System;
using System.Collections.Generic;
using System.Linq;

public class MonteCarloPi
{
    private readonly Random rng;
    
    public MonteCarloPi(int? seed = null)
    {
        rng = seed.HasValue ? new Random(seed.Value) : new Random();
    }
    
    public (double estimate, List<double> estimates) EstimatePi(int numPoints)
    {
        int pointsInside = 0;
        var estimates = new List<double>(numPoints);
        
        for (int i = 1; i <= numPoints; i++)
        {
            // Generate random point
            double x = rng.NextDouble();
            double y = rng.NextDouble();
            
            // Check if point is inside quarter circle
            if (Math.Sqrt(x*x + y*y) <= 1.0)
            {
                pointsInside++;
            }
            
            // Calculate current estimate
            double piEstimate = 4.0 * pointsInside / i;
            estimates.Add(piEstimate);
        }
        
        return (estimates.Last(), estimates);
    }
    
    public void PrintResults(int numPoints, double piEstimate)
    {
        const double PI = Math.PI;
        double error = Math.Abs(piEstimate - PI) / PI * 100;
        
        Console.WriteLine($"\nNumber of points: {numPoints}");
        Console.WriteLine($"π estimate: {piEstimate:F6}");
        Console.WriteLine($"Actual π: {PI:F6}");
        Console.WriteLine($"Error: {error:F2}%");
    }
    
    public static void Main()
    {
        // Create estimator with fixed seed for reproducibility
        var mc = new MonteCarloPi(seed: 42);
        
        // Run estimation with different numbers of points
        foreach (int points in new[] {1000, 10000, 100000})
        {
            var (piEstimate, estimates) = mc.EstimatePi(points);
            mc.PrintResults(points, piEstimate);
            
            // Calculate statistics
            double mean = estimates.Average();
            double stdDev = Math.Sqrt(
                estimates.Sum(x => Math.Pow(x - mean, 2)) / estimates.Count
            );
            
            Console.WriteLine($"Standard Deviation: {stdDev:F6}");
        }
    }
}