not final but im lazy
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
package ru.lionarius;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -30,7 +36,7 @@ public class IntegralCalculator {
|
||||
* @param progressCallback A callback function to track progress. Can be null.
|
||||
* @throws IllegalArgumentException if the accuracy is less than or equal to zero.
|
||||
*/
|
||||
public IntegralCalculator(double accuracy, BiConsumer<Long, Long> progressCallback) {
|
||||
public IntegralCalculator(double accuracy, final BiConsumer<Long, Long> progressCallback) {
|
||||
if (accuracy <= 0.0)
|
||||
throw new IllegalArgumentException("accuracy must be a positive number");
|
||||
|
||||
@@ -61,17 +67,23 @@ public class IntegralCalculator {
|
||||
* 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.
|
||||
* @param function The function to integrate.
|
||||
* @param lower The lower bound of the integration interval.
|
||||
* @param upper The upper bound of the integration interval.
|
||||
* @return The estimated value of the definite integral.
|
||||
* @throws NullPointerException if the function is null.
|
||||
*/
|
||||
public double calculate(Function<Double, Double> function, double lowerBound, double upperBound) {
|
||||
public double calculate(final Function<Double, Double> function, double lower, double upper, ExecutorService executor, int parallelism) throws ExecutionException, InterruptedException {
|
||||
if (function == null)
|
||||
throw new NullPointerException("function cannot be null");
|
||||
|
||||
if (lowerBound == upperBound) {
|
||||
if (executor == null)
|
||||
throw new NullPointerException("executor cannot be null");
|
||||
|
||||
if (parallelism <= 0)
|
||||
throw new IllegalArgumentException("parallelism must be a positive number");
|
||||
|
||||
if (lower == upper) {
|
||||
this.callProgressCallback(0, 1);
|
||||
this.callProgressCallback(1, 1);
|
||||
|
||||
@@ -79,24 +91,54 @@ public class IntegralCalculator {
|
||||
}
|
||||
|
||||
var invert = false;
|
||||
if (lowerBound > upperBound) {
|
||||
if (lower > upper) {
|
||||
invert = true;
|
||||
|
||||
var temp = lowerBound;
|
||||
lowerBound = upperBound;
|
||||
upperBound = temp;
|
||||
var temp = lower;
|
||||
lower = upper;
|
||||
upper = temp;
|
||||
}
|
||||
|
||||
final var lowerBound = lower;
|
||||
final var upperBound = upper;
|
||||
|
||||
var n = (long) Math.ceil((upperBound - lowerBound) / this.accuracy);
|
||||
var h = (upperBound - lowerBound) / n;
|
||||
|
||||
this.callProgressCallback(0, n);
|
||||
|
||||
var sum = (function.apply(lowerBound) + function.apply(upperBound)) / 2;
|
||||
for (var i = 1L; i < n; i++) {
|
||||
sum += function.apply(lowerBound + h * i);
|
||||
var remainder = n % parallelism;
|
||||
var partitionSize = n / parallelism;
|
||||
var currentStart = 0L;
|
||||
|
||||
this.callProgressCallback(i, n);
|
||||
var sum = (function.apply(lowerBound) + function.apply(upperBound)) / 2;
|
||||
final var futures = new ArrayList<Future<Double>>();
|
||||
final var total = new AtomicLong(0);
|
||||
for (var i = 0; i < parallelism; i++) {
|
||||
final var start = currentStart;
|
||||
final var end = currentStart + partitionSize + (remainder > 0 ? 1 : 0);
|
||||
if (remainder > 0)
|
||||
remainder--;
|
||||
currentStart = end;
|
||||
|
||||
futures.add(executor.submit(() -> {
|
||||
var prevSteps = 0;
|
||||
var steps = 0;
|
||||
var partitionSum = 0.0;
|
||||
for (var j = start; j < end; j++) {
|
||||
partitionSum += function.apply(lowerBound + h * j);
|
||||
steps++;
|
||||
|
||||
var progress = total.incrementAndGet();
|
||||
progressCallback.accept(progress, n);
|
||||
}
|
||||
|
||||
return partitionSum;
|
||||
}));
|
||||
}
|
||||
|
||||
for (var future : futures) {
|
||||
sum += future.get();
|
||||
}
|
||||
|
||||
sum *= h;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.lionarius;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Main {
|
||||
@@ -8,47 +10,42 @@ public class Main {
|
||||
var lowerBound = 0.0;
|
||||
var upperBound = 1.0;
|
||||
double[] accuracies = {0.001, 0.00001, 0.0000001, 0.000000001, 0.000000001};
|
||||
var threads = new Thread[accuracies.length];
|
||||
|
||||
var executor = Executors.newCachedThreadPool();
|
||||
for (final var accuracy : accuracies) {
|
||||
var calculator = new IntegralCalculator(
|
||||
accuracy,
|
||||
(current, total) -> {
|
||||
if (current % (total / 15) != 0)
|
||||
return;
|
||||
System.out.printf(
|
||||
"[%s] Progress: %.2f%%%n",
|
||||
Thread.currentThread().getName(),
|
||||
(current * 100.0 / total)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
for (var i = 0; i < accuracies.length; i++) {
|
||||
final var accuracy = accuracies[i];
|
||||
var thread = new Thread(() -> {
|
||||
var calculator = new IntegralCalculator(
|
||||
accuracy,
|
||||
(current, total) -> {
|
||||
if (current % (total / 15) != 0)
|
||||
return;
|
||||
System.out.printf(
|
||||
"[%s] Progress: %.2f%%%n",
|
||||
Thread.currentThread().getName(),
|
||||
(current * 100.0 / total)
|
||||
);
|
||||
}
|
||||
);
|
||||
var startTime = System.nanoTime();
|
||||
|
||||
var startTime = System.nanoTime();
|
||||
double value = 0;
|
||||
try {
|
||||
value = calculator.calculate(function, lowerBound, upperBound, executor, 12);
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
var value = calculator.calculate(function, lowerBound, upperBound);
|
||||
var totalTime = System.nanoTime() - startTime;
|
||||
|
||||
var totalTime = System.nanoTime() - startTime;
|
||||
|
||||
System.out.printf(
|
||||
"[%s] Calculated value: %.15f | Accuracy: %e | Time: %.9fms%n",
|
||||
Thread.currentThread().getName(),
|
||||
value,
|
||||
accuracy,
|
||||
totalTime / 1_000_000.0
|
||||
);
|
||||
});
|
||||
threads[i] = thread;
|
||||
thread.start();
|
||||
System.out.printf(
|
||||
"[%s] Calculated value: %.15f | Accuracy: %e | Time: %.9fms%n",
|
||||
Thread.currentThread().getName(),
|
||||
value,
|
||||
accuracy,
|
||||
totalTime / 1_000_000.0
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
for (var thread : threads)
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.lionarius.AreaCalculator;
|
||||
|
||||
class AreaCalculatorTests {
|
||||
|
||||
private final AreaCalculator areaCalculator = new AreaCalculator();
|
||||
|
||||
@Test
|
||||
void circleArea() {
|
||||
assertEquals(0.0, this.areaCalculator.circle(0.0));
|
||||
assertEquals(Math.PI, this.areaCalculator.circle(1.0));
|
||||
assertEquals(Math.PI * 4.0, this.areaCalculator.circle(2.0));
|
||||
|
||||
assertThrowsExactly(IllegalArgumentException.class, () -> this.areaCalculator.circle(-1.0));
|
||||
|
||||
assertEquals(Double.POSITIVE_INFINITY, this.areaCalculator.circle(Double.MAX_VALUE));
|
||||
assertEquals(Double.NaN, this.areaCalculator.circle(Double.NaN));
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.lionarius.IntegralCalculator;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiConsumer;
|
||||
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<Double, Double> function, final double lowerBound, final double upperBound) {
|
||||
var value = calculator.calculate(function, lowerBound, upperBound);
|
||||
|
||||
assertEquals(expected, value, 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<Double, Double> 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<Double, Double> 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgressCallbackDifferentBounds() {
|
||||
final var currentStep = new AtomicLong();
|
||||
final var totalSteps = new AtomicLong();
|
||||
final var callbackCount = new AtomicLong(0);
|
||||
|
||||
final BiConsumer<Long, Long> progressCallback = (current, total) -> {
|
||||
currentStep.set(current);
|
||||
totalSteps.set(total);
|
||||
callbackCount.incrementAndGet();
|
||||
};
|
||||
|
||||
final var calculator = new IntegralCalculator(0.01, progressCallback);
|
||||
|
||||
calculator.calculate(x -> x, 0, 1);
|
||||
|
||||
assertTrue(callbackCount.get() >= 2, "Callback should be called at least twice");
|
||||
assertEquals(totalSteps.get(), currentStep.get(), "Final progress should be equal to total steps");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgressCallbackSameBounds() {
|
||||
var currentStep = new AtomicLong();
|
||||
var totalSteps = new AtomicLong();
|
||||
var callbackCount = new AtomicLong(0);
|
||||
|
||||
BiConsumer<Long, Long> progressCallback = (current, total) -> {
|
||||
currentStep.set(current);
|
||||
totalSteps.set(total);
|
||||
callbackCount.incrementAndGet();
|
||||
};
|
||||
|
||||
var calculator = new IntegralCalculator(0.01, progressCallback);
|
||||
|
||||
calculator.calculate(x -> x, 0, 0);
|
||||
|
||||
assertTrue(callbackCount.get() >= 2, "Callback should be called at least twice");
|
||||
assertEquals(totalSteps.get(), currentStep.get(), "Final progress should be equal to total steps");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user