mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-07 01:35:16 +08:00
Add Monte Carlo's Integral Approximation (#6235)
This commit is contained in:
@ -0,0 +1,82 @@
|
|||||||
|
package com.thealgorithms.randomized;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A demonstration of the Monte Carlo integration algorithm in Java.
|
||||||
|
*
|
||||||
|
* <p>This class estimates the value of definite integrals using randomized sampling,
|
||||||
|
* also known as the Monte Carlo method. It is particularly effective for:
|
||||||
|
* <ul>
|
||||||
|
* <li>Functions that are difficult or impossible to integrate analytically</li>
|
||||||
|
* <li>High-dimensional integrals where traditional methods are inefficient</li>
|
||||||
|
* <li>Simulation and probabilistic analysis tasks</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The core idea is to sample random points uniformly from the integration domain,
|
||||||
|
* evaluate the function at those points, and compute the scaled average to estimate the integral.
|
||||||
|
*
|
||||||
|
* <p>For a one-dimensional integral over [a, b], the approximation is the function range (b-a),
|
||||||
|
* multiplied by the function average result for a random sample.
|
||||||
|
* See more: <a href="https://en.wikipedia.org/wiki/Monte_Carlo_integration">Monte Carlo Integration</a>
|
||||||
|
*
|
||||||
|
* @author: MuhammadEzzatHBK
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class MonteCarloIntegration {
|
||||||
|
|
||||||
|
private MonteCarloIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximates the definite integral of a given function over a specified
|
||||||
|
* interval using the Monte Carlo method with a fixed random seed for
|
||||||
|
* reproducibility.
|
||||||
|
*
|
||||||
|
* @param fx the function to integrate
|
||||||
|
* @param a the lower bound of the interval
|
||||||
|
* @param b the upper bound of the interval
|
||||||
|
* @param n the number of random samples to use
|
||||||
|
* @param seed the seed for the random number generator
|
||||||
|
* @return the approximate value of the integral
|
||||||
|
*/
|
||||||
|
public static double approximate(Function<Double, Double> fx, double a, double b, int n, long seed) {
|
||||||
|
return doApproximate(fx, a, b, n, new Random(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximates the definite integral of a given function over a specified
|
||||||
|
* interval using the Monte Carlo method with a random seed based on the
|
||||||
|
* current system time for more randomness.
|
||||||
|
*
|
||||||
|
* @param fx the function to integrate
|
||||||
|
* @param a the lower bound of the interval
|
||||||
|
* @param b the upper bound of the interval
|
||||||
|
* @param n the number of random samples to use
|
||||||
|
* @return the approximate value of the integral
|
||||||
|
*/
|
||||||
|
public static double approximate(Function<Double, Double> fx, double a, double b, int n) {
|
||||||
|
return doApproximate(fx, a, b, n, new Random(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double doApproximate(Function<Double, Double> fx, double a, double b, int n, Random generator) {
|
||||||
|
if (!validate(fx, a, b, n)) {
|
||||||
|
throw new IllegalArgumentException("Invalid input parameters");
|
||||||
|
}
|
||||||
|
double totalArea = 0.0;
|
||||||
|
double interval = b - a;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
double x = a + generator.nextDouble() * interval;
|
||||||
|
totalArea += fx.apply(x);
|
||||||
|
}
|
||||||
|
return interval * totalArea / n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validate(Function<Double, Double> fx, double a, double b, int n) {
|
||||||
|
boolean isFunctionValid = fx != null;
|
||||||
|
boolean isIntervalValid = a < b;
|
||||||
|
boolean isSampleSizeValid = n > 0;
|
||||||
|
return isFunctionValid && isIntervalValid && isSampleSizeValid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.thealgorithms.randomized;
|
||||||
|
|
||||||
|
import static com.thealgorithms.randomized.MonteCarloIntegration.approximate;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class MonteCarloIntegrationTest {
|
||||||
|
|
||||||
|
private static final double EPSILON = 0.03; // Allow 3% error margin
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testConstantFunction() {
|
||||||
|
// Integral of f(x) = 2 from 0 to 1 is 2
|
||||||
|
Function<Double, Double> constant = x -> 2.0;
|
||||||
|
double result = approximate(constant, 0, 1, 10000);
|
||||||
|
assertEquals(2.0, result, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLinearFunction() {
|
||||||
|
// Integral of f(x) = x from 0 to 1 is 0.5
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
double result = approximate(linear, 0, 1, 10000);
|
||||||
|
assertEquals(0.5, result, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQuadraticFunction() {
|
||||||
|
// Integral of f(x) = x^2 from 0 to 1 is 1/3
|
||||||
|
Function<Double, Double> quadratic = x -> x * x;
|
||||||
|
double result = approximate(quadratic, 0, 1, 10000);
|
||||||
|
assertEquals(1.0 / 3.0, result, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLargeSampleSize() {
|
||||||
|
// Integral of f(x) = x^2 from 0 to 1 is 1/3
|
||||||
|
Function<Double, Double> quadratic = x -> x * x;
|
||||||
|
double result = approximate(quadratic, 0, 1, 50000000);
|
||||||
|
assertEquals(1.0 / 3.0, result, EPSILON / 2); // Larger sample size, smaller error margin
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReproducibility() {
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
double result1 = approximate(linear, 0, 1, 10000, 42L);
|
||||||
|
double result2 = approximate(linear, 0, 1, 10000, 42L);
|
||||||
|
assertEquals(result1, result2, 0.0); // Exactly equal
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNegativeInterval() {
|
||||||
|
// Integral of f(x) = x from -1 to 1 is 0
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
double result = approximate(linear, -1, 1, 10000);
|
||||||
|
assertEquals(0.0, result, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullFunction() {
|
||||||
|
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(null, 0, 1, 1000));
|
||||||
|
assertNotNull(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInvalidInterval() {
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
approximate(linear, 2, 1, 1000); // b <= a
|
||||||
|
});
|
||||||
|
assertNotNull(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testZeroSampleSize() {
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(linear, 0, 1, 0));
|
||||||
|
assertNotNull(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNegativeSampleSize() {
|
||||||
|
Function<Double, Double> linear = Function.identity();
|
||||||
|
Exception exception = assertThrows(IllegalArgumentException.class, () -> approximate(linear, 0, 1, -100));
|
||||||
|
assertNotNull(exception);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user