Skip to content

Commit 6fe630c

Browse files
Add Monte Carlo's Integral Approximation (#6235)
1 parent c02074e commit 6fe630c

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.thealgorithms.randomized;
2+
3+
import java.util.Random;
4+
import java.util.function.Function;
5+
6+
/**
7+
* A demonstration of the Monte Carlo integration algorithm in Java.
8+
*
9+
* <p>This class estimates the value of definite integrals using randomized sampling,
10+
* also known as the Monte Carlo method. It is particularly effective for:
11+
* <ul>
12+
* <li>Functions that are difficult or impossible to integrate analytically</li>
13+
* <li>High-dimensional integrals where traditional methods are inefficient</li>
14+
* <li>Simulation and probabilistic analysis tasks</li>
15+
* </ul>
16+
*
17+
* <p>The core idea is to sample random points uniformly from the integration domain,
18+
* evaluate the function at those points, and compute the scaled average to estimate the integral.
19+
*
20+
* <p>For a one-dimensional integral over [a, b], the approximation is the function range (b-a),
21+
* multiplied by the function average result for a random sample.
22+
* See more: <a href="https://en.wikipedia.org/wiki/Monte_Carlo_integration">Monte Carlo Integration</a>
23+
*
24+
* @author: MuhammadEzzatHBK
25+
*/
26+
27+
public final class MonteCarloIntegration {
28+
29+
private MonteCarloIntegration() {
30+
}
31+
32+
/**
33+
* Approximates the definite integral of a given function over a specified
34+
* interval using the Monte Carlo method with a fixed random seed for
35+
* reproducibility.
36+
*
37+
* @param fx the function to integrate
38+
* @param a the lower bound of the interval
39+
* @param b the upper bound of the interval
40+
* @param n the number of random samples to use
41+
* @param seed the seed for the random number generator
42+
* @return the approximate value of the integral
43+
*/
44+
public static double approximate(Function<Double, Double> fx, double a, double b, int n, long seed) {
45+
return doApproximate(fx, a, b, n, new Random(seed));
46+
}
47+
48+
/**
49+
* Approximates the definite integral of a given function over a specified
50+
* interval using the Monte Carlo method with a random seed based on the
51+
* current system time for more randomness.
52+
*
53+
* @param fx the function to integrate
54+
* @param a the lower bound of the interval
55+
* @param b the upper bound of the interval
56+
* @param n the number of random samples to use
57+
* @return the approximate value of the integral
58+
*/
59+
public static double approximate(Function<Double, Double> fx, double a, double b, int n) {
60+
return doApproximate(fx, a, b, n, new Random(System.currentTimeMillis()));
61+
}
62+
63+
private static double doApproximate(Function<Double, Double> fx, double a, double b, int n, Random generator) {
64+
if (!validate(fx, a, b, n)) {
65+
throw new IllegalArgumentException("Invalid input parameters");
66+
}
67+
double totalArea = 0.0;
68+
double interval = b - a;
69+
for (int i = 0; i < n; i++) {
70+
double x = a + generator.nextDouble() * interval;
71+
totalArea += fx.apply(x);
72+
}
73+
return interval * totalArea / n;
74+
}
75+
76+
private static boolean validate(Function<Double, Double> fx, double a, double b, int n) {
77+
boolean isFunctionValid = fx != null;
78+
boolean isIntervalValid = a < b;
79+
boolean isSampleSizeValid = n > 0;
80+
return isFunctionValid && isIntervalValid && isSampleSizeValid;
81+
}
82+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.thealgorithms.randomized;
2+
3+
import static com.thealgorithms.randomized.MonteCarloIntegration.approximate;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
8+
import java.util.function.Function;
9+
import org.junit.jupiter.api.Test;
10+
11+
class MonteCarloIntegrationTest {
12+
13+
private static final double EPSILON = 0.03; // Allow 3% error margin
14+
15+
@Test
16+
void testConstantFunction() {
17+
// Integral of f(x) = 2 from 0 to 1 is 2
18+
Function<Double, Double> constant = x -> 2.0;
19+
double result = approximate(constant, 0, 1, 10000);
20+
assertEquals(2.0, result, EPSILON);
21+
}
22+
23+
@Test
24+
void testLinearFunction() {
25+
// Integral of f(x) = x from 0 to 1 is 0.5
26+
Function<Double, Double> linear = Function.identity();
27+
double result = approximate(linear, 0, 1, 10000);
28+
assertEquals(0.5, result, EPSILON);
29+
}
30+
31+
@Test
32+
void testQuadraticFunction() {
33+
// Integral of f(x) = x^2 from 0 to 1 is 1/3
34+
Function<Double, Double> quadratic = x -> x * x;
35+
double result = approximate(quadratic, 0, 1, 10000);
36+
assertEquals(1.0 / 3.0, result, EPSILON);
37+
}
38+
39+
@Test
40+
void testLargeSampleSize() {
41+
// Integral of f(x) = x^2 from 0 to 1 is 1/3
42+
Function<Double, Double> quadratic = x -> x * x;
43+
double result = approximate(quadratic, 0, 1, 50000000);
44+
assertEquals(1.0 / 3.0, result, EPSILON / 2); // Larger sample size, smaller error margin
45+
}
46+
47+
@Test
48+
void testReproducibility() {
49+
Function<Double, Double> linear = Function.identity();
50+
double result1 = approximate(linear, 0, 1, 10000, 42L);
51+
double result2 = approximate(linear, 0, 1, 10000, 42L);
52+
assertEquals(result1, result2, 0.0); // Exactly equal
53+
}
54+
55+
@Test
56+
void testNegativeInterval() {
57+
// Integral of f(x) = x from -1 to 1 is 0
58+
Function<Double, Double> linear = Function.identity();
59+
double result = approximate(linear, -1, 1, 10000);
60+
assertEquals(0.0, result, EPSILON);
61+
}
62+
63+
@Test
64+
void testNullFunction() {
65+
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(null, 0, 1, 1000));
66+
assertNotNull(exception);
67+
}
68+
69+
@Test
70+
void testInvalidInterval() {
71+
Function<Double, Double> linear = Function.identity();
72+
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
73+
approximate(linear, 2, 1, 1000); // b <= a
74+
});
75+
assertNotNull(exception);
76+
}
77+
78+
@Test
79+
void testZeroSampleSize() {
80+
Function<Double, Double> linear = Function.identity();
81+
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(linear, 0, 1, 0));
82+
assertNotNull(exception);
83+
}
84+
85+
@Test
86+
void testNegativeSampleSize() {
87+
Function<Double, Double> linear = Function.identity();
88+
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(linear, 0, 1, -100));
89+
assertNotNull(exception);
90+
}
91+
}

0 commit comments

Comments
 (0)