diff --git a/algorithms-modules/algorithms-miscellaneous-8/pom.xml b/algorithms-modules/algorithms-miscellaneous-8/pom.xml index 29163c5de7..086088114e 100644 --- a/algorithms-modules/algorithms-miscellaneous-8/pom.xml +++ b/algorithms-modules/algorithms-miscellaneous-8/pom.xml @@ -13,4 +13,11 @@ 1.0.0-SNAPSHOT - + + + org.apache.commons + commons-math3 + ${commons-math3.version} + + + \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverage.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverage.java new file mode 100644 index 0000000000..dc87c3c194 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverage.java @@ -0,0 +1,24 @@ +package com.baeldung.algorithms.movingaverages; + +public class ExponentialMovingAverage { + + private final double alpha; + private Double previousEMA; + + public ExponentialMovingAverage(double alpha) { + if (alpha <= 0 || alpha > 1) { + throw new IllegalArgumentException("Alpha must be in the range (0, 1]"); + } + this.alpha = alpha; + this.previousEMA = null; + } + + public double calculateEMA(double newValue) { + if (previousEMA == null) { + previousEMA = newValue; + } else { + previousEMA = alpha * newValue + (1 - alpha) * previousEMA; + } + return previousEMA; + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBuffer.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBuffer.java new file mode 100644 index 0000000000..0ff114bfa5 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBuffer.java @@ -0,0 +1,31 @@ +package com.baeldung.algorithms.movingaverages; + +public class MovingAverageByCircularBuffer { + + private final double[] buffer; + private int head; + private int count; + + public MovingAverageByCircularBuffer(int windowSize) { + this.buffer = new double[windowSize]; + } + + public void add(double value) { + buffer[head] = value; + head = (head + 1) % buffer.length; + if (count < buffer.length) { + count++; + } + } + + public double getMovingAverage() { + if (count == 0) { + return Double.NaN; + } + double sum = 0; + for (int i = 0; i < count; i++) { + sum += buffer[i]; + } + return sum / count; + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMath.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMath.java new file mode 100644 index 0000000000..ba8867fa09 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMath.java @@ -0,0 +1,21 @@ +package com.baeldung.algorithms.movingaverages; + +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; + +public class MovingAverageWithApacheCommonsMath { + + private final DescriptiveStatistics stats; + + public MovingAverageWithApacheCommonsMath(int windowSize) { + this.stats = new DescriptiveStatistics(windowSize); + } + + public void add(double value) { + stats.addValue(value); + } + + public double getMovingAverage() { + return stats.getMean(); + } + +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproach.java b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproach.java new file mode 100644 index 0000000000..8dbdf36288 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/main/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproach.java @@ -0,0 +1,18 @@ +package com.baeldung.algorithms.movingaverages; + +import java.util.stream.DoubleStream; + +public class MovingAverageWithStreamBasedApproach { + private int windowSize; + + public MovingAverageWithStreamBasedApproach(int windowSize) { + this.windowSize = windowSize; + } + public double calculateAverage(double[] data) { + return DoubleStream.of(data) + .skip(Math.max(0, data.length - windowSize)) + .limit(Math.min(data.length, windowSize)) + .summaryStatistics() + .getAverage(); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverageUnitTest.java b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverageUnitTest.java new file mode 100644 index 0000000000..dbf78aaae8 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/ExponentialMovingAverageUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.algorithms.movingaverages; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ExponentialMovingAverageUnitTest { + + @Test(expected = IllegalArgumentException.class) + public void whenAlphaIsInvalid_shouldThrowException() { + new ExponentialMovingAverage(0); + } + + @Test + public void whenFirstValueIsAdded_shouldHaveExponentialMovingAverageSameAsValue() { + ExponentialMovingAverage ema = new ExponentialMovingAverage(0.5); + assertEquals(10.0, ema.calculateEMA(10.0), 0.001); + } + + @Test + public void whenValuesAreAdded_shouldUpdateExponentialMovingAverageCorrectly() { + ExponentialMovingAverage ema = new ExponentialMovingAverage(0.4); + assertEquals(10.0, ema.calculateEMA(10.0), 0.001); + assertEquals(14.0, ema.calculateEMA(20.0), 0.001); + assertEquals(20.4, ema.calculateEMA(30.0), 0.001); + } + + @Test + public void whenAlphaIsCloserToOne_exponentialMovingAverageShouldRespondFasterToChanges() { + ExponentialMovingAverage ema1 = new ExponentialMovingAverage(0.2); + ExponentialMovingAverage ema2 = new ExponentialMovingAverage(0.8); + + assertEquals(10.0, ema1.calculateEMA(10.0), 0.001); + assertEquals(10.0, ema2.calculateEMA(10.0), 0.001); + + assertEquals(12.0, ema1.calculateEMA(20.0), 0.001); + assertEquals(18.0, ema2.calculateEMA(20.0), 0.001); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBufferUnitTest.java b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBufferUnitTest.java new file mode 100644 index 0000000000..a9a0ad5509 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageByCircularBufferUnitTest.java @@ -0,0 +1,35 @@ +package com.baeldung.algorithms.movingaverages; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MovingAverageByCircularBufferUnitTest { + + @Test + public void whenInitialAverageIsCalculated_shouldReturnNAN() { + MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(5); + assertEquals(Double.NaN, ma.getMovingAverage(), 0.001); + } + + @Test + public void whenValuesAreAdded_shouldUpdateAverageCorrectly() { + MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(3); + ma.add(10); + assertEquals(10.0, ma.getMovingAverage(), 0.001); + ma.add(20); + assertEquals(15.0, ma.getMovingAverage(), 0.001); + ma.add(30); + assertEquals(20.0, ma.getMovingAverage(), 0.001); + } + + @Test + public void whenWindowSizeIsFull_shouldOverrideTheOldestValue() { + MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(2); + ma.add(10); + ma.add(20); + assertEquals(15.0, ma.getMovingAverage(), 0.001); + ma.add(30); + assertEquals(25.0, ma.getMovingAverage(), 0.001); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMathUnitTest.java b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMathUnitTest.java new file mode 100644 index 0000000000..8d24b95b58 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithApacheCommonsMathUnitTest.java @@ -0,0 +1,36 @@ +package com.baeldung.algorithms.movingaverages; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MovingAverageWithApacheCommonsMathUnitTest { + + @Test + public void whenInitialAverageIsCalculated_shouldReturnNAN() { + MovingAverageWithApacheCommonsMath movingAverageCalculator = new MovingAverageWithApacheCommonsMath(5); + assertEquals(Double.NaN, movingAverageCalculator.getMovingAverage(), 0.001); + } + + @Test + public void whenValuesAreAdded_shouldUpdateAverageCorrectly() { + MovingAverageWithApacheCommonsMath movingAverageCalculator = new MovingAverageWithApacheCommonsMath(3); + movingAverageCalculator.add(10); + assertEquals(10.0, movingAverageCalculator.getMovingAverage(), 0.001); + movingAverageCalculator.add(20); + assertEquals(15.0, movingAverageCalculator.getMovingAverage(), 0.001); + movingAverageCalculator.add(30); + assertEquals(20.0, movingAverageCalculator.getMovingAverage(), 0.001); + } + + @Test + public void whenWindowSizeIsFull_shouldDropTheOldestValue() { + MovingAverageWithApacheCommonsMath ma = new MovingAverageWithApacheCommonsMath(2); + ma.add(10); + ma.add(20); + assertEquals(15.0, ma.getMovingAverage(), 0.001); + ma.add(30); + assertEquals(25.0, ma.getMovingAverage(), 0.001); + } + +} diff --git a/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproachUnitTest.java b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproachUnitTest.java new file mode 100644 index 0000000000..e51f508d6c --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-8/src/test/java/com/baeldung/algorithms/movingaverages/MovingAverageWithStreamBasedApproachUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.algorithms.movingaverages; + +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MovingAverageWithStreamBasedApproachUnitTest { + + @Test + public void whenEmptyDataIsPassed_shouldReturnZero() { + double[] data = {}; + int windowSize = 3; + double expectedAverage = 0; + MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize); + double actualAverage = calculator.calculateAverage(data); + assertEquals(expectedAverage, actualAverage); + } + + @Test + public void whenValidDataIsPassed_shouldReturnCorrectAverage() { + double[] data = {10, 20, 30, 40, 50}; + int windowSize = 3; + double expectedAverage = 40; + MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize); + double actualAverage = calculator.calculateAverage(data); + assertEquals(expectedAverage, actualAverage); + } + + @Test + public void whenValidDataIsPassedWithLongerWindowSize_shouldReturnCorrectAverage() { + double[] data = {10, 20, 30, 40, 50}; + int windowSize = 5; + double expectedAverage = 30; + MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize); + double actualAverage = calculator.calculateAverage(data); + assertEquals(expectedAverage, actualAverage); + } +} \ No newline at end of file