mirror of
https://github.com/TheAlgorithms/Java.git
synced 2026-03-13 08:40:43 +08:00
Added mos algorithm and dice thrower (#6591)
* Add Sum of Squares algorithm implementation * Format code and add Wikipedia URL for Lagrange's theorem * Fixed clang-format issues * Added Mo's Algorithm and DiceThrower recursive algorithms - Mo's Algorithm: Square root decomposition for offline range queries(Imp in CP) - DiceThrower: Recursive backtracking for dice combinations(very imp) - Both algorithms include comprehensive test suites - Formatted with clang-format and i follow contribution guidelines * Fixed checkstyle violation * Fixed SpotBugs issue * Added in PMD exclusions * Improved test coverage for better Codecov scores. * Fixed clang-format issues in test files * Add Mo's Algorithm and DiceThrower algorithms with comprehensive tests * Fixed PartitionProblem.java documentation comment placement
This commit is contained in:
@@ -88,12 +88,14 @@ com.thealgorithms.others.LinearCongruentialGenerator=UselessMainMethod
|
||||
com.thealgorithms.others.Luhn=UnnecessaryFullyQualifiedName,UselessMainMethod
|
||||
com.thealgorithms.others.Mandelbrot=UselessMainMethod,UselessParentheses
|
||||
com.thealgorithms.others.MiniMaxAlgorithm=UselessMainMethod,UselessParentheses
|
||||
com.thealgorithms.others.MosAlgorithm=UselessMainMethod
|
||||
com.thealgorithms.others.PageRank=UselessMainMethod,UselessParentheses
|
||||
com.thealgorithms.others.PerlinNoise=UselessMainMethod,UselessParentheses
|
||||
com.thealgorithms.others.QueueUsingTwoStacks=UselessParentheses
|
||||
com.thealgorithms.others.Trieac=UselessMainMethod,UselessParentheses
|
||||
com.thealgorithms.others.Verhoeff=UnnecessaryFullyQualifiedName,UselessMainMethod
|
||||
com.thealgorithms.puzzlesandgames.Sudoku=UselessMainMethod
|
||||
com.thealgorithms.recursion.DiceThrower=UselessMainMethod
|
||||
com.thealgorithms.searches.HowManyTimesRotated=UselessMainMethod
|
||||
com.thealgorithms.searches.InterpolationSearch=UselessParentheses
|
||||
com.thealgorithms.searches.KMPSearch=UselessParentheses
|
||||
|
||||
260
src/main/java/com/thealgorithms/others/MosAlgorithm.java
Normal file
260
src/main/java/com/thealgorithms/others/MosAlgorithm.java
Normal file
@@ -0,0 +1,260 @@
|
||||
package com.thealgorithms.others;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Mo's Algorithm (Square Root Decomposition) for offline range queries
|
||||
*
|
||||
* Mo's Algorithm is used to answer range queries efficiently when:
|
||||
* 1. Queries can be processed offline (all queries known beforehand)
|
||||
* 2. We can efficiently add/remove elements from current range
|
||||
* 3. The problem has optimal substructure for range operations
|
||||
*
|
||||
* Time Complexity: O((N + Q) * sqrt(N)) where N = array size, Q = number of queries
|
||||
* Space Complexity: O(N + Q)
|
||||
*
|
||||
* @see <a href="https://www.geeksforgeeks.org/dsa/mos-algorithm-query-square-root-decomposition-set-1-introduction/">Mo's Algorithm</a>
|
||||
* @author BEASTSHRIRAM
|
||||
*/
|
||||
public final class MosAlgorithm {
|
||||
|
||||
/**
|
||||
* Query structure to store range queries
|
||||
*/
|
||||
public static class Query {
|
||||
public final int left;
|
||||
public final int right;
|
||||
public final int index; // Original index of query
|
||||
public int result; // Result of the query
|
||||
|
||||
public Query(int left, int right, int index) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.index = index;
|
||||
this.result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private MosAlgorithm() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves range sum queries using Mo's Algorithm
|
||||
*
|
||||
* @param arr the input array
|
||||
* @param queries array of queries to process
|
||||
* @return array of results corresponding to each query
|
||||
*/
|
||||
public static int[] solveRangeSumQueries(int[] arr, Query[] queries) {
|
||||
if (arr == null || queries == null || arr.length == 0) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
int n = arr.length;
|
||||
int blockSize = (int) Math.sqrt(n);
|
||||
|
||||
// Sort queries using Mo's ordering
|
||||
Arrays.sort(queries, new MoComparator(blockSize));
|
||||
|
||||
// Initialize variables for current range
|
||||
int currentLeft = 0;
|
||||
int currentRight = -1;
|
||||
int currentSum = 0;
|
||||
|
||||
// Process each query
|
||||
for (Query query : queries) {
|
||||
// Expand or shrink the current range to match query range
|
||||
|
||||
// Expand right boundary
|
||||
while (currentRight < query.right) {
|
||||
currentRight++;
|
||||
currentSum += arr[currentRight];
|
||||
}
|
||||
|
||||
// Shrink right boundary
|
||||
while (currentRight > query.right) {
|
||||
currentSum -= arr[currentRight];
|
||||
currentRight--;
|
||||
}
|
||||
|
||||
// Expand left boundary
|
||||
while (currentLeft < query.left) {
|
||||
currentSum -= arr[currentLeft];
|
||||
currentLeft++;
|
||||
}
|
||||
|
||||
// Shrink left boundary
|
||||
while (currentLeft > query.left) {
|
||||
currentLeft--;
|
||||
currentSum += arr[currentLeft];
|
||||
}
|
||||
|
||||
// Store the result
|
||||
query.result = currentSum;
|
||||
}
|
||||
|
||||
// Extract results in original query order
|
||||
int[] results = new int[queries.length];
|
||||
for (Query query : queries) {
|
||||
results[query.index] = query.result;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves range frequency queries using Mo's Algorithm
|
||||
* Example: Count occurrences of a specific value in range [L, R]
|
||||
*
|
||||
* @param arr the input array
|
||||
* @param queries array of queries to process
|
||||
* @param targetValue the value to count in each range
|
||||
* @return array of results corresponding to each query
|
||||
*/
|
||||
public static int[] solveRangeFrequencyQueries(int[] arr, Query[] queries, int targetValue) {
|
||||
if (arr == null || queries == null || arr.length == 0) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
int n = arr.length;
|
||||
int blockSize = (int) Math.sqrt(n);
|
||||
|
||||
// Sort queries using Mo's ordering
|
||||
Arrays.sort(queries, new MoComparator(blockSize));
|
||||
|
||||
// Initialize variables for current range
|
||||
int currentLeft = 0;
|
||||
int currentRight = -1;
|
||||
int currentCount = 0;
|
||||
|
||||
// Process each query
|
||||
for (Query query : queries) {
|
||||
// Expand right boundary
|
||||
while (currentRight < query.right) {
|
||||
currentRight++;
|
||||
if (arr[currentRight] == targetValue) {
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Shrink right boundary
|
||||
while (currentRight > query.right) {
|
||||
if (arr[currentRight] == targetValue) {
|
||||
currentCount--;
|
||||
}
|
||||
currentRight--;
|
||||
}
|
||||
|
||||
// Expand left boundary
|
||||
while (currentLeft < query.left) {
|
||||
if (arr[currentLeft] == targetValue) {
|
||||
currentCount--;
|
||||
}
|
||||
currentLeft++;
|
||||
}
|
||||
|
||||
// Shrink left boundary
|
||||
while (currentLeft > query.left) {
|
||||
currentLeft--;
|
||||
if (arr[currentLeft] == targetValue) {
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the result
|
||||
query.result = currentCount;
|
||||
}
|
||||
|
||||
// Extract results in original query order
|
||||
int[] results = new int[queries.length];
|
||||
for (Query query : queries) {
|
||||
results[query.index] = query.result;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator for Mo's Algorithm query ordering
|
||||
* Queries are sorted by block of left endpoint, then by right endpoint
|
||||
*/
|
||||
private static class MoComparator implements Comparator<Query> {
|
||||
private final int blockSize;
|
||||
|
||||
MoComparator(int blockSize) {
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Query a, Query b) {
|
||||
int blockA = a.left / blockSize;
|
||||
int blockB = b.left / blockSize;
|
||||
|
||||
if (blockA != blockB) {
|
||||
return Integer.compare(blockA, blockB);
|
||||
}
|
||||
|
||||
// For odd blocks, sort right in ascending order
|
||||
// For even blocks, sort right in descending order (optimization)
|
||||
if ((blockA & 1) == 1) {
|
||||
return Integer.compare(a.right, b.right);
|
||||
} else {
|
||||
return Integer.compare(b.right, a.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demo method showing usage of Mo's Algorithm
|
||||
*
|
||||
* @param args command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Example: Range sum queries
|
||||
int[] arr = {1, 3, 5, 2, 7, 6, 3, 1, 4, 8};
|
||||
|
||||
Query[] queries = {
|
||||
new Query(0, 2, 0), // Sum of elements from index 0 to 2: 1+3+5 = 9
|
||||
new Query(1, 4, 1), // Sum of elements from index 1 to 4: 3+5+2+7 = 17
|
||||
new Query(2, 6, 2), // Sum of elements from index 2 to 6: 5+2+7+6+3 = 23
|
||||
new Query(3, 8, 3) // Sum of elements from index 3 to 8: 2+7+6+3+1+4 = 23
|
||||
};
|
||||
|
||||
System.out.println("Array: " + Arrays.toString(arr));
|
||||
System.out.println("Range Sum Queries:");
|
||||
|
||||
// Store original queries for display
|
||||
Query[] originalQueries = new Query[queries.length];
|
||||
for (int i = 0; i < queries.length; i++) {
|
||||
originalQueries[i] = new Query(queries[i].left, queries[i].right, queries[i].index);
|
||||
}
|
||||
|
||||
int[] results = solveRangeSumQueries(arr, queries);
|
||||
|
||||
for (int i = 0; i < originalQueries.length; i++) {
|
||||
System.out.printf("Query %d: Sum of range [%d, %d] = %d%n", i, originalQueries[i].left, originalQueries[i].right, results[i]);
|
||||
}
|
||||
|
||||
// Example: Range frequency queries
|
||||
System.out.println("\nRange Frequency Queries (count of value 3):");
|
||||
Query[] freqQueries = {
|
||||
new Query(0, 5, 0), // Count of 3 in range [0, 5]: 1 occurrence
|
||||
new Query(2, 8, 1), // Count of 3 in range [2, 8]: 2 occurrences
|
||||
new Query(6, 9, 2) // Count of 3 in range [6, 9]: 1 occurrence
|
||||
};
|
||||
|
||||
// Store original frequency queries for display
|
||||
Query[] originalFreqQueries = new Query[freqQueries.length];
|
||||
for (int i = 0; i < freqQueries.length; i++) {
|
||||
originalFreqQueries[i] = new Query(freqQueries[i].left, freqQueries[i].right, freqQueries[i].index);
|
||||
}
|
||||
|
||||
int[] freqResults = solveRangeFrequencyQueries(arr, freqQueries, 3);
|
||||
|
||||
for (int i = 0; i < originalFreqQueries.length; i++) {
|
||||
System.out.printf("Query %d: Count of 3 in range [%d, %d] = %d%n", i, originalFreqQueries[i].left, originalFreqQueries[i].right, freqResults[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
113
src/main/java/com/thealgorithms/recursion/DiceThrower.java
Normal file
113
src/main/java/com/thealgorithms/recursion/DiceThrower.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.thealgorithms.recursion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DiceThrower - Generates all possible dice roll combinations that sum to a target
|
||||
*
|
||||
* This algorithm uses recursive backtracking to find all combinations of dice rolls
|
||||
* (faces 1-6) that sum to a given target value.
|
||||
*
|
||||
* Example: If target = 4, possible combinations include:
|
||||
* - "1111" (1+1+1+1 = 4)
|
||||
* - "13" (1+3 = 4)
|
||||
* - "22" (2+2 = 4)
|
||||
* - "4" (4 = 4)
|
||||
*
|
||||
* @author BEASTSHRIRAM
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Backtracking">Backtracking Algorithm</a>
|
||||
*/
|
||||
public final class DiceThrower {
|
||||
|
||||
private DiceThrower() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all possible dice roll combinations that sum to the target
|
||||
*
|
||||
* @param target the target sum to achieve with dice rolls
|
||||
* @return list of all possible combinations as strings
|
||||
*/
|
||||
public static List<String> getDiceCombinations(int target) {
|
||||
if (target < 0) {
|
||||
throw new IllegalArgumentException("Target must be non-negative");
|
||||
}
|
||||
return generateCombinations("", target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints all possible dice roll combinations that sum to the target
|
||||
*
|
||||
* @param target the target sum to achieve with dice rolls
|
||||
*/
|
||||
public static void printDiceCombinations(int target) {
|
||||
if (target < 0) {
|
||||
throw new IllegalArgumentException("Target must be non-negative");
|
||||
}
|
||||
printCombinations("", target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper method to generate all combinations
|
||||
*
|
||||
* @param current the current combination being built
|
||||
* @param remaining the remaining sum needed
|
||||
* @return list of all combinations from this state
|
||||
*/
|
||||
private static List<String> generateCombinations(String current, int remaining) {
|
||||
List<String> combinations = new ArrayList<>();
|
||||
|
||||
// Base case: if remaining sum is 0, we found a valid combination
|
||||
if (remaining == 0) {
|
||||
combinations.add(current);
|
||||
return combinations;
|
||||
}
|
||||
|
||||
// Try all possible dice faces (1-6), but not more than remaining sum
|
||||
for (int face = 1; face <= 6 && face <= remaining; face++) {
|
||||
List<String> subCombinations = generateCombinations(current + face, remaining - face);
|
||||
combinations.addAll(subCombinations);
|
||||
}
|
||||
|
||||
return combinations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper method to print all combinations
|
||||
*
|
||||
* @param current the current combination being built
|
||||
* @param remaining the remaining sum needed
|
||||
*/
|
||||
private static void printCombinations(String current, int remaining) {
|
||||
// Base case: if remaining sum is 0, we found a valid combination
|
||||
if (remaining == 0) {
|
||||
System.out.println(current);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try all possible dice faces (1-6), but not more than remaining sum
|
||||
for (int face = 1; face <= 6 && face <= remaining; face++) {
|
||||
printCombinations(current + face, remaining - face);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Demo method to show usage
|
||||
*
|
||||
* @param args command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int target = 4;
|
||||
|
||||
System.out.println("All dice combinations that sum to " + target + ":");
|
||||
List<String> combinations = getDiceCombinations(target);
|
||||
|
||||
for (String combination : combinations) {
|
||||
System.out.println(combination);
|
||||
}
|
||||
|
||||
System.out.println("\nTotal combinations: " + combinations.size());
|
||||
}
|
||||
}
|
||||
202
src/test/java/com/thealgorithms/others/MosAlgorithmTest.java
Normal file
202
src/test/java/com/thealgorithms/others/MosAlgorithmTest.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package com.thealgorithms.others;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test class for MosAlgorithm
|
||||
*
|
||||
* @author BEASTSHRIRAM
|
||||
*/
|
||||
class MosAlgorithmTest {
|
||||
|
||||
@Test
|
||||
void testRangeSumQueriesBasic() {
|
||||
int[] arr = {1, 3, 5, 2, 7};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 2, 0), // Sum of [1, 3, 5] = 9
|
||||
new MosAlgorithm.Query(1, 3, 1), // Sum of [3, 5, 2] = 10
|
||||
new MosAlgorithm.Query(2, 4, 2) // Sum of [5, 2, 7] = 14
|
||||
};
|
||||
|
||||
int[] expected = {9, 10, 14};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeSumQueriesSingleElement() {
|
||||
int[] arr = {5, 10, 15, 20};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 0, 0), // Sum of [5] = 5
|
||||
new MosAlgorithm.Query(1, 1, 1), // Sum of [10] = 10
|
||||
new MosAlgorithm.Query(2, 2, 2), // Sum of [15] = 15
|
||||
new MosAlgorithm.Query(3, 3, 3) // Sum of [20] = 20
|
||||
};
|
||||
|
||||
int[] expected = {5, 10, 15, 20};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeSumQueriesFullArray() {
|
||||
int[] arr = {1, 2, 3, 4, 5};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 4, 0) // Sum of entire array = 15
|
||||
};
|
||||
|
||||
int[] expected = {15};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeSumQueriesOverlapping() {
|
||||
int[] arr = {2, 4, 6, 8, 10};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 2, 0), // Sum of [2, 4, 6] = 12
|
||||
new MosAlgorithm.Query(1, 3, 1), // Sum of [4, 6, 8] = 18
|
||||
new MosAlgorithm.Query(2, 4, 2) // Sum of [6, 8, 10] = 24
|
||||
};
|
||||
|
||||
int[] expected = {12, 18, 24};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeFrequencyQueriesBasic() {
|
||||
int[] arr = {1, 2, 2, 1, 3, 2, 1};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 3, 0), // Count of 2 in [1, 2, 2, 1] = 2
|
||||
new MosAlgorithm.Query(1, 5, 1), // Count of 2 in [2, 2, 1, 3, 2] = 3
|
||||
new MosAlgorithm.Query(4, 6, 2) // Count of 2 in [3, 2, 1] = 1
|
||||
};
|
||||
|
||||
int[] expected = {2, 3, 1};
|
||||
int[] results = MosAlgorithm.solveRangeFrequencyQueries(arr, queries, 2);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeFrequencyQueriesNoMatch() {
|
||||
int[] arr = {1, 3, 5, 7, 9};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 2, 0), // Count of 2 in [1, 3, 5] = 0
|
||||
new MosAlgorithm.Query(1, 4, 1) // Count of 2 in [3, 5, 7, 9] = 0
|
||||
};
|
||||
|
||||
int[] expected = {0, 0};
|
||||
int[] results = MosAlgorithm.solveRangeFrequencyQueries(arr, queries, 2);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeFrequencyQueriesAllMatch() {
|
||||
int[] arr = {5, 5, 5, 5, 5};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 2, 0), // Count of 5 in [5, 5, 5] = 3
|
||||
new MosAlgorithm.Query(1, 3, 1), // Count of 5 in [5, 5, 5] = 3
|
||||
new MosAlgorithm.Query(0, 4, 2) // Count of 5 in [5, 5, 5, 5, 5] = 5
|
||||
};
|
||||
|
||||
int[] expected = {3, 3, 5};
|
||||
int[] results = MosAlgorithm.solveRangeFrequencyQueries(arr, queries, 5);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyArray() {
|
||||
int[] arr = {};
|
||||
MosAlgorithm.Query[] queries = {};
|
||||
|
||||
int[] expected = {};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullInputs() {
|
||||
int[] results1 = MosAlgorithm.solveRangeSumQueries(null, null);
|
||||
assertArrayEquals(new int[0], results1);
|
||||
|
||||
int[] results2 = MosAlgorithm.solveRangeFrequencyQueries(null, null, 1);
|
||||
assertArrayEquals(new int[0], results2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQueryStructure() {
|
||||
MosAlgorithm.Query query = new MosAlgorithm.Query(1, 5, 0);
|
||||
|
||||
assertEquals(1, query.left);
|
||||
assertEquals(5, query.right);
|
||||
assertEquals(0, query.index);
|
||||
assertEquals(0, query.result); // Default value
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLargerArray() {
|
||||
int[] arr = {1, 4, 2, 8, 5, 7, 3, 6, 9, 10};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 4, 0), // Sum of [1, 4, 2, 8, 5] = 20
|
||||
new MosAlgorithm.Query(2, 7, 1), // Sum of [2, 8, 5, 7, 3, 6] = 31
|
||||
new MosAlgorithm.Query(5, 9, 2), // Sum of [7, 3, 6, 9, 10] = 35
|
||||
new MosAlgorithm.Query(1, 8, 3) // Sum of [4, 2, 8, 5, 7, 3, 6, 9] = 44
|
||||
};
|
||||
|
||||
int[] expected = {20, 31, 35, 44};
|
||||
int[] results = MosAlgorithm.solveRangeSumQueries(arr, queries);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRangeFrequencyWithDuplicates() {
|
||||
int[] arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
|
||||
MosAlgorithm.Query[] queries = {
|
||||
new MosAlgorithm.Query(0, 5, 0), // Count of 1 in [3, 1, 4, 1, 5, 9] = 2
|
||||
new MosAlgorithm.Query(3, 9, 1), // Count of 1 in [1, 5, 9, 2, 6, 5, 3] = 1
|
||||
new MosAlgorithm.Query(0, 9, 2) // Count of 1 in entire array = 2
|
||||
};
|
||||
|
||||
int[] expected = {2, 1, 2};
|
||||
int[] results = MosAlgorithm.solveRangeFrequencyQueries(arr, queries, 1);
|
||||
|
||||
assertArrayEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMainMethod() {
|
||||
// Capture System.out
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PrintStream originalOut = System.out;
|
||||
System.setOut(new PrintStream(outputStream));
|
||||
|
||||
try {
|
||||
// Test main method
|
||||
MosAlgorithm.main(new String[] {});
|
||||
String output = outputStream.toString();
|
||||
|
||||
// Verify expected output contains demonstration
|
||||
assertTrue(output.contains("Range Sum Queries:"));
|
||||
assertTrue(output.contains("Range Frequency Queries (count of value 3):"));
|
||||
assertTrue(output.contains("Array: [1, 3, 5, 2, 7, 6, 3, 1, 4, 8]"));
|
||||
} finally {
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
223
src/test/java/com/thealgorithms/recursion/DiceThrowerTest.java
Normal file
223
src/test/java/com/thealgorithms/recursion/DiceThrowerTest.java
Normal file
@@ -0,0 +1,223 @@
|
||||
package com.thealgorithms.recursion;
|
||||
|
||||
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.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test class for DiceThrower
|
||||
*
|
||||
* @author BEASTSHRIRAM
|
||||
*/
|
||||
class DiceThrowerTest {
|
||||
|
||||
@Test
|
||||
void testTargetZero() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(0);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("", result.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetOne() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(1);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("1", result.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetTwo() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(2);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.contains("11"));
|
||||
assertTrue(result.contains("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetThree() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(3);
|
||||
assertEquals(4, result.size());
|
||||
assertTrue(result.contains("111"));
|
||||
assertTrue(result.contains("12"));
|
||||
assertTrue(result.contains("21"));
|
||||
assertTrue(result.contains("3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetFour() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(4);
|
||||
assertEquals(8, result.size());
|
||||
assertTrue(result.contains("1111"));
|
||||
assertTrue(result.contains("112"));
|
||||
assertTrue(result.contains("121"));
|
||||
assertTrue(result.contains("13"));
|
||||
assertTrue(result.contains("211"));
|
||||
assertTrue(result.contains("22"));
|
||||
assertTrue(result.contains("31"));
|
||||
assertTrue(result.contains("4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetSix() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(6);
|
||||
assertEquals(32, result.size());
|
||||
assertTrue(result.contains("6"));
|
||||
assertTrue(result.contains("33"));
|
||||
assertTrue(result.contains("222"));
|
||||
assertTrue(result.contains("111111"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetSeven() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(7);
|
||||
// Should include combinations like 61, 52, 43, 331, 322, 2221, etc.
|
||||
assertTrue(result.size() > 0);
|
||||
assertTrue(result.contains("61"));
|
||||
assertTrue(result.contains("16"));
|
||||
assertTrue(result.contains("52"));
|
||||
assertTrue(result.contains("43"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLargerTarget() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(10);
|
||||
assertTrue(result.size() > 0);
|
||||
// All results should sum to 10
|
||||
for (String combination : result) {
|
||||
int sum = 0;
|
||||
for (char c : combination.toCharArray()) {
|
||||
sum += Character.getNumericValue(c);
|
||||
}
|
||||
assertEquals(10, sum);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegativeTarget() {
|
||||
assertThrows(IllegalArgumentException.class, () -> { DiceThrower.getDiceCombinations(-1); });
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegativeTargetPrint() {
|
||||
assertThrows(IllegalArgumentException.class, () -> { DiceThrower.printDiceCombinations(-1); });
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllCombinationsValid() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(5);
|
||||
|
||||
for (String combination : result) {
|
||||
// Check that each character is a valid dice face (1-6)
|
||||
for (char c : combination.toCharArray()) {
|
||||
int face = Character.getNumericValue(c);
|
||||
assertTrue(face >= 1 && face <= 6, "Invalid dice face: " + face);
|
||||
}
|
||||
|
||||
// Check that the sum equals the target
|
||||
int sum = 0;
|
||||
for (char c : combination.toCharArray()) {
|
||||
sum += Character.getNumericValue(c);
|
||||
}
|
||||
assertEquals(5, sum, "Combination " + combination + " does not sum to 5");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrintDiceCombinations() {
|
||||
// Capture System.out
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PrintStream originalOut = System.out;
|
||||
System.setOut(new PrintStream(outputStream));
|
||||
|
||||
try {
|
||||
// Test printing combinations for target 3
|
||||
DiceThrower.printDiceCombinations(3);
|
||||
String output = outputStream.toString();
|
||||
|
||||
// Verify all expected combinations are printed
|
||||
assertTrue(output.contains("111"));
|
||||
assertTrue(output.contains("12"));
|
||||
assertTrue(output.contains("21"));
|
||||
assertTrue(output.contains("3"));
|
||||
|
||||
// Count number of lines (combinations)
|
||||
String[] lines = output.trim().split("\n");
|
||||
assertEquals(4, lines.length);
|
||||
} finally {
|
||||
// Restore System.out
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrintDiceCombinationsZero() {
|
||||
// Capture System.out
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PrintStream originalOut = System.out;
|
||||
System.setOut(new PrintStream(outputStream));
|
||||
|
||||
try {
|
||||
DiceThrower.printDiceCombinations(0);
|
||||
String output = outputStream.toString();
|
||||
|
||||
// Should print empty string (one line)
|
||||
assertEquals("", output.trim());
|
||||
} finally {
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMainMethod() {
|
||||
// Capture System.out
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PrintStream originalOut = System.out;
|
||||
System.setOut(new PrintStream(outputStream));
|
||||
|
||||
try {
|
||||
// Test main method
|
||||
DiceThrower.main(new String[] {});
|
||||
String output = outputStream.toString();
|
||||
|
||||
// Verify expected output contains header and combinations
|
||||
assertTrue(output.contains("All dice combinations that sum to 4:"));
|
||||
assertTrue(output.contains("Total combinations: 8"));
|
||||
assertTrue(output.contains("1111"));
|
||||
assertTrue(output.contains("22"));
|
||||
assertTrue(output.contains("4"));
|
||||
} finally {
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEdgeCaseTargetFive() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(5);
|
||||
assertEquals(16, result.size());
|
||||
|
||||
// Test specific combinations exist
|
||||
assertTrue(result.contains("11111"));
|
||||
assertTrue(result.contains("1112"));
|
||||
assertTrue(result.contains("122"));
|
||||
assertTrue(result.contains("14"));
|
||||
assertTrue(result.contains("23"));
|
||||
assertTrue(result.contains("5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTargetGreaterThanSix() {
|
||||
List<String> result = DiceThrower.getDiceCombinations(8);
|
||||
assertTrue(result.size() > 0);
|
||||
|
||||
// Verify some expected combinations
|
||||
assertTrue(result.contains("62"));
|
||||
assertTrue(result.contains("53"));
|
||||
assertTrue(result.contains("44"));
|
||||
assertTrue(result.contains("2222"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user