diff --git a/maths/ncr_combinations.py b/maths/ncr_combinations.py new file mode 100644 index 000000000000..ebe17735a32c --- /dev/null +++ b/maths/ncr_combinations.py @@ -0,0 +1,74 @@ +""" +Generalized nCr (combinations) calculator for real numbers n and integer r. +Wikipedia URL: https://en.wikipedia.org/wiki/Binomial_coefficient +""" + +from math import factorial as math_factorial + + +def nCr(n: float, r: int) -> float: + """ + Compute the number of combinations (n choose r) for real n and integer r + using the formula: + + nCr = n * (n-1) * (n-2) * ... * (n-r+1) / r! + + Parameters + ---------- + n : float + Total number of items. Can be any real number. + r : int + Number of items to choose. Must be a non-negative integer. + + Returns + ------- + float + The number of combinations. + + Raises + ------ + ValueError + If r is not an integer or r < 0 + + Examples + -------- + >>> nCr(5, 2) + 10.0 + >>> nCr(5.5, 2) + 12.375 + >>> nCr(10, 0) + 1.0 + >>> nCr(0, 0) + 1.0 + >>> nCr(5, -1) + Traceback (most recent call last): + ... + ValueError: r must be a non-negative integer + >>> nCr(5, 2.5) + Traceback (most recent call last): + ... + ValueError: r must be a non-negative integer + """ + if not isinstance(r, int) or r < 0: + raise ValueError("r must be a non-negative integer") + + if r == 0: + return 1.0 + + numerator = 1.0 + for i in range(r): + numerator *= n - i + + denominator = math_factorial(r) + return numerator / denominator + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # Example usage + n = float(input("Enter n (real number): ").strip() or 0) + r = int(input("Enter r (integer): ").strip() or 0) + print(f"nCr({n}, {r}) = {nCr(n, r)}") diff --git a/maths/optimised_sieve_of_eratosthenes.py b/maths/optimised_sieve_of_eratosthenes.py new file mode 100644 index 000000000000..ef2f6fb90104 --- /dev/null +++ b/maths/optimised_sieve_of_eratosthenes.py @@ -0,0 +1,65 @@ +# Optimized Sieve of Eratosthenes: An efficient algorithm to compute all prime numbers +# up to limit. This version skips even numbers after 2, improving both memory and time +# usage. It is particularly efficient for larger limits (e.g., up to 10**8 on typical +# hardware). +# Wikipedia URL - https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + +from math import isqrt + + +def optimized_sieve(limit: int) -> list[int]: + """ + Compute all prime numbers up to and including `limit` using an optimized + Sieve of Eratosthenes. + + This implementation skips even numbers after 2 to reduce memory and + runtime by about 50%. + + Parameters + ---------- + limit : int + Upper bound (inclusive) of the range in which to find prime numbers. + Expected to be a non-negative integer. If limit < 2 the function + returns an empty list. + + Returns + ------- + list[int] + A list of primes in ascending order that are <= limit. + + Examples + -------- + >>> optimized_sieve(10) + [2, 3, 5, 7] + >>> optimized_sieve(1) + [] + >>> optimized_sieve(2) + [2] + >>> optimized_sieve(30) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + """ + if limit < 2: + return [] + + # Handle 2 separately, then consider only odd numbers + primes = [2] if limit >= 2 else [] + + # Only odd numbers from 3 to limit + size = (limit - 1) // 2 + is_prime = [True] * (size + 1) + bound = isqrt(limit) + + for i in range((bound - 1) // 2 + 1): + if is_prime[i]: + p = 2 * i + 3 + # Start marking from p^2, converted to index + start = (p * p - 3) // 2 + for j in range(start, size + 1, p): + is_prime[j] = False + + primes.extend(2 * i + 3 for i in range(size + 1) if is_prime[i]) + return primes + + +if __name__ == "__main__": + print(optimized_sieve(50)) diff --git a/matrix/determinant_calculator.py b/matrix/determinant_calculator.py new file mode 100644 index 000000000000..486ccc215cfa --- /dev/null +++ b/matrix/determinant_calculator.py @@ -0,0 +1,93 @@ +# Determinant calculator using cofactor expansion. +# Calculates the determinant of a square matrix recursively. +# Wikipedia URL - https://en.wikipedia.org/wiki/Determinant + + +def get_minor( + matrix: list[list[int] | list[float]], row: int, col: int +) -> list[list[int] | list[float]]: + """ + Returns the minor matrix obtained by removing the specified row and column. + + Parameters + ---------- + matrix : list[list[int] | list[float]] + The original square matrix. + row : int + Row to remove. + col : int + Column to remove. + + Returns + ------- + list[list[int] | list[float]] + Minor matrix. + + Examples + -------- + >>> get_minor([[1, 2], [3, 4]], 0, 0) + [[4]] + >>> get_minor([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 1, 1) + [[1, 3], [7, 9]] + """ + return [r[:col] + r[col + 1 :] for i, r in enumerate(matrix) if i != row] + + +def determinant_manual(matrix: list[list[int] | list[float]]) -> int | float: + """ + Calculates the determinant of a square matrix using cofactor expansion. + + Parameters + ---------- + matrix : list[list[int] | list[float]] + A square matrix. + + Returns + ------- + int | float + Determinant of the matrix. + + Examples + -------- + >>> determinant_manual([[2]]) + 2 + >>> determinant_manual([[1, 2], + ... [3, 4]]) + -2 + >>> determinant_manual([ + ... [1, 2, 3], + ... [4, 5, 6], + ... [7, 8, 9] + ... ]) + 0 + >>> determinant_manual([ + ... [3, 0, 2], + ... [2, 0, -2], + ... [0, 1, 1] + ... ]) + 10 + """ + n = len(matrix) + + if n == 1: + return matrix[0][0] + + if n == 2: + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] + + det = 0 + for j in range(n): + minor = get_minor(matrix, 0, j) + cofactor = (-1) ** j * determinant_manual(minor) + det += matrix[0][j] * cofactor + return det + + +if __name__ == "__main__": + + matrix_demo = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ] + print(f"The determinant is: {determinant_manual(matrix_demo)}")