Monte Carlo Algorithms - CSU083 | Shoolini University

Monte Carlo

1. Prerequisites

Monte Carlo methods rely on probability and random sampling to solve problems. To understand them, you need a strong grasp of the following:

2. What is Monte Carlo?

Monte Carlo is a class of computational algorithms that rely on repeated random sampling to estimate numerical results. The core idea is:

3. Why does this algorithm exist?

Monte Carlo methods exist because many problems are too complex to solve analytically. They provide approximate solutions where exact solutions are impractical. Some applications include:

4. When should you use it?

Monte Carlo methods are useful when:

5. How does it compare to alternatives?

5.1 Strengths

5.2 Weaknesses

6. Basic Implementation

A simple Monte Carlo implementation to estimate the value of π (pi) using the Monte Carlo method:

import random

def monte_carlo_pi(num_samples):
    inside_circle = 0

    for _ in range(num_samples):
        x, y = random.uniform(0, 1), random.uniform(0, 1)  # Generate random (x, y) in [0,1] x [0,1]
        if x**2 + y**2 <= 1:  # Check if the point lies inside the quarter circle
            inside_circle += 1

    return (inside_circle / num_samples) * 4  # Multiply by 4 to estimate full circle

# Example usage
samples = 10000
estimated_pi = monte_carlo_pi(samples)
print(f"Estimated π with {samples} samples: {estimated_pi}")

7. Dry Run the Algorithm

7.1 Initial Conditions

7.2 Step-by-Step Execution

Iteration Random x Random y x² + y² ≤ 1? inside_circle
1 0.2 0.3 Yes 1
2 0.8 0.7 No 1
3 0.5 0.5 Yes 2
4 0.9 0.4 No 2
5 0.3 0.2 Yes 3

7.3 Final Calculation

7.4 Observations

8. Time & Space Complexity Analysis

8.1 Time Complexity Analysis

9. Space Complexity Analysis

10. Understanding the Trade-offs

10.1 Strengths

10.2 Weaknesses

10.3 When to Use vs. Avoid

11. Optimizations & Variants

11.1 Common Optimizations

11.2 Different Variants of Monte Carlo

12. Iterative vs. Recursive Implementations

12.1 Iterative Implementation

import random

def monte_carlo_pi_iterative(n):
    inside_circle = 0

    for _ in range(n):
        x, y = random.uniform(0, 1), random.uniform(0, 1)
        if x**2 + y**2 <= 1:
            inside_circle += 1

    return (inside_circle / n) * 4

# Example usage
print(monte_carlo_pi_iterative(10000))

12.2 Recursive Implementation

import random

def monte_carlo_pi_recursive(n, inside_circle=0):
    if n == 0:
        return (inside_circle / 10000) * 4  # Final estimation

    x, y = random.uniform(0, 1), random.uniform(0, 1)
    if x**2 + y**2 <= 1:
        inside_circle += 1

    return monte_carlo_pi_recursive(n - 1, inside_circle)

# Example usage
print(monte_carlo_pi_recursive(10000))

12.3 Comparison

Approach Time Complexity Space Complexity Efficiency
Iterative O(n) O(1) More efficient due to constant memory usage
Recursive O(n) O(n) Less efficient due to function call overhead

12.4 Conclusion

13. Edge Cases & Failure Handling

13.1 Common Pitfalls & Edge Cases

14. Test Cases to Verify Correctness

14.1 Basic Test Cases

import unittest
import random

def monte_carlo_pi(n):
    if n <= 0:
        return 0  # Handle invalid input gracefully
    inside_circle = sum(1 for _ in range(n) if (random.uniform(0, 1)**2 + random.uniform(0, 1)**2) <= 1)
    return (inside_circle / n) * 4

class TestMonteCarlo(unittest.TestCase):
    
    def test_small_sample(self):
        self.assertGreater(monte_carlo_pi(10), 0)  # Small but non-zero value
    
    def test_large_sample(self):
        pi_estimate = monte_carlo_pi(100000)
        self.assertAlmostEqual(pi_estimate, 3.14, delta=0.1)  # Should be close to π
    
    def test_zero_samples(self):
        self.assertEqual(monte_carlo_pi(0), 0)  # Handle zero input
    
    def test_negative_samples(self):
        self.assertEqual(monte_carlo_pi(-5), 0)  # Handle negative input

if __name__ == "__main__":
    unittest.main()

15. Real-World Failure Scenarios

15.1 Practical Failures in Different Domains

15.2 How to Mitigate Failures

16. Real-World Applications & Industry Use Cases

16.1 Industries Utilizing Monte Carlo Methods

16.2 Example: Monte Carlo in Finance

Monte Carlo simulations help predict financial market behavior by running thousands of simulations with different possible inputs to estimate future trends.

17. Open-Source Implementations

17.1 Libraries & Tools

17.2 Example: Monte Carlo in SciPy

import numpy as np
from scipy.stats import norm

# Monte Carlo for European Call Option Pricing
def monte_carlo_option_price(S, K, T, r, sigma, num_simulations=100000):
    np.random.seed(42)
    Z = np.random.standard_normal(num_simulations)
    ST = S * np.exp((r - 0.5 * sigma ** 2) * T + sigma * np.sqrt(T) * Z)
    payoff = np.maximum(ST - K, 0)
    return np.exp(-r * T) * np.mean(payoff)

# Example: Stock Price = $100, Strike Price = $105, Time = 1 year, Interest Rate = 5%, Volatility = 20%
price = monte_carlo_option_price(100, 105, 1, 0.05, 0.2)
print(f"Estimated Option Price: ${price:.2f}")

18. Practical Project: Monte Carlo-Based Risk Analysis

18.1 Project Idea: Simulating Retirement Savings Growth

This script simulates different market conditions to estimate the probability of achieving a retirement goal.

import numpy as np
import matplotlib.pyplot as plt

# Simulating the growth of a retirement fund
def simulate_retirement_growth(starting_amount, years, mean_return=0.07, std_dev=0.15, simulations=10000):
    np.random.seed(42)
    results = []

    for _ in range(simulations):
        balance = starting_amount
        for _ in range(years):
            annual_return = np.random.normal(mean_return, std_dev)
            balance *= (1 + annual_return)
        results.append(balance)

    return results

# Running simulation
initial_savings = 100000
years_to_retirement = 30
simulated_balances = simulate_retirement_growth(initial_savings, years_to_retirement)

# Visualization
plt.hist(simulated_balances, bins=50, alpha=0.75, color="blue")
plt.axvline(np.percentile(simulated_balances, 10), color='red', linestyle='dashed', linewidth=2, label="10th Percentile")
plt.axvline(np.percentile(simulated_balances, 90), color='green', linestyle='dashed', linewidth=2, label="90th Percentile")
plt.xlabel("Final Retirement Savings ($)")
plt.ylabel("Frequency")
plt.title("Monte Carlo Simulation of Retirement Fund Growth")
plt.legend()
plt.show()

18.2 Key Takeaways

19. Competitive Programming & System Design Integration

19.1 Monte Carlo in Competitive Programming

Monte Carlo algorithms are useful in competitive programming when an exact solution is infeasible due to complexity constraints.

Common Problems:

19.2 Monte Carlo in System Design

Monte Carlo methods are used in system design to handle uncertainty and improve scalability.

Use Cases:

20. Assignments

20.1 Solve at least 10 problems using Monte Carlo

Practice solving the following problems:

  1. Estimate π using Monte Carlo.
  2. Monte Carlo method for definite integration.
  3. Estimating probability of rolling a sum of 7 using two dice.
  4. Simulate the Monty Hall problem and estimate winning probability.
  5. Estimate the area of an irregular shape using Monte Carlo.
  6. Monte Carlo method for estimating the square root of a number.
  7. Estimating the expected number of coin flips to get heads.
  8. Monte Carlo method to solve the Traveling Salesman Problem (TSP).
  9. Monte Carlo Tree Search (MCTS) for game AI.
  10. Use Monte Carlo for numerical root finding (e.g., Newton-Raphson with random restarts).

20.2 System Design Assignment

Design a system where Monte Carlo methods optimize decision-making:

20.3 Timed Implementation Practice

Practice implementing Monte Carlo algorithms under time constraints: