From 0dab9726f8c4199e5f25de61ba5ad9aee62363b1 Mon Sep 17 00:00:00 2001 From: lionarius Date: Thu, 5 Sep 2024 10:31:21 +0300 Subject: [PATCH] lab2 --- build.gradle | 2 +- .../java/ru/lionarius/IntegralCalculator.java | 75 +++++++++++++++++++ src/main/java/ru/lionarius/Main.java | 19 ++++- src/test/java/IntegralCalculatorTests.java | 53 +++++++++++++ 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/lionarius/IntegralCalculator.java create mode 100644 src/test/java/IntegralCalculatorTests.java diff --git a/build.gradle b/build.gradle index 6232e94..508e940 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'ru.lionarius' -version = '1-LAB' +version = '2-LAB' repositories { mavenCentral() diff --git a/src/main/java/ru/lionarius/IntegralCalculator.java b/src/main/java/ru/lionarius/IntegralCalculator.java new file mode 100644 index 0000000..ae1bc66 --- /dev/null +++ b/src/main/java/ru/lionarius/IntegralCalculator.java @@ -0,0 +1,75 @@ +package ru.lionarius; + +import java.util.function.Function; + +/** + * A class that provides a numerical method to calculate definite integrals of a given function. + * The integration is performed using the trapezoidal rule, with a specified accuracy. + */ +public class IntegralCalculator { + + /** + * The accuracy level for the integration process. + * A smaller value results in higher accuracy but more computation time. + */ + private final double accuracy; + + /** + * Constructs an {@link IntegralCalculator} with the specified accuracy. + * + * @param accuracy The desired accuracy for the integral calculation + * @throws IllegalArgumentException if the accuracy is less than or equal to zero. + */ + public IntegralCalculator(double accuracy) { + if (accuracy <= 0.0) + throw new IllegalArgumentException("accuracy must be a positive number"); + + this.accuracy = accuracy; + } + + /** + * Returns the accuracy level used for the integration calculation. + * + * @return The accuracy level as a double. + */ + public double getAccuracy() { + return this.accuracy; + } + + /** + * Calculates the definite integral of the specified function over a given interval + * using the trapezoidal rule. + * + * @param function The function to integrate. + * @param lowerBound The lower bound of the integration interval. + * @param upperBound The upper bound of the integration interval. + * @return The estimated value of the definite integral. + */ + public double calculate(Function function, double lowerBound, double upperBound) { + if (lowerBound == upperBound) + return 0.0; + + var invert = false; + if (lowerBound > upperBound) { + invert = true; + + var temp = lowerBound; + lowerBound = upperBound; + upperBound = temp; + } + + var n = (long) Math.ceil((upperBound - lowerBound) / this.accuracy); + var h = (upperBound - lowerBound) / n; + + var sum = (function.apply(lowerBound) + function.apply(upperBound)) / 2; + for (var i = 1L; i < n; i++) + sum += function.apply(lowerBound + h * i); + + sum *= h; + + if (invert) + sum = -sum; + + return sum; + } +} diff --git a/src/main/java/ru/lionarius/Main.java b/src/main/java/ru/lionarius/Main.java index 8023c11..a9563d6 100644 --- a/src/main/java/ru/lionarius/Main.java +++ b/src/main/java/ru/lionarius/Main.java @@ -1,7 +1,24 @@ package ru.lionarius; +import java.util.function.Function; + public class Main { public static void main(String[] args) { - System.out.println("Hello world!"); + Function function = x -> Math.sin(x) * x; + var lowerBound = 0.0; + var upperBound = 1.0; + double[] accuracies = {0.001, 0.00001, 0.0000001, 0.000000001}; + + for (var accuracy : accuracies) { + var calculator = new IntegralCalculator(accuracy); + + var startTime = System.nanoTime(); + + var value = calculator.calculate(function, lowerBound, upperBound); + + var totalTime = System.nanoTime() - startTime; + + System.out.printf("Calculated value: %.15f | Accuracy: %e | Time: %.9fms%n", value, accuracy, totalTime / 1_000_000.0); + } } } diff --git a/src/test/java/IntegralCalculatorTests.java b/src/test/java/IntegralCalculatorTests.java new file mode 100644 index 0000000..16f268a --- /dev/null +++ b/src/test/java/IntegralCalculatorTests.java @@ -0,0 +1,53 @@ +import org.junit.jupiter.api.Test; +import ru.lionarius.IntegralCalculator; + +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.*; + +class IntegralCalculatorTests { + + private static void assertIntegral(final double expected, final IntegralCalculator calculator, final Function function, final double lowerBound, final double upperBound) { + var value = calculator.calculate(function, lowerBound, upperBound); + + assertEquals(value, expected, calculator.getAccuracy()); + } + + @Test + void accuracyValidRange() { + assertThrowsExactly(IllegalArgumentException.class, () -> new IntegralCalculator(0.0)); + assertThrowsExactly(IllegalArgumentException.class, () -> new IntegralCalculator(-0.0)); + assertThrowsExactly(IllegalArgumentException.class, () -> new IntegralCalculator(-1.0)); + + assertDoesNotThrow(() -> new IntegralCalculator(Double.MIN_VALUE)); + } + + @Test + void accuracy_SinXX() { + final var actualValue = 0.30116867893975678925156571418732239589025264018044883800265445461081000961676790443; + + final Function function = x -> Math.sin(x) * x; + final var lowerBound = 0.0; + final var upperBound = 1.0; + final double[] accuracies = {0.001, 0.00001, 0.0000001}; + + for (var accuracy : accuracies) { + var calculator = new IntegralCalculator(accuracy); + + assertIntegral(actualValue, calculator, function, lowerBound, upperBound); + } + } + + @Test + void bounds_X() { + final var calculator = new IntegralCalculator(0.1); + + final Function function = x -> x; + final var lowerBound = 0.0; + final var upperBound = 1.0; + + assertIntegral(0.5, calculator, function, lowerBound, upperBound); + assertIntegral(-0.5, calculator, function, upperBound, lowerBound); + assertIntegral(0.0, calculator, function, lowerBound, lowerBound); + } +}