+ * The cache holds a fixed number of entries, defined by its capacity. When the cache is full and a + * new entry is added, the oldest entry in the cache is selected and evicted to make space. + *
+ * Optionally, entries can have a time-to-live (TTL) in milliseconds. If a TTL is set, entries will + * automatically expire and be removed upon access or insertion attempts. + *
+ * Features: + *
This constructor initializes the cache with the specified capacity and default TTL,
+ * sets up internal data structures (a {@code LinkedHashMap} for cache entries and configures eviction.
+ *
+ * @param builder the {@code Builder} object containing configuration parameters
+ */
+ private FIFOCache(Builder If the key is not present or the corresponding entry has expired, this method
+ * returns {@code null}. If an expired entry is found, it will be removed and the
+ * eviction listener (if any) will be notified. Cache hit-and-miss statistics are
+ * also updated accordingly.
+ *
+ * @param key the key whose associated value is to be returned; must not be {@code null}
+ * @return the cached value associated with the key, or {@code null} if not present or expired
+ * @throws IllegalArgumentException if {@code key} is {@code null}
+ */
+ public V get(K key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key must not be null");
+ }
+
+ lock.lock();
+ try {
+ evictionStrategy.onAccess(this);
+
+ CacheEntry The key may overwrite an existing entry. The actual insertion is delegated
+ * to the overloaded {@link #put(K, V, long)} method.
+ *
+ * @param key the key to cache the value under
+ * @param value the value to be cached
+ */
+ public void put(K key, V value) {
+ put(key, value, defaultTTL);
+ }
+
+ /**
+ * Adds a key-value pair to the cache with a specified time-to-live (TTL).
+ *
+ * If the key already exists, its value is removed, re-inserted at tail and its TTL is reset.
+ * If the key does not exist and the cache is full, the oldest entry is evicted to make space.
+ * Expired entries are also cleaned up prior to any eviction. The eviction listener
+ * is notified when an entry gets evicted.
+ *
+ * @param key the key to associate with the cached value; must not be {@code null}
+ * @param value the value to be cached; must not be {@code null}
+ * @param ttlMillis the time-to-live for this entry in milliseconds; must be >= 0
+ * @throws IllegalArgumentException if {@code key} or {@code value} is {@code null}, or if {@code ttlMillis} is negative
+ */
+ public void put(K key, V value, long ttlMillis) {
+ if (key == null || value == null) {
+ throw new IllegalArgumentException("Key and value must not be null");
+ }
+ if (ttlMillis < 0) {
+ throw new IllegalArgumentException("TTL must be >= 0");
+ }
+
+ lock.lock();
+ try {
+ // If key already exists, remove it
+ CacheEntry This method iterates through the list of cached keys and checks each associated
+ * entry for expiration. Expired entries are removed the cache map. For each eviction,
+ * the eviction listener is notified.
+ */
+ private int evictExpired() {
+ int count = 0;
+ Iterator If the {@code evictionListener} is not {@code null}, it is invoked with the provided key
+ * and value. Any exceptions thrown by the listener are caught and logged to standard error,
+ * preventing them from disrupting cache operations.
+ *
+ * @param key the key that was evicted
+ * @param value the value that was associated with the evicted key
+ */
+ private void notifyEviction(K key, V value) {
+ if (evictionListener != null) {
+ try {
+ evictionListener.accept(key, value);
+ } catch (Exception e) {
+ System.err.println("Eviction listener failed: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Returns the number of successful cache lookups (hits).
+ *
+ * @return the number of cache hits
+ */
+ public long getHits() {
+ lock.lock();
+ try {
+ return hits;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the number of failed cache lookups (misses), including expired entries.
+ *
+ * @return the number of cache misses
+ */
+ public long getMisses() {
+ lock.lock();
+ try {
+ return misses;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the current number of entries in the cache, excluding expired ones.
+ *
+ * @return the current cache size
+ */
+ public int size() {
+ lock.lock();
+ try {
+ evictionStrategy.onAccess(this);
+
+ int count = 0;
+ for (CacheEntry This method clears the internal cache map entirely, resets the hit-and-miss counters,
+ * and notifies the eviction listener (if any) for each removed entry.
+ * Note that expired entries are treated the same as active ones for the purpose of clearing.
+ *
+ * This operation acquires the internal lock to ensure thread safety.
+ */
+ public void clear() {
+ lock.lock();
+ try {
+ for (Map.Entry This method iterates through the cache and collects the keys of all non-expired entries.
+ * Expired entries are ignored but not removed. If you want to ensure expired entries are cleaned up,
+ * consider invoking {@link EvictionStrategy#onAccess(FIFOCache)} or calling {@link #evictExpired()} manually.
+ *
+ * This operation acquires the internal lock to ensure thread safety.
+ *
+ * @return a set containing all non-expired keys currently in the cache
+ */
+ public Set The returned string includes the cache's capacity, current size (excluding expired entries),
+ * hit-and-miss counts, and a map of all non-expired key-value pairs. This method acquires a lock
+ * to ensure thread-safe access.
+ *
+ * @return a string summarizing the state of the cache
+ */
+ @Override
+ public String toString() {
+ lock.lock();
+ try {
+ Map Implementations decide whether and when to trigger {@link FIFOCache#evictExpired()} based
+ * on cache usage patterns. This allows for flexible eviction behaviour such as periodic cleanup,
+ * or no automatic cleanup.
+ *
+ * @param This deterministic strategy ensures cleanup occurs at predictable intervals,
+ * ideal for moderately active caches where memory usage is a concern.
+ *
+ * @param Allows configuring capacity, default TTL, eviction listener, and a pluggable eviction
+ * strategy. Call {@link #build()} to create the configured cache instance.
+ *
+ * @param
+ * The cache holds a fixed number of entries, defined by its capacity. When the cache is full and a
+ * new entry is added, the youngest entry in the cache is selected and evicted to make space.
+ *
+ * Optionally, entries can have a time-to-live (TTL) in milliseconds. If a TTL is set, entries will
+ * automatically expire and be removed upon access or insertion attempts.
+ *
+ * Features:
+ * This constructor initializes the cache with the specified capacity and default TTL,
+ * sets up internal data structures (a {@code HashMap} for cache entries,
+ * {an @code ArrayDeque}, for key storage, and configures eviction.
+ *
+ * @param builder the {@code Builder} object containing configuration parameters
+ */
+ private LIFOCache(Builder If the key is not present or the corresponding entry has expired, this method
+ * returns {@code null}. If an expired entry is found, it will be removed and the
+ * eviction listener (if any) will be notified. Cache hit-and-miss statistics are
+ * also updated accordingly.
+ *
+ * @param key the key whose associated value is to be returned; must not be {@code null}
+ * @return the cached value associated with the key, or {@code null} if not present or expired
+ * @throws IllegalArgumentException if {@code key} is {@code null}
+ */
+ public V get(K key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key must not be null");
+ }
+
+ lock.lock();
+ try {
+ evictionStrategy.onAccess(this);
+
+ final CacheEntry The key may overwrite an existing entry. The actual insertion is delegated
+ * to the overloaded {@link #put(K, V, long)} method.
+ *
+ * @param key the key to cache the value under
+ * @param value the value to be cached
+ */
+ public void put(K key, V value) {
+ put(key, value, defaultTTL);
+ }
+
+ /**
+ * Adds a key-value pair to the cache with a specified time-to-live (TTL).
+ *
+ * If the key already exists, its value is removed, re-inserted at tail and its TTL is reset.
+ * If the key does not exist and the cache is full, the youngest entry is evicted to make space.
+ * Expired entries are also cleaned up prior to any eviction. The eviction listener
+ * is notified when an entry gets evicted.
+ *
+ * @param key the key to associate with the cached value; must not be {@code null}
+ * @param value the value to be cached; must not be {@code null}
+ * @param ttlMillis the time-to-live for this entry in milliseconds; must be >= 0
+ * @throws IllegalArgumentException if {@code key} or {@code value} is {@code null}, or if {@code ttlMillis} is negative
+ */
+ public void put(K key, V value, long ttlMillis) {
+ if (key == null || value == null) {
+ throw new IllegalArgumentException("Key and value must not be null");
+ }
+ if (ttlMillis < 0) {
+ throw new IllegalArgumentException("TTL must be >= 0");
+ }
+
+ lock.lock();
+ try {
+ // If key already exists, remove it. It will later be re-inserted at top of stack
+ keys.remove(key);
+ final CacheEntry This method iterates through the list of cached keys and checks each associated
+ * entry for expiration. Expired entries are removed the cache map. For each eviction,
+ * the eviction listener is notified.
+ */
+ private int evictExpired() {
+ int count = 0;
+ final Iterator If the {@code evictionListener} is not {@code null}, it is invoked with the provided key
+ * and value. Any exceptions thrown by the listener are caught and logged to standard error,
+ * preventing them from disrupting cache operations.
+ *
+ * @param key the key that was evicted
+ * @param value the value that was associated with the evicted key
+ */
+ private void notifyEviction(K key, V value) {
+ if (evictionListener != null) {
+ try {
+ evictionListener.accept(key, value);
+ } catch (Exception e) {
+ System.err.println("Eviction listener failed: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Returns the number of successful cache lookups (hits).
+ *
+ * @return the number of cache hits
+ */
+ public long getHits() {
+ lock.lock();
+ try {
+ return hits;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the number of failed cache lookups (misses), including expired entries.
+ *
+ * @return the number of cache misses
+ */
+ public long getMisses() {
+ lock.lock();
+ try {
+ return misses;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the current number of entries in the cache, excluding expired ones.
+ *
+ * @return the current cache size
+ */
+ public int size() {
+ lock.lock();
+ try {
+ evictionStrategy.onAccess(this);
+
+ int count = 0;
+ for (CacheEntry This method clears the internal cache map entirely, resets the hit-and-miss counters,
+ * and notifies the eviction listener (if any) for each removed entry.
+ * Note that expired entries are treated the same as active ones for the purpose of clearing.
+ *
+ * This operation acquires the internal lock to ensure thread safety.
+ */
+ public void clear() {
+ lock.lock();
+ try {
+ for (Map.Entry This method iterates through the cache and collects the keys of all non-expired entries.
+ * Expired entries are ignored but not removed. If you want to ensure expired entries are cleaned up,
+ * consider invoking {@link EvictionStrategy#onAccess(LIFOCache)} or calling {@link #evictExpired()} manually.
+ *
+ * This operation acquires the internal lock to ensure thread safety.
+ *
+ * @return a set containing all non-expired keys currently in the cache
+ */
+ public Set The returned string includes the cache's capacity, current size (excluding expired entries),
+ * hit-and-miss counts, and a map of all non-expired key-value pairs. This method acquires a lock
+ * to ensure thread-safe access.
+ *
+ * @return a string summarizing the state of the cache
+ */
+ @Override
+ public String toString() {
+ lock.lock();
+ try {
+ final Map Implementations decide whether and when to trigger {@link LIFOCache#evictExpired()} based
+ * on cache usage patterns. This allows for flexible eviction behaviour such as periodic cleanup,
+ * or no automatic cleanup.
+ *
+ * @param This deterministic strategy ensures cleanup occurs at predictable intervals,
+ * ideal for moderately active caches where memory usage is a concern.
+ *
+ * @param Allows configuring capacity, default TTL, eviction listener, and a pluggable eviction
+ * strategy. Call {@link #build()} to create the configured cache instance.
+ *
+ * @param
+ * The cache holds a fixed number of entries, defined by its capacity. When the cache is full and a
+ * new entry is added, one of the existing entries is selected at random and evicted to make space.
+ *
+ * Optionally, entries can have a time-to-live (TTL) in milliseconds. If a TTL is set, entries will
+ * automatically expire and be removed upon access or insertion attempts.
+ *
+ * Features:
+ * This constructor initializes the cache with the specified capacity and default TTL,
+ * sets up internal data structures (a {@code HashMap} for cache entries and an {@code ArrayList}
+ * for key tracking), and configures eviction and randomization behavior.
+ *
+ * @param builder the {@code Builder} object containing configuration parameters
+ */
+ private RRCache(Builder If the key is not present or the corresponding entry has expired, this method
+ * returns {@code null}. If an expired entry is found, it will be removed and the
+ * eviction listener (if any) will be notified. Cache hit-and-miss statistics are
+ * also updated accordingly.
+ *
+ * @param key the key whose associated value is to be returned; must not be {@code null}
+ * @return the cached value associated with the key, or {@code null} if not present or expired
+ * @throws IllegalArgumentException if {@code key} is {@code null}
+ */
+ public V get(K key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key must not be null");
+ }
+
+ lock.lock();
+ try {
+ evictionStrategy.onAccess(this);
+
+ CacheEntry The key may overwrite an existing entry. The actual insertion is delegated
+ * to the overloaded {@link #put(K, V, long)} method.
+ *
+ * @param key the key to cache the value under
+ * @param value the value to be cached
+ */
+ public void put(K key, V value) {
+ put(key, value, defaultTTL);
+ }
+
+ /**
+ * Adds a key-value pair to the cache with a specified time-to-live (TTL).
+ *
+ * If the key already exists, its value is updated and its TTL is reset. If the key
+ * does not exist and the cache is full, a random entry is evicted to make space.
+ * Expired entries are also cleaned up prior to any eviction. The eviction listener
+ * is notified when an entry gets evicted.
+ *
+ * @param key the key to associate with the cached value; must not be {@code null}
+ * @param value the value to be cached; must not be {@code null}
+ * @param ttlMillis the time-to-live for this entry in milliseconds; must be >= 0
+ * @throws IllegalArgumentException if {@code key} or {@code value} is {@code null}, or if {@code ttlMillis} is negative
+ */
+ public void put(K key, V value, long ttlMillis) {
+ if (key == null || value == null) {
+ throw new IllegalArgumentException("Key and value must not be null");
+ }
+ if (ttlMillis < 0) {
+ throw new IllegalArgumentException("TTL must be >= 0");
+ }
+
+ lock.lock();
+ try {
+ if (cache.containsKey(key)) {
+ cache.put(key, new CacheEntry<>(value, ttlMillis));
+ return;
+ }
+
+ evictExpired();
+
+ if (cache.size() >= capacity) {
+ int idx = random.nextInt(keys.size());
+ K evictKey = keys.remove(idx);
+ CacheEntry This method iterates through the list of cached keys and checks each associated
+ * entry for expiration. Expired entries are removed from both the key tracking list
+ * and the cache map. For each eviction, the eviction listener is notified.
+ */
+ private int evictExpired() {
+ Iterator This method deletes the key from both the cache map and the key tracking list.
+ *
+ * @param key the key to remove from the cache
+ */
+ private void removeKey(K key) {
+ cache.remove(key);
+ keys.remove(key);
+ }
+
+ /**
+ * Notifies the eviction listener, if one is registered, that a key-value pair has been evicted.
+ *
+ * If the {@code evictionListener} is not {@code null}, it is invoked with the provided key
+ * and value. Any exceptions thrown by the listener are caught and logged to standard error,
+ * preventing them from disrupting cache operations.
+ *
+ * @param key the key that was evicted
+ * @param value the value that was associated with the evicted key
+ */
+ private void notifyEviction(K key, V value) {
+ if (evictionListener != null) {
+ try {
+ evictionListener.accept(key, value);
+ } catch (Exception e) {
+ System.err.println("Eviction listener failed: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Returns the number of successful cache lookups (hits).
+ *
+ * @return the number of cache hits
+ */
+ public long getHits() {
+ lock.lock();
+ try {
+ return hits;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the number of failed cache lookups (misses), including expired entries.
+ *
+ * @return the number of cache misses
+ */
+ public long getMisses() {
+ lock.lock();
+ try {
+ return misses;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the current number of entries in the cache, excluding expired ones.
+ *
+ * @return the current cache size
+ */
+ public int size() {
+ lock.lock();
+ try {
+ int cachedSize = cache.size();
+ int evictedCount = evictionStrategy.onAccess(this);
+ if (evictedCount > 0) {
+ return cachedSize - evictedCount;
+ }
+
+ // This runs if periodic eviction does not occur
+ int count = 0;
+ for (Map.Entry The returned string includes the cache's capacity, current size (excluding expired entries),
+ * hit-and-miss counts, and a map of all non-expired key-value pairs. This method acquires a lock
+ * to ensure thread-safe access.
+ *
+ * @return a string summarizing the state of the cache
+ */
+ @Override
+ public String toString() {
+ lock.lock();
+ try {
+ Map Implementations decide whether and when to trigger {@link RRCache#evictExpired()} based
+ * on cache usage patterns. This allows for flexible eviction behaviour such as periodic cleanup,
+ * or no automatic cleanup.
+ *
+ * @param This deterministic strategy ensures cleanup occurs at predictable intervals,
+ * ideal for moderately active caches where memory usage is a concern.
+ *
+ * @param Allows configuring capacity, default TTL, random eviction behavior, eviction listener,
+ * and a pluggable eviction strategy. Call {@link #build()} to create the configured cache instance.
+ *
+ * @param Time Complexity: O(E log V), where E is the number of edges and V is the number of vertices.
- * This class utilizes a HashMap to efficiently count occurrences of elements in the first array,
- * allowing for an efficient lookup of common elements in the second array.
+ * This intersection includes duplicate values — meaning elements are included in the result
+ * as many times as they appear in both arrays (i.e., multiset intersection).
*
- * Example:
- *
+ * Example usage:
+ *
- * Note: The order of the returned list may vary since it depends on the order of elements
- * in the input arrays.
+ * Note: The order of elements in the returned list depends on the order in the second input array.
*
* The implementation contains:
- * - {@code length(Node head)}: A method to calculate the length of the linked list.
- * - {@code reverse(Node head, int count, int k)}: A helper method that reverses the nodes
+ * - {@code length(SinglyLinkedListNode head)}: A method to calculate the length of the linked list.
+ * - {@code reverse(SinglyLinkedListNode head, int count, int k)}: A helper method that reverses the nodes
* in the linked list in groups of k.
- * - {@code reverseKGroup(Node head, int k)}: The main method that initiates the reversal
+ * - {@code reverseKGroup(SinglyLinkedListNode head, int k)}: The main method that initiates the reversal
* process by calling the reverse method.
*
@@ -38,8 +38,8 @@ public class ReverseKGroup {
* @param head The head node of the linked list.
* @return The total number of nodes in the linked list.
*/
- public int length(Node head) {
- Node curr = head;
+ public int length(SinglyLinkedListNode head) {
+ SinglyLinkedListNode curr = head;
int count = 0;
while (curr != null) {
curr = curr.next;
@@ -56,14 +56,14 @@ public int length(Node head) {
* @param k The size of the group to reverse.
* @return The new head of the reversed linked list segment.
*/
- public Node reverse(Node head, int count, int k) {
+ public SinglyLinkedListNode reverse(SinglyLinkedListNode head, int count, int k) {
if (count < k) {
return head;
}
- Node prev = null;
+ SinglyLinkedListNode prev = null;
int count1 = 0;
- Node curr = head;
- Node next = null;
+ SinglyLinkedListNode curr = head;
+ SinglyLinkedListNode next = null;
while (curr != null && count1 < k) {
next = curr.next;
curr.next = prev;
@@ -85,7 +85,7 @@ public Node reverse(Node head, int count, int k) {
* @param k The size of the group to reverse.
* @return The head of the modified linked list after reversal.
*/
- public Node reverseKGroup(Node head, int k) {
+ public SinglyLinkedListNode reverseKGroup(SinglyLinkedListNode head, int k) {
int count = length(head);
return reverse(head, count, k);
}
diff --git a/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java b/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java
index 7676cc343653..47ee5397097c 100644
--- a/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java
+++ b/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java
@@ -38,12 +38,12 @@ public class RotateSinglyLinkedLists {
* @param k The number of positions to rotate the list to the right.
* @return The head of the rotated linked list.
*/
- public Node rotateRight(Node head, int k) {
+ public SinglyLinkedListNode rotateRight(SinglyLinkedListNode head, int k) {
if (head == null || head.next == null || k == 0) {
return head;
}
- Node curr = head;
+ SinglyLinkedListNode curr = head;
int len = 1;
while (curr.next != null) {
curr = curr.next;
diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java b/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java
index a40e9b2a1a66..4ac2de422595 100644
--- a/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java
+++ b/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java
@@ -30,7 +30,7 @@ public class SearchSinglyLinkedListRecursion extends SinglyLinkedList {
* @param key the integer value to be searched for.
* @return {@code true} if the value `key` is present in the list; otherwise, {@code false}.
*/
- private boolean searchRecursion(Node node, int key) {
+ private boolean searchRecursion(SinglyLinkedListNode node, int key) {
return (node != null && (node.value == key || searchRecursion(node.next, key)));
}
diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java
index eb6cdf48f58b..ff4af4437cc7 100644
--- a/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java
+++ b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java
@@ -12,7 +12,7 @@ public class SinglyLinkedList implements Iterable This algorithm determines whether any subset of a given array sums up to a specific target value. Time Complexity: O(n * sum) Space Complexity: O(sum)
+ * For example, the generic root of 12345 is calculated as:
+ * 1 + 2 + 3 + 4 + 5 = 15,
+ * then 1 + 5 = 6, so the generic root is 6.
+ *
+ * Reference:
+ * https://technotip.com/6774/c-program-to-find-generic-root-of-a-number/
*/
public final class GenericRoot {
+
+ private static final int BASE = 10;
+
private GenericRoot() {
}
- private static int base = 10;
-
+ /**
+ * Computes the sum of the digits of a non-negative integer in base 10.
+ *
+ * @param n non-negative integer
+ * @return sum of digits of {@code n}
+ */
private static int sumOfDigits(final int n) {
assert n >= 0;
- if (n < base) {
+ if (n < BASE) {
return n;
}
- return n % base + sumOfDigits(n / base);
+ return (n % BASE) + sumOfDigits(n / BASE);
}
+ /**
+ * Computes the generic root (repeated digital sum) of an integer.
+ * For negative inputs, the absolute value is used.
+ *
+ * @param n integer input
+ * @return generic root of {@code n}
+ */
public static int genericRoot(final int n) {
- if (n < 0) {
- return genericRoot(-n);
- }
- if (n > base) {
- return genericRoot(sumOfDigits(n));
+ int number = Math.abs(n);
+ if (number < BASE) {
+ return number;
}
- return n;
+ return genericRoot(sumOfDigits(number));
}
}
diff --git a/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java b/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java
new file mode 100644
index 000000000000..4e962722ba88
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java
@@ -0,0 +1,30 @@
+package com.thealgorithms.maths;
+
+import static com.thealgorithms.maths.Prime.PrimeCheck.isPrime;
+
+/**
+ * This is a representation of the unsolved problem of Goldbach's Projection, according to which every
+ * even natural number greater than 2 can be written as the sum of 2 prime numbers
+ * More info: https://en.wikipedia.org/wiki/Goldbach%27s_conjecture
+ * @author Vasilis Sarantidis (https://github.com/BILLSARAN)
+ */
+
+public final class GoldbachConjecture {
+ private GoldbachConjecture() {
+ }
+ public record Result(int number1, int number2) {
+ }
+
+ public static Result getPrimeSum(int number) {
+ if (number <= 2 || number % 2 != 0) {
+ throw new IllegalArgumentException("Number must be even and greater than 2.");
+ }
+
+ for (int i = 0; i <= number / 2; i++) {
+ if (isPrime(i) && isPrime(number - i)) {
+ return new Result(i, number - i);
+ }
+ }
+ throw new IllegalStateException("No valid prime sum found."); // Should not occur
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/MathBuilder.java b/src/main/java/com/thealgorithms/maths/MathBuilder.java
new file mode 100644
index 000000000000..1cf3d8b7fc9a
--- /dev/null
+++ b/src/main/java/com/thealgorithms/maths/MathBuilder.java
@@ -0,0 +1,503 @@
+package com.thealgorithms.maths;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Author: Sadiul Hakim : https://github.com/sadiul-hakim
+ * Profession: Backend Engineer
+ * Date: Oct 20, 2024
+ */
+public final class MathBuilder {
+ private final double result;
+
+ private MathBuilder(Builder builder) {
+ this.result = builder.number;
+ }
+
+ // Returns final result
+ public double get() {
+ return result;
+ }
+
+ // Return result in long
+ public long toLong() {
+ try {
+ if (Double.isNaN(result)) {
+ throw new IllegalArgumentException("Cannot convert NaN to long!");
+ }
+ if (result == Double.POSITIVE_INFINITY) {
+ return Long.MAX_VALUE;
+ }
+ if (result == Double.NEGATIVE_INFINITY) {
+ return Long.MIN_VALUE;
+ }
+ if (result > Long.MAX_VALUE) {
+ return Long.MAX_VALUE;
+ }
+ if (result < Long.MIN_VALUE) {
+ return Long.MIN_VALUE;
+ }
+ return Math.round(result);
+ } catch (Exception ex) {
+ return 0;
+ }
+ }
+
+ public static class Builder {
+ private double number;
+ private double sideNumber;
+ private boolean inParenthesis;
+ private double memory = 0;
+
+ public Builder() {
+ number = 0;
+ }
+
+ public Builder(double num) {
+ number = num;
+ }
+
+ public Builder add(double num) {
+ if (inParenthesis) {
+ sideNumber += num;
+ } else {
+ number += num;
+ }
+ return this;
+ }
+
+ // Takes a number and a condition, only does the operation if condition is true.
+ public Builder addIf(double num, BiFunction
+ * The mode of an array is the integer value(s) that occur most frequently.
+ * If multiple values have the same highest frequency, all such values are returned.
+ *
+ * If the input array is empty, this method returns {@code null}.
+ * If multiple numbers share the highest frequency, all are returned in the result array.
+ * Matrix multiplication takes two 2D arrays (matrices) as input and
+ * produces their product, following the mathematical definition of
+ * matrix multiplication.
+ *
+ * For more details:
+ * https://www.geeksforgeeks.org/java/java-program-to-multiply-two-matrices-of-any-size/
+ * https://en.wikipedia.org/wiki/Matrix_multiplication
+ *
+ * Time Complexity: O(n^3) – where n is the dimension of the matrices
+ * (assuming square matrices for simplicity).
+ *
+ * Space Complexity: O(n^2) – for storing the result matrix.
+ *
+ *
+ * @author Nishitha Wihala Pitigala
+ *
+ */
+
+public final class MatrixMultiplication {
+ private MatrixMultiplication() {
+ }
+
+ /**
+ * Multiplies two matrices.
+ *
+ * @param matrixA the first matrix rowsA x colsA
+ * @param matrixB the second matrix rowsB x colsB
+ * @return the product of the two matrices rowsA x colsB
+ * @throws IllegalArgumentException if the matrices cannot be multiplied
+ */
+ public static double[][] multiply(double[][] matrixA, double[][] matrixB) {
+ // Check the input matrices are not null
+ if (matrixA == null || matrixB == null) {
+ throw new IllegalArgumentException("Input matrices cannot be null");
+ }
+
+ // Check for empty matrices
+ if (matrixA.length == 0 || matrixB.length == 0 || matrixA[0].length == 0 || matrixB[0].length == 0) {
+ throw new IllegalArgumentException("Input matrices must not be empty");
+ }
+
+ // Validate the matrix dimensions
+ if (matrixA[0].length != matrixB.length) {
+ throw new IllegalArgumentException("Matrices cannot be multiplied: incompatible dimensions.");
+ }
+
+ int rowsA = matrixA.length;
+ int colsA = matrixA[0].length;
+ int colsB = matrixB[0].length;
+
+ // Initialize the result matrix with zeros
+ double[][] result = new double[rowsA][colsB];
+
+ // Perform matrix multiplication
+ for (int i = 0; i < rowsA; i++) {
+ for (int j = 0; j < colsB; j++) {
+ for (int k = 0; k < colsA; k++) {
+ result[i][j] += matrixA[i][k] * matrixB[k][j];
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/matrix/MatrixRank.java
similarity index 77%
rename from src/main/java/com/thealgorithms/maths/MatrixRank.java
rename to src/main/java/com/thealgorithms/matrix/MatrixRank.java
index 7a628b92dccb..6692b6c37c60 100644
--- a/src/main/java/com/thealgorithms/maths/MatrixRank.java
+++ b/src/main/java/com/thealgorithms/matrix/MatrixRank.java
@@ -1,4 +1,6 @@
-package com.thealgorithms.maths;
+package com.thealgorithms.matrix;
+
+import static com.thealgorithms.matrix.utils.MatrixUtil.validateInputMatrix;
/**
* This class provides a method to compute the rank of a matrix.
@@ -63,47 +65,6 @@ private static double[][] deepCopy(double[][] matrix) {
return matrixCopy;
}
- private static void validateInputMatrix(double[][] matrix) {
- if (matrix == null) {
- throw new IllegalArgumentException("The input matrix cannot be null");
- }
- if (matrix.length == 0) {
- throw new IllegalArgumentException("The input matrix cannot be empty");
- }
- if (!hasValidRows(matrix)) {
- throw new IllegalArgumentException("The input matrix cannot have null or empty rows");
- }
- if (isJaggedMatrix(matrix)) {
- throw new IllegalArgumentException("The input matrix cannot be jagged");
- }
- }
-
- private static boolean hasValidRows(double[][] matrix) {
- for (double[] row : matrix) {
- if (row == null || row.length == 0) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * @brief Checks if the input matrix is a jagged matrix.
- * Jagged matrix is a matrix where the number of columns in each row is not the same.
- *
- * @param matrix The input matrix
- * @return True if the input matrix is a jagged matrix, false otherwise
- */
- private static boolean isJaggedMatrix(double[][] matrix) {
- int numColumns = matrix[0].length;
- for (double[] row : matrix) {
- if (row.length != numColumns) {
- return true;
- }
- }
- return false;
- }
-
/**
* @brief The pivot row is the row in the matrix that is used to eliminate other rows and reduce the matrix to its row echelon form.
* The pivot row is selected as the first row (from top to bottom) where the value in the current column (the pivot column) is not zero.
diff --git a/src/main/java/com/thealgorithms/misc/MatrixTranspose.java b/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java
similarity index 97%
rename from src/main/java/com/thealgorithms/misc/MatrixTranspose.java
rename to src/main/java/com/thealgorithms/matrix/MatrixTranspose.java
index 743682780b01..f91ebc10b8a9 100644
--- a/src/main/java/com/thealgorithms/misc/MatrixTranspose.java
+++ b/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.misc;
+package com.thealgorithms.matrix;
/**
*
diff --git a/src/main/java/com/thealgorithms/misc/MedianOfMatrix.java b/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java
similarity index 54%
rename from src/main/java/com/thealgorithms/misc/MedianOfMatrix.java
rename to src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java
index edeedbbee540..1ec977af07c6 100644
--- a/src/main/java/com/thealgorithms/misc/MedianOfMatrix.java
+++ b/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.misc;
+package com.thealgorithms.matrix;
import java.util.ArrayList;
import java.util.Collections;
@@ -14,19 +14,19 @@ private MedianOfMatrix() {
}
public static int median(Iterable
+ * This OVERWRITES the input matrix to save on memory
+ *
+ * @param matrix - a square matrix of doubles
+ * @param constants - an array of constant
+ * @return solutions
+ */
+ public static double[] solveSystem(double[][] matrix, double[] constants) {
+ final double tol = 0.00000001; // tolerance for round off
+ for (int k = 0; k < matrix.length - 1; k++) {
+ // find the largest value in column (to avoid zero pivots)
+ double maxVal = Math.abs(matrix[k][k]);
+ int maxIdx = k;
+ for (int j = k + 1; j < matrix.length; j++) {
+ if (Math.abs(matrix[j][k]) > maxVal) {
+ maxVal = matrix[j][k];
+ maxIdx = j;
+ }
+ }
+ if (Math.abs(maxVal) < tol) {
+ // hope the matrix works out
+ continue;
+ }
+ // swap rows
+ double[] temp = matrix[k];
+ matrix[k] = matrix[maxIdx];
+ matrix[maxIdx] = temp;
+ double tempConst = constants[k];
+ constants[k] = constants[maxIdx];
+ constants[maxIdx] = tempConst;
+ for (int i = k + 1; i < matrix.length; i++) {
+ // compute multipliers and save them in the column
+ matrix[i][k] /= matrix[k][k];
+ for (int j = k + 1; j < matrix.length; j++) {
+ matrix[i][j] -= matrix[i][k] * matrix[k][j];
+ }
+ constants[i] -= matrix[i][k] * constants[k];
+ }
+ }
+ // back substitution
+ double[] x = new double[constants.length];
+ System.arraycopy(constants, 0, x, 0, constants.length);
+ for (int i = matrix.length - 1; i >= 0; i--) {
+ double sum = 0;
+ for (int j = i + 1; j < matrix.length; j++) {
+ sum += matrix[i][j] * x[j];
+ }
+ x[i] = constants[i] - sum;
+ if (Math.abs(matrix[i][i]) > tol) {
+ x[i] /= matrix[i][i];
+ } else {
+ throw new IllegalArgumentException("Matrix was found to be singular");
+ }
+ }
+ return x;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java b/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java
new file mode 100644
index 000000000000..85852713b9ba
--- /dev/null
+++ b/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java
@@ -0,0 +1,42 @@
+package com.thealgorithms.matrix.matrixexponentiation;
+
+import com.thealgorithms.matrix.utils.MatrixUtil;
+import java.math.BigDecimal;
+
+/**
+ * @author Anirudh Buvanesh (https://github.com/anirudhb11) For more information
+ * see https://www.geeksforgeeks.org/matrix-exponentiation/
+ *
+ */
+public final class Fibonacci {
+ private Fibonacci() {
+ }
+
+ // Exponentiation matrix for Fibonacci sequence
+ private static final BigDecimal ONE = BigDecimal.valueOf(1);
+ private static final BigDecimal ZERO = BigDecimal.valueOf(0);
+
+ private static final BigDecimal[][] FIB_MATRIX = {{ONE, ONE}, {ONE, ZERO}};
+ private static final BigDecimal[][] IDENTITY_MATRIX = {{ONE, ZERO}, {ZERO, ONE}};
+
+ /**
+ * Calculates the fibonacci number using matrix exponentiaition technique
+ *
+ * @param n The input n for which we have to determine the fibonacci number
+ * Outputs the nth * fibonacci number
+ * @return a 2 X 1 array as { {F_n+1}, {F_n} }
+ */
+ public static BigDecimal[][] fib(int n) {
+ if (n == 0) {
+ return IDENTITY_MATRIX;
+ } else {
+ BigDecimal[][] cachedResult = fib(n / 2);
+ BigDecimal[][] matrixExpResult = MatrixUtil.multiply(cachedResult, cachedResult).get();
+ if (n % 2 == 0) {
+ return matrixExpResult;
+ } else {
+ return MatrixUtil.multiply(FIB_MATRIX, matrixExpResult).get();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/MatrixUtil.java b/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java
similarity index 62%
rename from src/main/java/com/thealgorithms/maths/MatrixUtil.java
rename to src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java
index 7e462f92e185..5ff9e37f6b9a 100644
--- a/src/main/java/com/thealgorithms/maths/MatrixUtil.java
+++ b/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.maths;
+package com.thealgorithms.matrix.utils;
import java.math.BigDecimal;
import java.util.Optional;
@@ -10,6 +10,7 @@
* @date: 31 October 2021 (Sunday)
*/
public final class MatrixUtil {
+
private MatrixUtil() {
}
@@ -18,11 +19,52 @@ private static boolean isValid(final BigDecimal[][] matrix) {
}
private static boolean hasEqualSizes(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) {
- return (isValid(matrix1) && isValid(matrix2) && matrix1.length == matrix2.length && matrix1[0].length == matrix2[0].length);
+ return isValid(matrix1) && isValid(matrix2) && matrix1.length == matrix2.length && matrix1[0].length == matrix2[0].length;
}
private static boolean canMultiply(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) {
- return (isValid(matrix1) && isValid(matrix2) && matrix1[0].length == matrix2.length);
+ return isValid(matrix1) && isValid(matrix2) && matrix1[0].length == matrix2.length;
+ }
+
+ public static void validateInputMatrix(double[][] matrix) {
+ if (matrix == null) {
+ throw new IllegalArgumentException("The input matrix cannot be null");
+ }
+ if (matrix.length == 0) {
+ throw new IllegalArgumentException("The input matrix cannot be empty");
+ }
+ if (!hasValidRows(matrix)) {
+ throw new IllegalArgumentException("The input matrix cannot have null or empty rows");
+ }
+ if (isJaggedMatrix(matrix)) {
+ throw new IllegalArgumentException("The input matrix cannot be jagged");
+ }
+ }
+
+ private static boolean hasValidRows(double[][] matrix) {
+ for (double[] row : matrix) {
+ if (row == null || row.length == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @brief Checks if the input matrix is a jagged matrix.
+ * Jagged matrix is a matrix where the number of columns in each row is not the same.
+ *
+ * @param matrix The input matrix
+ * @return True if the input matrix is a jagged matrix, false otherwise
+ */
+ private static boolean isJaggedMatrix(double[][] matrix) {
+ int numColumns = matrix[0].length;
+ for (double[] row : matrix) {
+ if (row.length != numColumns) {
+ return true;
+ }
+ }
+ return false;
}
private static Optional
* Link: https://www.geeksforgeeks.org/two-pointers-technique/
*/
-final class TwoPointers {
+public final class TwoPointers {
+
private TwoPointers() {
}
/**
- * Given a sorted array arr (sorted in ascending order), find if there exists
- * any pair of elements such that their sum is equal to the key.
+ * Checks whether there exists a pair of elements in a sorted array whose sum equals the specified key.
*
- * @param arr the array containing elements (must be sorted in ascending order)
- * @param key the number to search
- * @return {@code true} if there exists a pair of elements, {@code false} otherwise.
+ * @param arr a sorted array of integers in ascending order (must not be null)
+ * @param key the target sum to find
+ * @return {@code true} if there exists at least one pair whose sum equals {@code key}, {@code false} otherwise
+ * @throws IllegalArgumentException if {@code arr} is {@code null}
*/
public static boolean isPairedSum(int[] arr, int key) {
- int i = 0; // index of the first element
- int j = arr.length - 1; // index of the last element
+ if (arr == null) {
+ throw new IllegalArgumentException("Input array must not be null.");
+ }
+
+ int left = 0;
+ int right = arr.length - 1;
+
+ while (left < right) {
+ int sum = arr[left] + arr[right];
- while (i < j) {
- int sum = arr[i] + arr[j];
if (sum == key) {
return true;
- } else if (sum < key) {
- i++;
+ }
+ if (sum < key) {
+ left++;
} else {
- j--;
+ right--;
}
}
return false;
diff --git a/src/main/java/com/thealgorithms/others/Sudoku.java b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java
similarity index 99%
rename from src/main/java/com/thealgorithms/others/Sudoku.java
rename to src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java
index 0e88aee46f4d..fce665c4de00 100644
--- a/src/main/java/com/thealgorithms/others/Sudoku.java
+++ b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.others;
+package com.thealgorithms.puzzlesandgames;
/**
* A class that provides methods to solve Sudoku puzzles of any n x n size
diff --git a/src/main/java/com/thealgorithms/others/TowerOfHanoi.java b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java
similarity index 98%
rename from src/main/java/com/thealgorithms/others/TowerOfHanoi.java
rename to src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java
index 7017ed03f843..72e9a14ac070 100644
--- a/src/main/java/com/thealgorithms/others/TowerOfHanoi.java
+++ b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.others;
+package com.thealgorithms.puzzlesandgames;
import java.util.List;
diff --git a/src/main/java/com/thealgorithms/misc/WordBoggle.java b/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java
similarity index 98%
rename from src/main/java/com/thealgorithms/misc/WordBoggle.java
rename to src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java
index 8b629d68209b..ca1430f744ab 100644
--- a/src/main/java/com/thealgorithms/misc/WordBoggle.java
+++ b/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java
@@ -1,4 +1,4 @@
-package com.thealgorithms.misc;
+package com.thealgorithms.puzzlesandgames;
import java.util.ArrayList;
import java.util.HashMap;
@@ -8,9 +8,9 @@
import java.util.Set;
public final class WordBoggle {
+
private WordBoggle() {
}
-
/**
* O(nm * 8^s + ws) time where n = width of boggle board, m = height of
* boggle board, s = length of longest word in string array, w = length of
diff --git a/src/main/java/com/thealgorithms/randomized/KargerMinCut.java b/src/main/java/com/thealgorithms/randomized/KargerMinCut.java
new file mode 100644
index 000000000000..14f1f97450a0
--- /dev/null
+++ b/src/main/java/com/thealgorithms/randomized/KargerMinCut.java
@@ -0,0 +1,195 @@
+package com.thealgorithms.randomized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Implementation of Karger's Minimum Cut algorithm.
+ *
+ * Karger's algorithm is a randomized algorithm to compute the minimum cut of a connected graph.
+ * A minimum cut is the smallest set of edges that, if removed, would split the graph into two
+ * disconnected components.
+ *
+ * The algorithm works by repeatedly contracting random edges in the graph until only two
+ * nodes remain. The edges between these two nodes represent a cut. By running the algorithm
+ * multiple times and keeping track of the smallest cut found, the probability of finding the
+ * true minimum cut increases.
+ *
+ * Key steps of the algorithm:
+ *
+ * See more: Karger's algorithm
+ *
+ * @author MuhammadEzzatHBK
+ */
+public final class KargerMinCut {
+
+ /**
+ * Output of the Karger algorithm.
+ *
+ * @param first The first set of nodes in the cut.
+ * @param second The second set of nodes in the cut.
+ * @param minCut The size of the minimum cut.
+ */
+ public record KargerOutput(Set This class estimates the value of definite integrals using randomized sampling,
+ * also known as the Monte Carlo method. It is particularly effective for:
+ * The core idea is to sample random points uniformly from the integration domain,
+ * evaluate the function at those points, and compute the scaled average to estimate the integral.
+ *
+ * For a one-dimensional integral over [a, b], the approximation is the function range (b-a),
+ * multiplied by the function average result for a random sample.
+ * See more: Monte Carlo Integration
+ *
+ * @author: MuhammadEzzatHBK
+ */
+
+public final class MonteCarloIntegration {
+
+ private MonteCarloIntegration() {
+ }
+
+ /**
+ * Approximates the definite integral of a given function over a specified
+ * interval using the Monte Carlo method with a fixed random seed for
+ * reproducibility.
+ *
+ * @param fx the function to integrate
+ * @param a the lower bound of the interval
+ * @param b the upper bound of the interval
+ * @param n the number of random samples to use
+ * @param seed the seed for the random number generator
+ * @return the approximate value of the integral
+ */
+ public static double approximate(Function For example, the string "ab" will produce: ["ab", "a", "b", ""]
+ */
public final class GenerateSubsets {
private GenerateSubsets() {
- throw new UnsupportedOperationException("Utility class");
}
+ /**
+ * Generates all subsets (power set) of the given string using recursion.
+ *
+ * @param str the input string to generate subsets for
+ * @return a list of all subsets of the input string
+ */
public static List
+ *
+ *
+ * @param
+ *
+ *
+ * @param
+ *
+ *
+ * @see Disjoint Set Union (Wikipedia)
*/
public class DisjointSetUnion
+ * The algorithm uses a {@link java.util.HashMap} to count occurrences of elements in the first array,
+ * then iterates through the second array to collect common elements based on these counts.
+ *
{@code
* int[] array1 = {1, 2, 2, 1};
* int[] array2 = {2, 2};
- * List
+ * List> adjacencyList;
+
+ public Graph(int numNodes) {
+ adjacencyList = new ArrayList<>();
+ for (int i = 0; i < numNodes; i++) {
+ adjacencyList.add(new ArrayList<>());
+ }
+ }
+
+ /**
+ * Adds an edge to the graph.
+ * @param from the starting node
+ * @param to the ending node
+ * @param cost the cost of the edge
+ * @param resource the resource required to traverse the edge
+ */
+ public void addEdge(int from, int to, int cost, int resource) {
+ adjacencyList.get(from).add(new Edge(from, to, cost, resource));
+ }
+
+ /**
+ * Gets the edges that are adjacent to a given node.
+ * @param node the node to get the edges for
+ * @return the list of edges adjacent to the node
+ */
+ public List
> permutations = generatePermutations(cities);
+ int minDistance = Integer.MAX_VALUE;
+
+ for (List
> generatePermutations(List
> permutations = new ArrayList<>();
+ permute(cities, 0, permutations);
+ return permutations;
+ }
+
+ /**
+ * Recursively generates permutations using backtracking.
+ *
+ * @param arr The list of cities.
+ * @param k The current index in the permutation process.
+ * @param output The list to store generated permutations.
+ */
+ private static void permute(List
> output) {
+ if (k == arr.size()) {
+ output.add(new ArrayList<>(arr));
+ return;
+ }
+ for (int i = k; i < arr.size(); i++) {
+ Collections.swap(arr, i, k);
+ permute(arr, k + 1, output);
+ Collections.swap(arr, i, k);
+ }
+ }
+
+ /**
+ * Solves the Traveling Salesman Problem (TSP) using dynamic programming with the Held-Karp algorithm.
+ *
+ * @param distanceMatrix A square matrix where element [i][j] represents the distance from city i to city j.
+ * @return The shortest possible route distance visiting all cities exactly once and returning to the starting city.
+ * @throws IllegalArgumentException if the input matrix is not square.
+ */
+ public static int dynamicProgramming(int[][] distanceMatrix) {
+ if (distanceMatrix.length == 0) {
+ return 0;
+ }
+ int n = distanceMatrix.length;
+
+ for (int[] row : distanceMatrix) {
+ if (row.length != n) {
+ throw new IllegalArgumentException("Matrix must be square");
+ }
+ }
+
+ int[][] dp = new int[n][1 << n];
+ for (int[] row : dp) {
+ Arrays.fill(row, Integer.MAX_VALUE);
+ }
+ dp[0][1] = 0;
+
+ for (int mask = 1; mask < (1 << n); mask++) {
+ for (int u = 0; u < n; u++) {
+ if ((mask & (1 << u)) == 0 || dp[u][mask] == Integer.MAX_VALUE) {
+ continue;
+ }
+ for (int v = 0; v < n; v++) {
+ if ((mask & (1 << v)) != 0 || distanceMatrix[u][v] == Integer.MAX_VALUE) {
+ continue;
+ }
+ int newMask = mask | (1 << v);
+ dp[v][newMask] = Math.min(dp[v][newMask], dp[u][mask] + distanceMatrix[u][v]);
+ }
+ }
+ }
+
+ int minDistance = Integer.MAX_VALUE;
+ int fullMask = (1 << n) - 1;
+ for (int i = 1; i < n; i++) {
+ if (dp[i][fullMask] != Integer.MAX_VALUE && distanceMatrix[i][0] != Integer.MAX_VALUE) {
+ minDistance = Math.min(minDistance, dp[i][fullMask] + distanceMatrix[i][0]);
+ }
+ }
+
+ return minDistance == Integer.MAX_VALUE ? 0 : minDistance;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteMax.java b/src/main/java/com/thealgorithms/maths/AbsoluteMax.java
index d0c3db3790a3..c32a408b6609 100644
--- a/src/main/java/com/thealgorithms/maths/AbsoluteMax.java
+++ b/src/main/java/com/thealgorithms/maths/AbsoluteMax.java
@@ -17,7 +17,7 @@ public static int getMaxValue(int... numbers) {
}
int absMax = numbers[0];
for (int i = 1; i < numbers.length; i++) {
- if (Math.abs(numbers[i]) > Math.abs(absMax)) {
+ if (Math.abs(numbers[i]) > Math.abs(absMax) || (Math.abs(numbers[i]) == Math.abs(absMax) && numbers[i] > absMax)) {
absMax = numbers[i];
}
}
diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteMin.java b/src/main/java/com/thealgorithms/maths/AbsoluteMin.java
index 1ffe6d2e81bc..1b9575a330dd 100644
--- a/src/main/java/com/thealgorithms/maths/AbsoluteMin.java
+++ b/src/main/java/com/thealgorithms/maths/AbsoluteMin.java
@@ -19,7 +19,7 @@ public static int getMinValue(int... numbers) {
var absMinWrapper = new Object() { int value = numbers[0]; };
- Arrays.stream(numbers).skip(1).filter(number -> Math.abs(number) < Math.abs(absMinWrapper.value)).forEach(number -> absMinWrapper.value = number);
+ Arrays.stream(numbers).skip(1).filter(number -> Math.abs(number) <= Math.abs(absMinWrapper.value)).forEach(number -> absMinWrapper.value = Math.min(absMinWrapper.value, number));
return absMinWrapper.value;
}
diff --git a/src/main/java/com/thealgorithms/maths/AliquotSum.java b/src/main/java/com/thealgorithms/maths/AliquotSum.java
index 0dbc58bed605..996843b56826 100644
--- a/src/main/java/com/thealgorithms/maths/AliquotSum.java
+++ b/src/main/java/com/thealgorithms/maths/AliquotSum.java
@@ -56,7 +56,7 @@ public static int getAliquotSum(int n) {
// if n is a perfect square then its root was added twice in above loop, so subtracting root
// from sum
if (root == (int) root) {
- sum -= root;
+ sum -= (int) root;
}
return sum;
}
diff --git a/src/main/java/com/thealgorithms/maths/Armstrong.java b/src/main/java/com/thealgorithms/maths/Armstrong.java
index ff4ae027a0b7..9a7a014ec99f 100644
--- a/src/main/java/com/thealgorithms/maths/Armstrong.java
+++ b/src/main/java/com/thealgorithms/maths/Armstrong.java
@@ -10,6 +10,7 @@
* An Armstrong number is often called a Narcissistic number.
*
* @author satyabarghav
+ * @modifier rahul katteda - (13/01/2025) - [updated the logic for getting total number of digits]
*/
public class Armstrong {
@@ -20,14 +21,16 @@ public class Armstrong {
* @return {@code true} if the given number is an Armstrong number, {@code false} otherwise
*/
public boolean isArmstrong(int number) {
+ if (number < 0) {
+ return false; // Negative numbers cannot be Armstrong numbers
+ }
long sum = 0;
- String temp = Integer.toString(number); // Convert the given number to a string
- int power = temp.length(); // Extract the length of the number (number of digits)
+ int totalDigits = (int) Math.log10(number) + 1; // get the length of the number (number of digits)
long originalNumber = number;
while (originalNumber > 0) {
long digit = originalNumber % 10;
- sum += (long) Math.pow(digit, power); // The digit raised to the power of the number of digits and added to the sum.
+ sum += (long) Math.pow(digit, totalDigits); // The digit raised to the power of total number of digits and added to the sum.
originalNumber /= 10;
}
diff --git a/src/main/java/com/thealgorithms/maths/Average.java b/src/main/java/com/thealgorithms/maths/Average.java
index 6b9c20162da1..a550a7f6504d 100644
--- a/src/main/java/com/thealgorithms/maths/Average.java
+++ b/src/main/java/com/thealgorithms/maths/Average.java
@@ -37,7 +37,7 @@ public static double average(double[] numbers) {
* @return the average of the given numbers
* @throws IllegalArgumentException if the input array is {@code null} or empty
*/
- public static double average(int[] numbers) {
+ public static long average(int[] numbers) {
if (numbers == null || numbers.length == 0) {
throw new IllegalArgumentException("Numbers array cannot be empty or null");
}
@@ -45,6 +45,6 @@ public static double average(int[] numbers) {
for (int number : numbers) {
sum += number;
}
- return (double) (sum / numbers.length);
+ return sum / numbers.length;
}
}
diff --git a/src/main/java/com/thealgorithms/maths/Ceil.java b/src/main/java/com/thealgorithms/maths/Ceil.java
index aacb9d969950..28804eb1c569 100644
--- a/src/main/java/com/thealgorithms/maths/Ceil.java
+++ b/src/main/java/com/thealgorithms/maths/Ceil.java
@@ -1,23 +1,34 @@
package com.thealgorithms.maths;
+/**
+ * Utility class to compute the ceiling of a given number.
+ */
public final class Ceil {
+
private Ceil() {
}
/**
- * Returns the smallest (closest to negative infinity)
+ * Returns the smallest double value that is greater than or equal to the input.
+ * Equivalent to mathematical ⌈x⌉ (ceiling function).
*
- * @param number the number
- * @return the smallest (closest to negative infinity) of given
- * {@code number}
+ * @param number the number to ceil
+ * @return the smallest double greater than or equal to {@code number}
*/
public static double ceil(double number) {
- if (number - (int) number == 0) {
+ if (Double.isNaN(number) || Double.isInfinite(number) || number == 0.0 || number < Integer.MIN_VALUE || number > Integer.MAX_VALUE) {
return number;
- } else if (number - (int) number > 0) {
- return (int) (number + 1);
+ }
+
+ if (number < 0.0 && number > -1.0) {
+ return -0.0;
+ }
+
+ long intPart = (long) number;
+ if (number > 0 && number != intPart) {
+ return intPart + 1.0;
} else {
- return (int) number;
+ return intPart;
}
}
}
diff --git a/src/main/java/com/thealgorithms/maths/Convolution.java b/src/main/java/com/thealgorithms/maths/Convolution.java
index 93e103f8c7cf..a86ecabce933 100644
--- a/src/main/java/com/thealgorithms/maths/Convolution.java
+++ b/src/main/java/com/thealgorithms/maths/Convolution.java
@@ -23,24 +23,21 @@ public static double[] convolution(double[] a, double[] b) {
double[] convolved = new double[a.length + b.length - 1];
/*
- The discrete convolution of two signals A and B is defined as:
-
- A.length
- C[i] = Σ (A[k]*B[i-k])
- k=0
-
- It's obvious that: 0 <= k <= A.length , 0 <= i <= A.length + B.length - 2 and 0 <= i-k <=
- B.length - 1 From the last inequality we get that: i - B.length + 1 <= k <= i and thus we get
- the conditions below.
+ * Discrete convolution formula:
+ * C[i] = Σ A[k] * B[i - k]
+ * where k ranges over valid indices so that both A[k] and B[i-k] are in bounds.
*/
+
for (int i = 0; i < convolved.length; i++) {
- convolved[i] = 0;
- int k = Math.max(i - b.length + 1, 0);
+ double sum = 0;
+ int kStart = Math.max(0, i - b.length + 1);
+ int kEnd = Math.min(i, a.length - 1);
- while (k < i + 1 && k < a.length) {
- convolved[i] += a[k] * b[i - k];
- k++;
+ for (int k = kStart; k <= kEnd; k++) {
+ sum += a[k] * b[i - k];
}
+
+ convolved[i] = sum;
}
return convolved;
diff --git a/src/main/java/com/thealgorithms/maths/DudeneyNumber.java b/src/main/java/com/thealgorithms/maths/DudeneyNumber.java
index acf1e55d49c8..37f28e188663 100644
--- a/src/main/java/com/thealgorithms/maths/DudeneyNumber.java
+++ b/src/main/java/com/thealgorithms/maths/DudeneyNumber.java
@@ -1,11 +1,11 @@
+package com.thealgorithms.maths;
+
/**
* A number is said to be Dudeney if the sum of the digits, is the cube root of the entered number.
* Example- Let the number be 512, its sum of digits is 5+1+2=8. The cube root of 512 is also 8.
* Since, the sum of the digits is equal to the cube root of the entered number;
* it is a Dudeney Number.
*/
-package com.thealgorithms.maths;
-
public final class DudeneyNumber {
private DudeneyNumber() {
}
diff --git a/src/main/java/com/thealgorithms/maths/GenericRoot.java b/src/main/java/com/thealgorithms/maths/GenericRoot.java
index 07f4756f93f8..e13efe5a77e0 100644
--- a/src/main/java/com/thealgorithms/maths/GenericRoot.java
+++ b/src/main/java/com/thealgorithms/maths/GenericRoot.java
@@ -1,30 +1,48 @@
package com.thealgorithms.maths;
-/*
- * Algorithm explanation:
- * https://technotip.com/6774/c-program-to-find-generic-root-of-a-number/#:~:text=Generic%20Root%3A%20of%20a%20number,get%20a%20single%2Ddigit%20output.&text=For%20Example%3A%20If%20user%20input,%2B%204%20%2B%205%20%3D%2015.
+/**
+ * Calculates the generic root (repeated digital sum) of a non-negative integer.
+ *
> matrix) {
- // Flatten the matrix into a 1D list
- List
+ *
+ *
+ *
+ *
+ *