diff --git a/pysnippets/Memoisation/Importance-of-memoisation copy.md b/pysnippets/Memoisation/Importance-of-memoisation copy.md deleted file mode 100644 index 747bad56..00000000 --- a/pysnippets/Memoisation/Importance-of-memoisation copy.md +++ /dev/null @@ -1,67 +0,0 @@ -## Memoization - -Memoization is a powerful optimization technique primarily used in algorithms that involve recursion. It works by storing the results of expensive function calls and reusing those results when the same inputs occur again. This approach significantly improves the performance of recursive algorithms, particularly in problems involving overlapping subproblems, such as those commonly found in dynamic programming. - -### Importance of Memoization - -1. **Efficiency Improvement**: - - By caching results, memoization reduces the time complexity of algorithms, converting exponential time complexity into polynomial time complexity in many cases. - -2. **Reducing Redundant Computations**: - - Memoization avoids unnecessary recalculations of previously computed results, leading to significant savings in computational resources. - -3. **Facilitating Recursive Solutions**: - - It enables straightforward recursive solutions to problems that would otherwise be inefficient, allowing programmers to use a more intuitive approach without sacrificing performance. - -4. **Dynamic Programming Foundation**: - - Memoization is a critical concept in dynamic programming, serving as the basis for many dynamic programming algorithms that optimize recursive solutions. - -### Common Applications of Memoization - -- **Fibonacci Sequence**: - - Calculating Fibonacci numbers can be optimized using memoization to avoid redundant calculations. - -- **Combinatorial Problems**: - - Problems such as the number of ways to climb stairs or partition numbers can benefit from memoization to speed up the computation. - -- **Shortest Path Problems**: - - In algorithms like Dijkstra’s, memoization can cache results for previously calculated shortest paths to improve efficiency. - -- **Dynamic Programming Problems**: - - Many classic dynamic programming problems, such as the Knapsack problem or Longest Common Subsequence, leverage memoization to enhance performance. - -### Example of Memoization - -#### Problem: Calculate Fibonacci Numbers - -**Recursive Implementation without Memoization**: - -```python -def fibonacci(n): - if n <= 1: - return n - return fibonacci(n - 1) + fibonacci(n - 2) - -# Example usage -print(fibonacci(10)) # Output: 55 -``` - -**Recursive Implementation with Memoization**: - -```python -def fibonacci_memo(n, memo={}): - if n in memo: - return memo[n] - if n <= 1: - return n - memo[n] = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo) - return memo[n] - -# Example usage -print(fibonacci_memo(10)) # Output: 55 - -``` - -**Conclusion** - -Memoization is an essential technique in the optimization of recursive algorithms, significantly improving their efficiency. Understanding how and when to use memoization is crucial for solving complex problems effectively, especially in competitive programming and algorithm design. By mastering this technique, programmers can write cleaner, more efficient code that performs well even for large input sizes. \ No newline at end of file diff --git a/pysnippets/Memoisation/Memoisation.md b/pysnippets/Memoisation/Memoisation.md deleted file mode 100644 index 9d47f8e5..00000000 --- a/pysnippets/Memoisation/Memoisation.md +++ /dev/null @@ -1,193 +0,0 @@ - -## Memoization - -Memoization is an optimization technique used in computer science to improve the efficiency of recursive algorithms by storing the results of expensive function calls and reusing them when the same inputs occur again. This technique is particularly useful in dynamic programming, where problems can often be broken down into overlapping subproblems. - -### Importance of Memoization - -Memoization is vital for several reasons: - -1. **Improved Performance**: By caching results of expensive function calls, memoization significantly reduces the time complexity of algorithms that would otherwise perform redundant calculations. -2. **Optimal Resource Utilization**: It helps in optimizing resource usage by avoiding unnecessary computations, leading to faster execution times and lower memory consumption in some scenarios. -3. **Simplifying Recursive Algorithms**: Memoization allows complex recursive algorithms to be implemented more straightforwardly, enhancing readability and maintainability of the code. -4. **Foundation for Dynamic Programming**: Understanding memoization is essential for grasping dynamic programming concepts, which are critical for solving many algorithmic challenges. - -### Common Problems Utilizing Memoization - -Here are some commonly encountered problems that can be efficiently solved using memoization: - -- **Fibonacci Sequence**: Calculating Fibonacci numbers using memoization avoids the exponential time complexity of naive recursive approaches. -- **Longest Common Subsequence**: Finding the length of the longest common subsequence between two strings can be optimized with memoization. -- **Knapsack Problem**: Solving the 0/1 knapsack problem using memoization allows for efficient computation of maximum value. -- **Edit Distance**: Calculating the minimum edit distance between two strings can be optimized with memoization. - -### Memoization Implementation Examples - -#### 1. **Fibonacci Sequence** - -**Problem**: Compute the n-th Fibonacci number using memoization. - -**Python Implementation**: -```python -def fibonacci(n, memo={}): - if n in memo: - return memo[n] - if n <= 1: - return n - memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo) - return memo[n] - -# Example usage -print(fibonacci(10)) # Output: 55 -``` - -**C++ Implementation**: -```C++ -#include -#include -using namespace std; - -unordered_map memo; - -long long fibonacci(int n) { - if (memo.count(n)) return memo[n]; - if (n <= 1) return n; - memo[n] = fibonacci(n - 1) + fibonacci(n - 2); - return memo[n]; -} - -int main() { - cout << fibonacci(10) << endl; // Output: 55 - return 0; -} - -``` - -#### 2. **Longest Common Subsequence** - -**Problem**: Find the length of the longest common subsequence between two strings. -**Python Implementation**: -```python -def lcs(x, y, m, n, memo={}): - if (m, n) in memo: - return memo[(m, n)] - if m == 0 or n == 0: - return 0 - if x[m - 1] == y[n - 1]: - memo[(m, n)] = 1 + lcs(x, y, m - 1, n - 1, memo) - else: - memo[(m, n)] = max(lcs(x, y, m, n - 1, memo), lcs(x, y, m - 1, n, memo)) - return memo[(m, n)] - -# Example usage -x = "AGGTAB" -y = "GXTXAYB" -print(lcs(x, y, len(x), len(y))) # Output: 4 -``` - -**C++ Implementation**: -```C++ -#include -#include -using namespace std; - -unordered_map memo; - -int lcs(string x, string y, int m, int n) { - if (m == 0 || n == 0) return 0; - string key = to_string(m) + "|" + to_string(n); - if (memo.count(key)) return memo[key]; - - if (x[m - 1] == y[n - 1]) { - memo[key] = 1 + lcs(x, y, m - 1, n - 1); - } else { - memo[key] = max(lcs(x, y, m, n - 1), lcs(x, y, m - 1, n)); - } - return memo[key]; -} - -int main() { - string x = "AGGTAB"; - string y = "GXTXAYB"; - cout << lcs(x, y, x.size(), y.size()) << endl; // Output: 4 - return 0; -} -``` - - -#### 3. **0/1 Knapsack Problem** - -**Problem**: Find the maximum value that can be obtained in a knapsack of a given capacity. - -**Python Implementation**: -```python -def knapsack(weights, values, capacity, n, memo={}): - if (n, capacity) in memo: - return memo[(n, capacity)] - if n == 0 or capacity == 0: - return 0 - if weights[n - 1] > capacity: - memo[(n, capacity)] = knapsack(weights, values, capacity, n - 1, memo) - else: - memo[(n, capacity)] = max(values[n - 1] + knapsack(weights, values, capacity - weights[n - 1], n - 1, memo), - knapsack(weights, values, capacity, n - 1, memo)) - return memo[(n, capacity)] - -# Example usage -weights = [1, 2, 3] -values = [10, 15, 40] -capacity = 6 -n = len(values) -print(knapsack(weights, values, capacity, n)) # Output: 55 - -``` - -**C++ Implementation**: -```C++ -#include -#include -using namespace std; - -unordered_map memo; - -int knapsack(int weights[], int values[], int capacity, int n) { - if (n == 0 || capacity == 0) return 0; - string key = to_string(n) + "|" + to_string(capacity); - if (memo.count(key)) return memo[key]; - - if (weights[n - 1] > capacity) { - memo[key] = knapsack(weights, values, capacity, n - 1); - } else { - memo[key] = max(values[n - 1] + knapsack(weights, values, capacity - weights[n - 1], n - 1), - knapsack(weights, values, capacity, n - 1)); - } - return memo[key]; -} - -int main() { - int weights[] = {1, 2, 3}; - int values[] = {10, 15, 40}; - int capacity = 6; - int n = sizeof(values) / sizeof(values[0]); - cout << knapsack(weights, values, capacity, n) << endl; // Output: 55 - return 0; -} - -``` - -## Applications of Memoization in Competitive Programming - -In competitive programming, memoization is widely used for solving problems that involve recursive algorithms with overlapping subproblems. Some of its applications include: - -1. **Dynamic Programming**: Many dynamic programming problems, such as Fibonacci numbers, knapsack problems, and longest common subsequence, can be solved using memoization to optimize recursive solutions. - -2. **Combinatorial Problems**: Problems that involve combinations and permutations often benefit from memoization, allowing efficient computation of results without repeated calculations. - -3. **Game Theory**: In game theory problems, memoization can help efficiently compute the outcomes of recursive game scenarios. - -4. **Graph Problems**: Some graph problems, particularly those that involve paths and cycles, can also be optimized using memoization. - -## Conclusion - -Memoization is a powerful technique in computer science that enhances the performance of recursive algorithms by storing previously computed results. Its applications span various domains, particularly in dynamic programming and competitive programming. Mastering memoization equips programmers with essential tools to tackle complex problems efficiently, providing a significant advantage in both academic and professional settings. - diff --git a/pysnippets/Memoisation/Memoization.md b/pysnippets/Memoisation/Memoization.md new file mode 100644 index 00000000..fb1a4ef6 --- /dev/null +++ b/pysnippets/Memoisation/Memoization.md @@ -0,0 +1,26 @@ +# Memoisation Module + +## Overview + +The **Memoisation** module is a comprehensive collection of mathematical and algorithmic functions optimized with memoization techniques to significantly enhance performance by caching previously computed results. This approach is particularly beneficial for functions that involve expensive recursive calls, such as those commonly found in combinatorial mathematics, dynamic programming, and other computationally intensive applications. By leveraging memoization, the module reduces the computational overhead of repetitive calculations, leading to substantial improvements in execution speed and efficiency. + +## Features + +- **Memoizer Class**: A class-based memoization decorator designed to cache function results based on input arguments. This allows for the efficient reuse of previously computed values, reducing the need for redundant calculations. The Memoizer Class is a versatile tool that can be applied to a wide range of functions, making it an essential component of the module. +- **Combinatorial Calculations**: The module includes optimized functions for computing combinations efficiently using memoization. This is particularly useful for problems involving permutations, combinations, and other combinatorial calculations that can be computationally expensive without memoization. +- **Mathematical Functions**: The module provides a suite of mathematical functions that have been optimized using memoization techniques: + - **Factorial**: Calculate the factorial of a number with caching. This function leverages memoization to store previously computed factorials, ensuring that subsequent calculations are performed efficiently. + - **Fibonacci**: Compute Fibonacci numbers efficiently. The Fibonacci function utilizes memoization to cache previously computed values, reducing the computational complexity of calculating Fibonacci numbers. + - **Longest Common Subsequence (LCS)**: Determine the length of the LCS between two strings. This function employs memoization to cache intermediate results, making it more efficient than traditional recursive approaches. + - **Knapsack Solver**: Solve the classic knapsack problem using memoization. The Knapsack Solver function leverages memoization to cache solutions to subproblems, reducing the computational overhead of solving this complex problem. + +## Usage and Instructions + +To utilize the Memoisation module effectively, follow these steps: + +1. **Installation**: Ensure you have Python 3.6 or later installed. Clone the repository and navigate to the project directory. +2. **Importing the Module**: Import the Memoisation module in your Python script or project using `import memoisation`. +3. **Function Usage**: Use the provided functions as you would any other Python function. For example, to calculate the factorial of a number, use `memoisation.factorial(number)`. +4. **Memoizer Class**: To apply memoization to a custom function, use the `@memoisation.memoizer` decorator. This will enable caching of function results based on input arguments. + +By following these instructions and leveraging the features of the Memoisation module, you can significantly improve the performance of your Python applications that involve computationally intensive mathematical and algorithmic operations. \ No newline at end of file diff --git a/pysnippets/Memoisation/class_based_memoization.py b/pysnippets/Memoisation/class_based_memoization.py new file mode 100644 index 00000000..c9e24d42 --- /dev/null +++ b/pysnippets/Memoisation/class_based_memoization.py @@ -0,0 +1,41 @@ +import logging +from dataclasses import dataclass, field +from typing import Dict, Tuple + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +@dataclass +class Memoizer: + cache: Dict[Tuple, int] = field(default_factory=dict) + + def memoize(self, func): + def wrapper(*args): + try: + if args in self.cache: + logger.info(f"Fetching from cache for args: {args}") + return self.cache[args] + result = func(*args) + self.cache[args] = result + logger.info(f"Caching result for args: {args}") + return result + except Exception as e: + logger.error(f"Error in Memoizer: {e}") + raise + return wrapper + +@dataclass +class CombinatorialCalculator: + memoizer: Memoizer = field(default_factory=Memoizer) + + @staticmethod + def combination(n: int, k: int) -> int: + if k > n: + return 0 + if k == 0 or k == n: + return 1 + return CombinatorialCalculator.combination(n-1, k-1) + CombinatorialCalculator.combination(n-1, k) + + def get_combination(self, n: int, k: int) -> int: + decorated_combination = self.memoizer.memoize(CombinatorialCalculator.combination) + return decorated_combination(n, k) \ No newline at end of file diff --git a/pysnippets/Memoisation/decorator.py b/pysnippets/Memoisation/decorator.py new file mode 100644 index 00000000..67d476a9 --- /dev/null +++ b/pysnippets/Memoisation/decorator.py @@ -0,0 +1,23 @@ +import logging +from functools import wraps + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def memoize(func): + cache = {} + + @wraps(func) + def wrapper(*args): + try: + if args in cache: + logger.info(f"Fetching from cache for args: {args}") + return cache[args] + result = func(*args) + cache[args] = result + logger.info(f"Caching result for args: {args}") + return result + except Exception as e: + logger.error(f"Error in memoize decorator: {e}") + raise + return wrapper \ No newline at end of file diff --git a/pysnippets/Memoisation/factorial.py b/pysnippets/Memoisation/factorial.py new file mode 100644 index 00000000..774f75e3 --- /dev/null +++ b/pysnippets/Memoisation/factorial.py @@ -0,0 +1,17 @@ +import logging +from dataclasses import dataclass +from decorator import memoize + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +@dataclass +class FactorialCalculator: + @staticmethod + @memoize + def factorial(n: int) -> int: + if n < 0: + raise ValueError("Factorial is not defined for negative numbers.") + elif n == 0 or n == 1: + return 1 + return n * FactorialCalculator.factorial(n - 1) \ No newline at end of file diff --git a/pysnippets/Memoisation/fibonacci.py b/pysnippets/Memoisation/fibonacci.py index 467bebc9..9461c23f 100644 --- a/pysnippets/Memoisation/fibonacci.py +++ b/pysnippets/Memoisation/fibonacci.py @@ -1,16 +1,17 @@ -from functools import lru_cache +import logging +from dataclasses import dataclass +from decorator import memoize -@lru_cache(maxsize=None) -def fibonacci(n): - """ - Returns the nth Fibonacci number using automatic memoization with lru_cache. - """ - if n < 2: - return n - return fibonacci(n - 1) + fibonacci(n - 2) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) -if __name__ == "__main__": - print(fibonacci(10)) # Output: 55 - # New test cases - print(fibonacci(20)) # Output: 6765 - print(fibonacci(30)) # Output: 832040 +@dataclass +class FibonacciCalculator: + @staticmethod + @memoize + def fibonacci(n: int) -> int: + if n <= 0: + raise ValueError("Input must be a positive integer.") + elif n == 1 or n == 2: + return 1 + return FibonacciCalculator.fibonacci(n-1) + FibonacciCalculator.fibonacci(n-2) \ No newline at end of file diff --git a/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.md b/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.md deleted file mode 100644 index de73562b..00000000 --- a/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.md +++ /dev/null @@ -1,25 +0,0 @@ - -# Using functools.lru_cache for Automatic Memoization - -## Overview - -This project demonstrates the use of `functools.lru_cache` to implement automatic memoization in Python, specifically for calculating Fibonacci numbers. This approach helps in optimizing recursive functions by caching previous results. - -## Files - -- **dictionarywithlru.py**: Contains the `fibonacci` function with `@lru_cache` decorator to enable memoization. -- **test_dictionarywithlru.py**: Contains unit tests for `fibonacci`, verifying the efficiency and accuracy of the memoized function. - -## Example - -The `fibonacci` function calculates the nth Fibonacci number efficiently due to memoization, with cached results avoiding redundant calculations. - -## How to Run Tests - -To run the unit tests, execute: - -```bash -python -m unittest test_dictionarywithlru.py -``` - -This will validate that the function works correctly with memoization enabled. diff --git a/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.py b/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.py deleted file mode 100644 index fde14df2..00000000 --- a/pysnippets/Memoisation/functoolslru_cache_AutomaticMemoization.py +++ /dev/null @@ -1,11 +0,0 @@ - -from functools import lru_cache - -@lru_cache(maxsize=None) -def fibonacci(n): - """ - Returns the nth Fibonacci number using automatic memoization with lru_cache. - """ - if n < 2: - return n - return fibonacci(n - 1) + fibonacci(n - 2) diff --git a/pysnippets/Memoisation/knapsack.py b/pysnippets/Memoisation/knapsack.py index 53064992..44f5410a 100644 --- a/pysnippets/Memoisation/knapsack.py +++ b/pysnippets/Memoisation/knapsack.py @@ -1,20 +1,28 @@ -from functools import lru_cache +import logging +from dataclasses import dataclass +from decorator import memoize +from typing import Tuple -@lru_cache(maxsize=None) -def knapsack(weights, values, capacity, n): - if n == 0 or capacity == 0: - return 0 - if weights[n - 1] > capacity: - return knapsack(weights, values, capacity, n - 1) - else: - return max(values[n - 1] + knapsack(weights, values, capacity - weights[n - 1], n - 1), - knapsack(weights, values, capacity, n - 1)) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) -if __name__ == "__main__": - weights = (1, 2, 3) - values = (10, 15, 40) - capacity = 6 - n = len(values) - print(knapsack(weights, values, capacity, n)) # Output: 55 - # New test cases - print(knapsack((1, 2, 3, 4), (10, 20, 30, 40), 6, 4)) # Output: 60 +@dataclass(frozen=True) +class Item: + value: int + weight: int + +@dataclass +class KnapsackSolver: + @staticmethod + @memoize + def knapsack(max_weight: int, items: Tuple[Item, ...], n: int) -> int: + if n == 0 or max_weight == 0: + return 0 + current_item = items[n-1] + if current_item.weight > max_weight: + return KnapsackSolver.knapsack(max_weight, items, n-1) + else: + return max( + current_item.value + KnapsackSolver.knapsack(max_weight - current_item.weight, items, n-1), + KnapsackSolver.knapsack(max_weight, items, n-1) + ) \ No newline at end of file diff --git a/pysnippets/Memoisation/lcs.py b/pysnippets/Memoisation/lcs.py index 43f87508..fc2fc565 100644 --- a/pysnippets/Memoisation/lcs.py +++ b/pysnippets/Memoisation/lcs.py @@ -1,17 +1,18 @@ -from functools import lru_cache +import logging +from dataclasses import dataclass +from decorator import memoize -@lru_cache(maxsize=None) -def lcs(x, y, m, n): - if m == 0 or n == 0: - return 0 - if x[m - 1] == y[n - 1]: - return 1 + lcs(x, y, m - 1, n - 1) - else: - return max(lcs(x, y, m, n - 1), lcs(x, y, m - 1, n)) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) -if __name__ == "__main__": - x = "AGGTAB" - y = "GXTXAYB" - print(lcs(x, y, len(x), len(y))) # Output: 4 - # New test cases - print(lcs("ABC", "AC", len("ABC"), len("AC"))) # Output: 2 +@dataclass +class LCSSolver: + @staticmethod + @memoize + def lcs(X: str, Y: str, m: int, n: int) -> int: + if m == 0 or n == 0: + return 0 + if X[m-1] == Y[n-1]: + return 1 + LCSSolver.lcs(X, Y, m-1, n-1) + else: + return max(LCSSolver.lcs(X, Y, m, n-1), LCSSolver.lcs(X, Y, m-1, n)) \ No newline at end of file diff --git a/pysnippets/Memoisation/test_all.py b/pysnippets/Memoisation/test_all.py new file mode 100644 index 00000000..548cc220 --- /dev/null +++ b/pysnippets/Memoisation/test_all.py @@ -0,0 +1,114 @@ +import unittest +from class_based_memoization import CombinatorialCalculator +from decorator import memoize +from factorial import FactorialCalculator +from fibonacci import FibonacciCalculator +from knapsack import KnapsackSolver, Item +from lcs import LCSSolver + +class TestCombinatorialCalculator(unittest.TestCase): + + def setUp(self): + self.calculator = CombinatorialCalculator() + + def test_combination_standard_cases(self): + self.assertEqual(self.calculator.get_combination(5, 2), 10) + self.assertEqual(self.calculator.get_combination(10, 5), 252) + self.assertEqual(self.calculator.get_combination(0, 0), 1) + + def test_combination_edge_cases(self): + self.assertEqual(self.calculator.get_combination(5, 0), 1) + self.assertEqual(self.calculator.get_combination(5, 5), 1) + self.assertEqual(self.calculator.get_combination(5, 6), 0) + + def test_combination_invalid_input(self): + with self.assertRaises(TypeError): + self.calculator.get_combination("5", 2) + with self.assertRaises(TypeError): + self.calculator.get_combination(5, "2") + +class TestMemoizeDecorator(unittest.TestCase): + + def test_memoize_add_function(self): + call_count = 0 + + @memoize + def add(a, b): + nonlocal call_count + call_count += 1 + return a + b + + self.assertEqual(add(2, 3), 5) + self.assertEqual(add(2, 3), 5) + self.assertEqual(call_count, 1) + +class TestFactorialCalculator(unittest.TestCase): + + def test_factorial_standard_cases(self): + self.assertEqual(FactorialCalculator.factorial(0), 1) + self.assertEqual(FactorialCalculator.factorial(1), 1) + self.assertEqual(FactorialCalculator.factorial(5), 120) + self.assertEqual(FactorialCalculator.factorial(10), 3628800) + + def test_factorial_invalid_input(self): + with self.assertRaises(ValueError): + FactorialCalculator.factorial(-1) + with self.assertRaises(ValueError): + FactorialCalculator.factorial(-10) + +class TestFibonacciCalculator(unittest.TestCase): + + def test_fibonacci_standard_cases(self): + self.assertEqual(FibonacciCalculator.fibonacci(1), 1) + self.assertEqual(FibonacciCalculator.fibonacci(5), 5) + self.assertEqual(FibonacciCalculator.fibonacci(10), 55) + + def test_fibonacci_invalid_input(self): + with self.assertRaises(ValueError): + FibonacciCalculator.fibonacci(0) + with self.assertRaises(ValueError): + FibonacciCalculator.fibonacci(-5) + +class TestKnapsackSolver(unittest.TestCase): + + def test_knapsack_standard_case(self): + items = ( + Item(value=60, weight=10), + Item(value=100, weight=20), + Item(value=120, weight=30), + ) + self.assertEqual(KnapsackSolver.knapsack(50, items, 3), 220) + + def test_knapsack_no_items(self): + items = () + self.assertEqual(KnapsackSolver.knapsack(50, items, 0), 0) + + def test_knapsack_zero_capacity(self): + items = ( + Item(value=60, weight=10), + ) + self.assertEqual(KnapsackSolver.knapsack(0, items, 1), 0) + + def test_knapsack_item_exceeds_capacity(self): + items = ( + Item(value=100, weight=60), + ) + self.assertEqual(KnapsackSolver.knapsack(50, items, 1), 0) + +class TestLCSSolver(unittest.TestCase): + + def test_lcs_standard_case(self): + self.assertEqual(LCSSolver.lcs("AGGTAB", "GXTXAYB", 6, 7), 4) + + def test_lcs_no_common_subsequence(self): + self.assertEqual(LCSSolver.lcs("ABC", "DEF", 3, 3), 0) + + def test_lcs_empty_string(self): + self.assertEqual(LCSSolver.lcs("", "ABC", 0, 3), 0) + self.assertEqual(LCSSolver.lcs("ABC", "", 3, 0), 0) + + def test_lcs_complete_overlap(self): + self.assertEqual(LCSSolver.lcs("ABC", "ABC", 3, 3), 3) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_class_based_memoization.py b/pysnippets/Memoisation/test_class_based_memoization.py new file mode 100644 index 00000000..d6c1352f --- /dev/null +++ b/pysnippets/Memoisation/test_class_based_memoization.py @@ -0,0 +1,26 @@ +import unittest +from class_based_memoization import CombinatorialCalculator + +class TestCombinatorialCalculator(unittest.TestCase): + + def setUp(self): + self.calculator = CombinatorialCalculator() + + def test_combination_standard_cases(self): + self.assertEqual(self.calculator.get_combination(5, 2), 10) + self.assertEqual(self.calculator.get_combination(10, 5), 252) + self.assertEqual(self.calculator.get_combination(0, 0), 1) + + def test_combination_edge_cases(self): + self.assertEqual(self.calculator.get_combination(5, 0), 1) + self.assertEqual(self.calculator.get_combination(5, 5), 1) + self.assertEqual(self.calculator.get_combination(5, 6), 0) + + def test_combination_invalid_input(self): + with self.assertRaises(TypeError): + self.calculator.get_combination("5", 2) + with self.assertRaises(TypeError): + self.calculator.get_combination(5, "2") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_decorator.py b/pysnippets/Memoisation/test_decorator.py new file mode 100644 index 00000000..2bb2df7c --- /dev/null +++ b/pysnippets/Memoisation/test_decorator.py @@ -0,0 +1,20 @@ +import unittest +from decorator import memoize + +class TestMemoizeDecorator(unittest.TestCase): + + def test_memoize_add_function(self): + call_count = 0 + + @memoize + def add(a, b): + nonlocal call_count + call_count += 1 + return a + b + + self.assertEqual(add(2, 3), 5) + self.assertEqual(add(2, 3), 5) + self.assertEqual(call_count, 1) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_factorial.py b/pysnippets/Memoisation/test_factorial.py new file mode 100644 index 00000000..462710ce --- /dev/null +++ b/pysnippets/Memoisation/test_factorial.py @@ -0,0 +1,19 @@ +import unittest +from factorial import FactorialCalculator + +class TestFactorialCalculator(unittest.TestCase): + + def test_factorial_standard_cases(self): + self.assertEqual(FactorialCalculator.factorial(0), 1) + self.assertEqual(FactorialCalculator.factorial(1), 1) + self.assertEqual(FactorialCalculator.factorial(5), 120) + self.assertEqual(FactorialCalculator.factorial(10), 3628800) + + def test_factorial_invalid_input(self): + with self.assertRaises(ValueError): + FactorialCalculator.factorial(-1) + with self.assertRaises(ValueError): + FactorialCalculator.factorial(-10) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_fibonacci.py b/pysnippets/Memoisation/test_fibonacci.py new file mode 100644 index 00000000..c4006759 --- /dev/null +++ b/pysnippets/Memoisation/test_fibonacci.py @@ -0,0 +1,18 @@ +import unittest +from fibonacci import FibonacciCalculator + +class TestFibonacciCalculator(unittest.TestCase): + + def test_fibonacci_standard_cases(self): + self.assertEqual(FibonacciCalculator.fibonacci(1), 1) + self.assertEqual(FibonacciCalculator.fibonacci(5), 5) + self.assertEqual(FibonacciCalculator.fibonacci(10), 55) + + def test_fibonacci_invalid_input(self): + with self.assertRaises(ValueError): + FibonacciCalculator.fibonacci(0) + with self.assertRaises(ValueError): + FibonacciCalculator.fibonacci(-5) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_knapsack.py b/pysnippets/Memoisation/test_knapsack.py new file mode 100644 index 00000000..f63a465e --- /dev/null +++ b/pysnippets/Memoisation/test_knapsack.py @@ -0,0 +1,31 @@ +import unittest +from knapsack import KnapsackSolver, Item + +class TestKnapsackSolver(unittest.TestCase): + + def test_knapsack_standard_case(self): + items = ( + Item(value=60, weight=10), + Item(value=100, weight=20), + Item(value=120, weight=30), + ) + self.assertEqual(KnapsackSolver.knapsack(50, items, 3), 220) + + def test_knapsack_no_items(self): + items = () + self.assertEqual(KnapsackSolver.knapsack(50, items, 0), 0) + + def test_knapsack_zero_capacity(self): + items = ( + Item(value=60, weight=10), + ) + self.assertEqual(KnapsackSolver.knapsack(0, items, 1), 0) + + def test_knapsack_item_exceeds_capacity(self): + items = ( + Item(value=100, weight=60), + ) + self.assertEqual(KnapsackSolver.knapsack(50, items, 1), 0) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_lcs.py b/pysnippets/Memoisation/test_lcs.py new file mode 100644 index 00000000..454a84bd --- /dev/null +++ b/pysnippets/Memoisation/test_lcs.py @@ -0,0 +1,20 @@ +import unittest +from lcs import LCSSolver + +class TestLCSSolver(unittest.TestCase): + + def test_lcs_standard_case(self): + self.assertEqual(LCSSolver.lcs("AGGTAB", "GXTXAYB", 6, 7), 4) + + def test_lcs_no_common_subsequence(self): + self.assertEqual(LCSSolver.lcs("ABC", "DEF", 3, 3), 0) + + def test_lcs_empty_string(self): + self.assertEqual(LCSSolver.lcs("", "ABC", 0, 3), 0) + self.assertEqual(LCSSolver.lcs("ABC", "", 3, 0), 0) + + def test_lcs_complete_overlap(self): + self.assertEqual(LCSSolver.lcs("ABC", "ABC", 3, 3), 3) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/pysnippets/Memoisation/test_memoization.py b/pysnippets/Memoisation/test_memoization.py deleted file mode 100644 index c007ef43..00000000 --- a/pysnippets/Memoisation/test_memoization.py +++ /dev/null @@ -1,24 +0,0 @@ -import unittest -from fibonacci import fibonacci -from knapsack import knapsack -from lcs import lcs - -class TestMemoization(unittest.TestCase): - def test_fibonacci(self): - self.assertEqual(fibonacci(10), 55) - self.assertEqual(fibonacci(20), 6765) - self.assertEqual(fibonacci(30), 832040) - - def test_knapsack(self): - weights = (1, 2, 3) - values = (10, 15, 40) - capacity = 6 - self.assertEqual(knapsack(weights, values, capacity, len(values)), 55) - self.assertEqual(knapsack((1, 2, 3, 4), (10, 20, 30, 40), 6, 4), 60) - - def test_lcs(self): - self.assertEqual(lcs("AGGTAB", "GXTXAYB", len("AGGTAB"), len("GXTXAYB")), 4) - self.assertEqual(lcs("ABC", "AC", len("ABC"), len("AC")), 2) - -if __name__ == "__main__": - unittest.main() \ No newline at end of file