From 65065313889264304af549811f13c7149562cecc Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 13:55:19 +0530 Subject: [PATCH 01/11] Create sieve_of_eratosthenes.py Created a new folder of primality under Python/math and added a suitable algorithm --- maths/primality/sieve_of_eratosthenes.py | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 maths/primality/sieve_of_eratosthenes.py diff --git a/maths/primality/sieve_of_eratosthenes.py b/maths/primality/sieve_of_eratosthenes.py new file mode 100644 index 000000000000..3a5db49922d6 --- /dev/null +++ b/maths/primality/sieve_of_eratosthenes.py @@ -0,0 +1,33 @@ +# Sieve of Eratosthenes: an efficient algorithm to compute all prime numbers up to n. +# It repeatedly marks multiples of each prime as non-prime, starting from 2. +# This method is suitable for n up to about 10**7 on typical hardware. + +def sieve_of_erastosthenes(n): + + #Boolean list to track prime status of numbers + prime = [True] * (n + 1) + p = 2 + + # Main Algorithm + while p * p <= n: + if prime[p]: + + # All multiples of p will be non-prime hence delcare them False. + + for i in range(p * p, n + 1, p): + prime[i] = False + p += 1 + + # Store all primes. + result = [] + for p in range(2, n + 1): + if prime[p]: + result.append(p) + + return result + +if __name__ == "__main__": + n = 35 + result = sieve_of_erastosthenes(n) + for num in result: + print(num, end=' ') \ No newline at end of file From e33cfc212b4f7dba638561597b319995497b615c Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 13:58:07 +0530 Subject: [PATCH 02/11] Update sieve_of_eratosthenes.py --- maths/primality/sieve_of_eratosthenes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/maths/primality/sieve_of_eratosthenes.py b/maths/primality/sieve_of_eratosthenes.py index 3a5db49922d6..a558aa9ce5bd 100644 --- a/maths/primality/sieve_of_eratosthenes.py +++ b/maths/primality/sieve_of_eratosthenes.py @@ -3,6 +3,19 @@ # This method is suitable for n up to about 10**7 on typical hardware. def sieve_of_erastosthenes(n): + """ +Compute all prime numbers up to and including n using the Sieve of Eratosthenes. +Parameters +---------- +n : int + Upper bound (inclusive) of the range in which to find prime numbers. + Expected to be a non-negative integer. If n < 2 the function returns an empty list. +Returns +------- +list[int] + A list of primes in ascending order that are <= n. + """ + #Boolean list to track prime status of numbers prime = [True] * (n + 1) From aceb903c93b44e351571d86f2607d03fc5aee906 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 13:59:43 +0530 Subject: [PATCH 03/11] Update sieve_of_eratosthenes.py --- maths/primality/sieve_of_eratosthenes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/maths/primality/sieve_of_eratosthenes.py b/maths/primality/sieve_of_eratosthenes.py index a558aa9ce5bd..39703fa3746b 100644 --- a/maths/primality/sieve_of_eratosthenes.py +++ b/maths/primality/sieve_of_eratosthenes.py @@ -1,6 +1,7 @@ # Sieve of Eratosthenes: an efficient algorithm to compute all prime numbers up to n. # It repeatedly marks multiples of each prime as non-prime, starting from 2. # This method is suitable for n up to about 10**7 on typical hardware. +# Wikipedia URl- https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes def sieve_of_erastosthenes(n): """ From f16e35efee80c4c20529bbfc81fe22776b7de901 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:05:52 +0530 Subject: [PATCH 04/11] Update sieve_of_eratosthenes.py --- maths/primality/sieve_of_eratosthenes.py | 60 ++++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/maths/primality/sieve_of_eratosthenes.py b/maths/primality/sieve_of_eratosthenes.py index 39703fa3746b..d38676901bfc 100644 --- a/maths/primality/sieve_of_eratosthenes.py +++ b/maths/primality/sieve_of_eratosthenes.py @@ -1,47 +1,47 @@ # Sieve of Eratosthenes: an efficient algorithm to compute all prime numbers up to n. # It repeatedly marks multiples of each prime as non-prime, starting from 2. # This method is suitable for n up to about 10**7 on typical hardware. -# Wikipedia URl- https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes +# Wikipedia URL - https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes -def sieve_of_erastosthenes(n): +def sieve_of_eratosthenes(n: int) -> list[int]: """ -Compute all prime numbers up to and including n using the Sieve of Eratosthenes. -Parameters ----------- -n : int - Upper bound (inclusive) of the range in which to find prime numbers. - Expected to be a non-negative integer. If n < 2 the function returns an empty list. -Returns -------- -list[int] - A list of primes in ascending order that are <= n. + Compute all prime numbers up to and including n using the Sieve of Eratosthenes. + + Parameters + ---------- + n : int + Upper bound (inclusive) of the range in which to find prime numbers. + Expected to be a non-negative integer. If n < 2 the function returns an empty list. + + Returns + ------- + list[int] + A list of primes in ascending order that are <= n. + + Examples + -------- + >>> sieve_of_eratosthenes(10) + [2, 3, 5, 7] + >>> sieve_of_eratosthenes(1) + [] + >>> sieve_of_eratosthenes(2) + [2] + >>> sieve_of_eratosthenes(20) + [2, 3, 5, 7, 11, 13, 17, 19] """ + if n < 2: + return [] - - #Boolean list to track prime status of numbers prime = [True] * (n + 1) p = 2 - - # Main Algorithm while p * p <= n: if prime[p]: - - # All multiples of p will be non-prime hence delcare them False. - for i in range(p * p, n + 1, p): prime[i] = False p += 1 - # Store all primes. - result = [] - for p in range(2, n + 1): - if prime[p]: - result.append(p) - - return result + return [p for p in range(2, n + 1) if prime[p]] + if __name__ == "__main__": - n = 35 - result = sieve_of_erastosthenes(n) - for num in result: - print(num, end=' ') \ No newline at end of file + print(sieve_of_eratosthenes(35)) From eb5581792e363f9a652ddee1fba5fe5ad6440c19 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:11:17 +0530 Subject: [PATCH 05/11] Add optimized Sieve of Eratosthenes algorithm --- maths/optimised_sieve_of_eratosthenes.py | 61 ++++++++++++++++++++++++ maths/primality/sieve_of_eratosthenes.py | 47 ------------------ 2 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 maths/optimised_sieve_of_eratosthenes.py delete mode 100644 maths/primality/sieve_of_eratosthenes.py diff --git a/maths/optimised_sieve_of_eratosthenes.py b/maths/optimised_sieve_of_eratosthenes.py new file mode 100644 index 000000000000..41a4314a4d49 --- /dev/null +++ b/maths/optimised_sieve_of_eratosthenes.py @@ -0,0 +1,61 @@ +# Optimized Sieve of Eratosthenes: An efficient algorithm to compute all prime numbers up to n. +# This version skips even numbers after 2, improving both memory and time usage. +# It is particularly efficient for larger n (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(n: int) -> list[int]: + """ + Compute all prime numbers up to and including n using an optimized Sieve of Eratosthenes. + + This implementation skips even numbers after 2 to reduce memory and runtime by about 50%. + + Parameters + ---------- + n : int + Upper bound (inclusive) of the range in which to find prime numbers. + Expected to be a non-negative integer. If n < 2 the function returns an empty list. + + Returns + ------- + list[int] + A list of primes in ascending order that are <= n. + + 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 n < 2: + return [] + + # Handle 2 separately, then consider only odd numbers + primes = [2] if n >= 2 else [] + + # Only odd numbers from 3 to n + size = (n - 1) // 2 + is_prime = [True] * (size + 1) + limit = isqrt(n) + + for i in range((limit - 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/maths/primality/sieve_of_eratosthenes.py b/maths/primality/sieve_of_eratosthenes.py deleted file mode 100644 index d38676901bfc..000000000000 --- a/maths/primality/sieve_of_eratosthenes.py +++ /dev/null @@ -1,47 +0,0 @@ -# Sieve of Eratosthenes: an efficient algorithm to compute all prime numbers up to n. -# It repeatedly marks multiples of each prime as non-prime, starting from 2. -# This method is suitable for n up to about 10**7 on typical hardware. -# Wikipedia URL - https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - -def sieve_of_eratosthenes(n: int) -> list[int]: - """ - Compute all prime numbers up to and including n using the Sieve of Eratosthenes. - - Parameters - ---------- - n : int - Upper bound (inclusive) of the range in which to find prime numbers. - Expected to be a non-negative integer. If n < 2 the function returns an empty list. - - Returns - ------- - list[int] - A list of primes in ascending order that are <= n. - - Examples - -------- - >>> sieve_of_eratosthenes(10) - [2, 3, 5, 7] - >>> sieve_of_eratosthenes(1) - [] - >>> sieve_of_eratosthenes(2) - [2] - >>> sieve_of_eratosthenes(20) - [2, 3, 5, 7, 11, 13, 17, 19] - """ - if n < 2: - return [] - - prime = [True] * (n + 1) - p = 2 - while p * p <= n: - if prime[p]: - for i in range(p * p, n + 1, p): - prime[i] = False - p += 1 - - return [p for p in range(2, n + 1) if prime[p]] - - -if __name__ == "__main__": - print(sieve_of_eratosthenes(35)) From 40608c92245565b377681cf425402358baf92359 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:14:08 +0530 Subject: [PATCH 06/11] Update optimised_sieve_of_eratosthenes.py --- maths/optimised_sieve_of_eratosthenes.py | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/maths/optimised_sieve_of_eratosthenes.py b/maths/optimised_sieve_of_eratosthenes.py index 41a4314a4d49..d27ba1b488df 100644 --- a/maths/optimised_sieve_of_eratosthenes.py +++ b/maths/optimised_sieve_of_eratosthenes.py @@ -1,26 +1,26 @@ -# Optimized Sieve of Eratosthenes: An efficient algorithm to compute all prime numbers up to n. +# 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 n (e.g., up to 10**8 on typical hardware). +# 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(n: int) -> list[int]: +def optimized_sieve(limit: int) -> list[int]: """ - Compute all prime numbers up to and including n using an optimized Sieve of Eratosthenes. + 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 ---------- - n : int + limit : int Upper bound (inclusive) of the range in which to find prime numbers. - Expected to be a non-negative integer. If n < 2 the function returns an empty list. + 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 <= n. + A list of primes in ascending order that are <= limit. Examples -------- @@ -33,18 +33,18 @@ def optimized_sieve(n: int) -> list[int]: >>> optimized_sieve(30) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] """ - if n < 2: + if limit < 2: return [] # Handle 2 separately, then consider only odd numbers - primes = [2] if n >= 2 else [] + primes = [2] if limit >= 2 else [] - # Only odd numbers from 3 to n - size = (n - 1) // 2 + # Only odd numbers from 3 to limit + size = (limit - 1) // 2 is_prime = [True] * (size + 1) - limit = isqrt(n) + bound = isqrt(limit) - for i in range((limit - 1) // 2 + 1): + for i in range((bound - 1) // 2 + 1): if is_prime[i]: p = 2 * i + 3 # Start marking from p^2, converted to index @@ -57,5 +57,4 @@ def optimized_sieve(n: int) -> list[int]: if __name__ == "__main__": - print(optimized_sieve(50)) From c9baa2432f5c2be61b7933123f5788ea9d2dd1e0 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:15:57 +0530 Subject: [PATCH 07/11] Update optimised_sieve_of_eratosthenes.py --- maths/optimised_sieve_of_eratosthenes.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/maths/optimised_sieve_of_eratosthenes.py b/maths/optimised_sieve_of_eratosthenes.py index d27ba1b488df..ef2f6fb90104 100644 --- a/maths/optimised_sieve_of_eratosthenes.py +++ b/maths/optimised_sieve_of_eratosthenes.py @@ -1,21 +1,26 @@ -# 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). +# 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. + 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%. + 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. + Expected to be a non-negative integer. If limit < 2 the function + returns an empty list. Returns ------- From 97a0c705354e00faf2f5ce4773fcab1a7c1dc94b Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:31:07 +0530 Subject: [PATCH 08/11] Create determinant_calculator.py --- matrix/determinant_calculator.py | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 matrix/determinant_calculator.py diff --git a/matrix/determinant_calculator.py b/matrix/determinant_calculator.py new file mode 100644 index 000000000000..8f8f18f7e151 --- /dev/null +++ b/matrix/determinant_calculator.py @@ -0,0 +1,70 @@ +# 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 + The row to remove. + col : int + The column to remove. + + Returns + ------- + list[list[int] | list[float]] + Minor matrix. + """ + 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([[1,2],[3,4]]) + -2 + >>> determinant_manual([[2]]) + 2 + >>> determinant_manual([[1,2,3],[4,5,6],[7,8,9]]) + 0 + """ + 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__": + # Simple demo + matrix_demo = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ] + print(f"The determinant is: {determinant_manual(matrix_demo)}") From 04100485dedf6ff78ae83b54c8398ff53b49c396 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:34:18 +0530 Subject: [PATCH 09/11] Update determinant_calculator.py --- matrix/determinant_calculator.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/matrix/determinant_calculator.py b/matrix/determinant_calculator.py index 8f8f18f7e151..670bd273912f 100644 --- a/matrix/determinant_calculator.py +++ b/matrix/determinant_calculator.py @@ -1,5 +1,3 @@ -# 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. @@ -9,14 +7,21 @@ def get_minor(matrix: list[list[int] | list[float]], row: int, col: int) -> list matrix : list[list[int] | list[float]] The original square matrix. row : int - The row to remove. + Row to remove. col : int - The column to remove. + 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] @@ -37,12 +42,14 @@ def determinant_manual(matrix: list[list[int] | list[float]]) -> int | float: Examples -------- - >>> determinant_manual([[1,2],[3,4]]) - -2 >>> 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) From cdeff433c512c034269d6dae965683e93407ceda Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 14:37:05 +0530 Subject: [PATCH 10/11] Update determinant_calculator.py --- matrix/determinant_calculator.py | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/matrix/determinant_calculator.py b/matrix/determinant_calculator.py index 670bd273912f..486ccc215cfa 100644 --- a/matrix/determinant_calculator.py +++ b/matrix/determinant_calculator.py @@ -1,4 +1,11 @@ -def get_minor(matrix: list[list[int] | list[float]], row: int, col: int) -> list[list[int] | list[float]]: +# 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. @@ -18,12 +25,12 @@ def get_minor(matrix: list[list[int] | list[float]], row: int, col: int) -> list Examples -------- - >>> get_minor([[1,2],[3,4]], 0, 0) + >>> get_minor([[1, 2], [3, 4]], 0, 0) [[4]] - >>> get_minor([[1,2,3],[4,5,6],[7,8,9]], 1, 1) + >>> 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] + 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: @@ -44,11 +51,20 @@ def determinant_manual(matrix: list[list[int] | list[float]]) -> int | float: -------- >>> determinant_manual([[2]]) 2 - >>> determinant_manual([[1, 2], [3, 4]]) + >>> determinant_manual([[1, 2], + ... [3, 4]]) -2 - >>> determinant_manual([[1,2,3],[4,5,6],[7,8,9]]) + >>> determinant_manual([ + ... [1, 2, 3], + ... [4, 5, 6], + ... [7, 8, 9] + ... ]) 0 - >>> determinant_manual([[3,0,2],[2,0,-2],[0,1,1]]) + >>> determinant_manual([ + ... [3, 0, 2], + ... [2, 0, -2], + ... [0, 1, 1] + ... ]) 10 """ n = len(matrix) @@ -57,7 +73,7 @@ def determinant_manual(matrix: list[list[int] | list[float]]) -> int | float: return matrix[0][0] if n == 2: - return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0] + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] det = 0 for j in range(n): @@ -68,7 +84,7 @@ def determinant_manual(matrix: list[list[int] | list[float]]) -> int | float: if __name__ == "__main__": - # Simple demo + matrix_demo = [ [1, 2, 3], [4, 5, 6], From 858109934886966d8f99daccd931df6380288af3 Mon Sep 17 00:00:00 2001 From: Naman-Vasudev Date: Mon, 20 Oct 2025 15:03:00 +0530 Subject: [PATCH 11/11] Create ncr_combinations.py --- maths/ncr_combinations.py | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 maths/ncr_combinations.py 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)}")