From 57863bb53936be52b81cfcc8c28f9fd7fc634503 Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Sat, 29 Mar 2025 19:59:34 -0600 Subject: [PATCH 1/6] Add Reservoir Sampling algorithm to randomized module --- .../randomized/ReservoirSampling.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/main/java/com/thealgorithms/randomized/ReservoirSampling.java diff --git a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java new file mode 100644 index 000000000000..1278d3f9669c --- /dev/null +++ b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java @@ -0,0 +1,59 @@ +package com.thealgorithms.randomized; + +import java.util.Random; +import java.util.ArrayList; +import java.util.List; + +/** + * Reservoir Sampling Algorithm + * + * Use Case: + * - Efficient for selecting k random items from a stream of unknown size + * - Used in streaming systems, big data, and memory-limited environments + * + * Time Complexity: O(n) + * Space Complexity: O(k) + * + * Author: Michael Alexander Montoya (@cureprotocols) + */ +public class ReservoirSampling { + + /** + * Selects k random elements from a stream using reservoir sampling. + * + * @param stream The input stream as an array of integers. + * @param sampleSize The number of elements to sample. + * @return A list containing k randomly selected elements. + */ + public static List sample(int[] stream, int sampleSize) { + List reservoir = new ArrayList<>(sampleSize); + Random rand = new Random(); + + for (int i = 0; i < stream.length; i++) { + if (i < sampleSize) { + reservoir.add(stream[i]); + } else { + int j = rand.nextInt(i + 1); + if (j < sampleSize) { + reservoir.set(j, stream[i]); + } + } + } + + return reservoir; + } + + // Demo usage + public static void main(String[] args) { + int[] streamData = new int[1000]; + for (int i = 0; i < 1000; i++) { + streamData[i] = i + 1; + } + + List result = ReservoirSampling.sample(streamData, 10); + System.out.println("Random sample of 10 items:"); + for (int value : result) { + System.out.print(value + " "); + } + } +} From 3ea012b897cce844df30545b3423302c2ffa3bea Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Mon, 31 Mar 2025 11:03:26 -0600 Subject: [PATCH 2/6] Fix: add validation for sampleSize exceeding stream length --- .../java/com/thealgorithms/randomized/ReservoirSampling.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java index 1278d3f9669c..5256c4869262 100644 --- a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java +++ b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java @@ -26,6 +26,10 @@ public class ReservoirSampling { * @return A list containing k randomly selected elements. */ public static List sample(int[] stream, int sampleSize) { + if (sampleSize > stream.length) { + throw new IllegalArgumentException("Sample size cannot exceed stream size."); + } + List reservoir = new ArrayList<>(sampleSize); Random rand = new Random(); From 5c2b3d15d1f8fcbcfcc1ae9a30127863658d0508 Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Sat, 5 Apr 2025 08:04:39 -0600 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=85=20Apply=20clang-format=20to=20Res?= =?UTF-8?q?ervoirSampling.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../randomized/ReservoirSampling.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java index 5256c4869262..2fb272ecf8a1 100644 --- a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java +++ b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java @@ -1,8 +1,8 @@ package com.thealgorithms.randomized; -import java.util.Random; import java.util.ArrayList; import java.util.List; +import java.util.Random; /** * Reservoir Sampling Algorithm @@ -46,18 +46,4 @@ public static List sample(int[] stream, int sampleSize) { return reservoir; } - - // Demo usage - public static void main(String[] args) { - int[] streamData = new int[1000]; - for (int i = 0; i < 1000; i++) { - streamData[i] = i + 1; - } - - List result = ReservoirSampling.sample(streamData, 10); - System.out.println("Random sample of 10 items:"); - for (int value : result) { - System.out.print(value + " "); - } - } } From bfd9a9304937e25fe7a48471957cce46ff6a3d8b Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Sat, 5 Apr 2025 08:08:27 -0600 Subject: [PATCH 4/6] =?UTF-8?q?=E2=9C=85=20Add=20JUnit=20test=20for=20Rese?= =?UTF-8?q?rvoirSampling=20(corrected=20file=20path)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../randomized/ReservoirSamplingTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java diff --git a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java new file mode 100644 index 000000000000..90fa3b2aa93e --- /dev/null +++ b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.randomized; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ReservoirSamplingTest { + + @Test + public void testSampleSizeEqualsStreamLength() { + int[] stream = {1, 2, 3, 4, 5}; + int sampleSize = 5; + + List result = ReservoirSampling.sample(stream, sampleSize); + + assertEquals(sampleSize, result.size()); + assertTrue(Arrays.stream(stream).allMatch(result::contains)); + } + + @Test + public void testSampleSizeLessThanStreamLength() { + int[] stream = {10, 20, 30, 40, 50, 60}; + int sampleSize = 3; + + List result = ReservoirSampling.sample(stream, sampleSize); + + assertEquals(sampleSize, result.size()); + for (int value : result) { + assertTrue(Arrays.stream(stream).anyMatch(x -> x == value)); + } + } + + @Test + public void testSampleSizeGreaterThanStreamLengthThrowsException() { + int[] stream = {1, 2, 3}; + + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + ReservoirSampling.sample(stream, 5); + }); + + assertEquals("Sample size cannot exceed stream size.", exception.getMessage()); + } +} From e2a0a9a75073688d0e60a4a8bcbc236d01e009fa Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Sun, 6 Apr 2025 12:53:07 -0600 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=94=A7=20Apply=20clang-format=20to=20?= =?UTF-8?q?ReservoirSamplingTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/thealgorithms/randomized/ReservoirSamplingTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java index 90fa3b2aa93e..9501c1c8cc5a 100644 --- a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java +++ b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java @@ -36,9 +36,7 @@ public void testSampleSizeLessThanStreamLength() { public void testSampleSizeGreaterThanStreamLengthThrowsException() { int[] stream = {1, 2, 3}; - Exception exception = assertThrows(IllegalArgumentException.class, () -> { - ReservoirSampling.sample(stream, 5); - }); + Exception exception = assertThrows(IllegalArgumentException.class, () -> { ReservoirSampling.sample(stream, 5); }); assertEquals("Sample size cannot exceed stream size.", exception.getMessage()); } From 5341847e00c48c8d61d260211cc5ca575b03ff74 Mon Sep 17 00:00:00 2001 From: cureprotocols Date: Mon, 7 Apr 2025 11:23:35 -0600 Subject: [PATCH 6/6] Fix: enforce utility class policy and test import rules in ReservoirSampling --- .../thealgorithms/randomized/ReservoirSampling.java | 10 ++++++++-- .../randomized/ReservoirSamplingTest.java | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java index 2fb272ecf8a1..05e70f635055 100644 --- a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java +++ b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java @@ -14,9 +14,15 @@ * Time Complexity: O(n) * Space Complexity: O(k) * - * Author: Michael Alexander Montoya (@cureprotocols) + * @author Michael Alexander Montoya (@cureprotocols) + * @see Reservoir Sampling - Wikipedia */ -public class ReservoirSampling { +public final class ReservoirSampling { + + // Prevent instantiation of utility class + private ReservoirSampling() { + throw new UnsupportedOperationException("Utility class"); + } /** * Selects k random elements from a stream using reservoir sampling. diff --git a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java index 9501c1c8cc5a..0c6061fcde2a 100644 --- a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java +++ b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java @@ -1,6 +1,8 @@ package com.thealgorithms.randomized; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.List; @@ -36,7 +38,7 @@ public void testSampleSizeLessThanStreamLength() { public void testSampleSizeGreaterThanStreamLengthThrowsException() { int[] stream = {1, 2, 3}; - Exception exception = assertThrows(IllegalArgumentException.class, () -> { ReservoirSampling.sample(stream, 5); }); + Exception exception = assertThrows(IllegalArgumentException.class, () -> ReservoirSampling.sample(stream, 5)); assertEquals("Sample size cannot exceed stream size.", exception.getMessage()); }