Skip to content

Commit f53bc00

Browse files
Add ReservoirSampling algorithm to randomized module (#6204)
1 parent 2570a99 commit f53bc00

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.thealgorithms.randomized;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Random;
6+
7+
/**
8+
* Reservoir Sampling Algorithm
9+
*
10+
* Use Case:
11+
* - Efficient for selecting k random items from a stream of unknown size
12+
* - Used in streaming systems, big data, and memory-limited environments
13+
*
14+
* Time Complexity: O(n)
15+
* Space Complexity: O(k)
16+
*
17+
* @author Michael Alexander Montoya (@cureprotocols)
18+
* @see <a href="https://en.wikipedia.org/wiki/Reservoir_sampling">Reservoir Sampling - Wikipedia</a>
19+
*/
20+
public final class ReservoirSampling {
21+
22+
// Prevent instantiation of utility class
23+
private ReservoirSampling() {
24+
throw new UnsupportedOperationException("Utility class");
25+
}
26+
27+
/**
28+
* Selects k random elements from a stream using reservoir sampling.
29+
*
30+
* @param stream The input stream as an array of integers.
31+
* @param sampleSize The number of elements to sample.
32+
* @return A list containing k randomly selected elements.
33+
*/
34+
public static List<Integer> sample(int[] stream, int sampleSize) {
35+
if (sampleSize > stream.length) {
36+
throw new IllegalArgumentException("Sample size cannot exceed stream size.");
37+
}
38+
39+
List<Integer> reservoir = new ArrayList<>(sampleSize);
40+
Random rand = new Random();
41+
42+
for (int i = 0; i < stream.length; i++) {
43+
if (i < sampleSize) {
44+
reservoir.add(stream[i]);
45+
} else {
46+
int j = rand.nextInt(i + 1);
47+
if (j < sampleSize) {
48+
reservoir.set(j, stream[i]);
49+
}
50+
}
51+
}
52+
53+
return reservoir;
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.thealgorithms.randomized;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import org.junit.jupiter.api.Test;
10+
11+
public class ReservoirSamplingTest {
12+
13+
@Test
14+
public void testSampleSizeEqualsStreamLength() {
15+
int[] stream = {1, 2, 3, 4, 5};
16+
int sampleSize = 5;
17+
18+
List<Integer> result = ReservoirSampling.sample(stream, sampleSize);
19+
20+
assertEquals(sampleSize, result.size());
21+
assertTrue(Arrays.stream(stream).allMatch(result::contains));
22+
}
23+
24+
@Test
25+
public void testSampleSizeLessThanStreamLength() {
26+
int[] stream = {10, 20, 30, 40, 50, 60};
27+
int sampleSize = 3;
28+
29+
List<Integer> result = ReservoirSampling.sample(stream, sampleSize);
30+
31+
assertEquals(sampleSize, result.size());
32+
for (int value : result) {
33+
assertTrue(Arrays.stream(stream).anyMatch(x -> x == value));
34+
}
35+
}
36+
37+
@Test
38+
public void testSampleSizeGreaterThanStreamLengthThrowsException() {
39+
int[] stream = {1, 2, 3};
40+
41+
Exception exception = assertThrows(IllegalArgumentException.class, () -> ReservoirSampling.sample(stream, 5));
42+
43+
assertEquals("Sample size cannot exceed stream size.", exception.getMessage());
44+
}
45+
}

0 commit comments

Comments
 (0)