Merge branch 'TheAlgorithms:master' into master

This commit is contained in:
Praful Katare 2023-10-09 21:20:51 +05:30 committed by GitHub
commit d70575b231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1113 additions and 240 deletions

View File

@ -556,7 +556,7 @@
* [Bell Numbers](maths/bell_numbers.py)
* [Binary Exp Mod](maths/binary_exp_mod.py)
* [Binary Exponentiation](maths/binary_exponentiation.py)
* [Binary Exponentiation 3](maths/binary_exponentiation_3.py)
* [Binary Exponentiation 2](maths/binary_exponentiation_2.py)
* [Binary Multiplication](maths/binary_multiplication.py)
* [Binomial Coefficient](maths/binomial_coefficient.py)
* [Binomial Distribution](maths/binomial_distribution.py)
@ -588,7 +588,6 @@
* [Find Min](maths/find_min.py)
* [Floor](maths/floor.py)
* [Gamma](maths/gamma.py)
* [Gamma Recursive](maths/gamma_recursive.py)
* [Gaussian](maths/gaussian.py)
* [Gaussian Error Linear Unit](maths/gaussian_error_linear_unit.py)
* [Gcd Of N Numbers](maths/gcd_of_n_numbers.py)
@ -723,6 +722,7 @@
* Activation Functions
* [Exponential Linear Unit](neural_network/activation_functions/exponential_linear_unit.py)
* [Leaky Rectified Linear Unit](neural_network/activation_functions/leaky_rectified_linear_unit.py)
* [Mish](neural_network/activation_functions/mish.py)
* [Rectified Linear Unit](neural_network/activation_functions/rectified_linear_unit.py)
* [Scaled Exponential Linear Unit](neural_network/activation_functions/scaled_exponential_linear_unit.py)
* [Sigmoid Linear Unit](neural_network/activation_functions/sigmoid_linear_unit.py)
@ -748,6 +748,7 @@
* [Linear Congruential Generator](other/linear_congruential_generator.py)
* [Lru Cache](other/lru_cache.py)
* [Magicdiamondpattern](other/magicdiamondpattern.py)
* [Majority Vote Algorithm](other/majority_vote_algorithm.py)
* [Maximum Subsequence](other/maximum_subsequence.py)
* [Nested Brackets](other/nested_brackets.py)
* [Number Container System](other/number_container_system.py)
@ -1196,7 +1197,6 @@
* [Rabin Karp](strings/rabin_karp.py)
* [Remove Duplicate](strings/remove_duplicate.py)
* [Reverse Letters](strings/reverse_letters.py)
* [Reverse Long Words](strings/reverse_long_words.py)
* [Reverse Words](strings/reverse_words.py)
* [Snake Case To Camel Pascal Case](strings/snake_case_to_camel_pascal_case.py)
* [Split](strings/split.py)

View File

@ -0,0 +1,67 @@
"""
Task:
Given a positive int number. Return True if this number is power of 4
or False otherwise.
Implementation notes: Use bit manipulation.
For example if the number is the power of 2 it's bits representation:
n = 0..100..00
n - 1 = 0..011..11
n & (n - 1) - no intersections = 0
If the number is a power of 4 then it should be a power of 2
and the set bit should be at an odd position.
"""
def power_of_4(number: int) -> bool:
"""
Return True if this number is power of 4 or False otherwise.
>>> power_of_4(0)
Traceback (most recent call last):
...
ValueError: number must be positive
>>> power_of_4(1)
True
>>> power_of_4(2)
False
>>> power_of_4(4)
True
>>> power_of_4(6)
False
>>> power_of_4(8)
False
>>> power_of_4(17)
False
>>> power_of_4(64)
True
>>> power_of_4(-1)
Traceback (most recent call last):
...
ValueError: number must be positive
>>> power_of_4(1.2)
Traceback (most recent call last):
...
TypeError: number must be an integer
"""
if not isinstance(number, int):
raise TypeError("number must be an integer")
if number <= 0:
raise ValueError("number must be positive")
if number & (number - 1) == 0:
c = 0
while number:
c += 1
number >>= 1
return c % 2 == 1
else:
return False
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,11 +1,13 @@
from __future__ import annotations
from maths.greatest_common_divisor import greatest_common_divisor
def diophantine(a: int, b: int, c: int) -> tuple[float, float]:
"""
Diophantine Equation : Given integers a,b,c ( at least one of a and b != 0), the
diophantine equation a*x + b*y = c has a solution (where x and y are integers)
iff gcd(a,b) divides c.
iff greatest_common_divisor(a,b) divides c.
GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor )
@ -22,7 +24,7 @@ def diophantine(a: int, b: int, c: int) -> tuple[float, float]:
assert (
c % greatest_common_divisor(a, b) == 0
) # greatest_common_divisor(a,b) function implemented below
) # greatest_common_divisor(a,b) is in maths directory
(d, x, y) = extended_gcd(a, b) # extended_gcd(a,b) function implemented below
r = c / d
return (r * x, r * y)
@ -69,32 +71,6 @@ def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None:
print(x, y)
def greatest_common_divisor(a: int, b: int) -> int:
"""
Euclid's Lemma : d divides a and b, if and only if d divides a-b and b
Euclid's Algorithm
>>> greatest_common_divisor(7,5)
1
Note : In number theory, two integers a and b are said to be relatively prime,
mutually prime, or co-prime if the only positive integer (factor) that
divides both of them is 1 i.e., gcd(a,b) = 1.
>>> greatest_common_divisor(121, 11)
11
"""
if a < b:
a, b = b, a
while a % b != 0:
a, b = b, a % b
return b
def extended_gcd(a: int, b: int) -> tuple[int, int, int]:
"""
Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers

View File

@ -1,6 +1,8 @@
import random
import sys
from maths.greatest_common_divisor import gcd_by_iterative
from . import cryptomath_module as cryptomath
SYMBOLS = (
@ -26,7 +28,7 @@ def check_keys(key_a: int, key_b: int, mode: str) -> None:
"Key A must be greater than 0 and key B must "
f"be between 0 and {len(SYMBOLS) - 1}."
)
if cryptomath.gcd(key_a, len(SYMBOLS)) != 1:
if gcd_by_iterative(key_a, len(SYMBOLS)) != 1:
sys.exit(
f"Key A {key_a} and the symbol set size {len(SYMBOLS)} "
"are not relatively prime. Choose a different key."
@ -76,7 +78,7 @@ def get_random_key() -> int:
while True:
key_b = random.randint(2, len(SYMBOLS))
key_b = random.randint(2, len(SYMBOLS))
if cryptomath.gcd(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0:
if gcd_by_iterative(key_b, len(SYMBOLS)) == 1 and key_b % len(SYMBOLS) != 0:
return key_b * len(SYMBOLS) + key_b

View File

@ -1,11 +1,8 @@
def gcd(a: int, b: int) -> int:
while a != 0:
a, b = b % a, a
return b
from maths.greatest_common_divisor import gcd_by_iterative
def find_mod_inverse(a: int, m: int) -> int:
if gcd(a, m) != 1:
if gcd_by_iterative(a, m) != 1:
msg = f"mod inverse of {a!r} and {m!r} does not exist"
raise ValueError(msg)
u1, u2, u3 = 1, 0, a

View File

@ -1,11 +1,28 @@
from __future__ import annotations
def find_primitive(n: int) -> int | None:
for r in range(1, n):
def find_primitive(modulus: int) -> int | None:
"""
Find a primitive root modulo modulus, if one exists.
Args:
modulus : The modulus for which to find a primitive root.
Returns:
The primitive root if one exists, or None if there is none.
Examples:
>>> find_primitive(7) # Modulo 7 has primitive root 3
3
>>> find_primitive(11) # Modulo 11 has primitive root 2
2
>>> find_primitive(8) == None # Modulo 8 has no primitive root
True
"""
for r in range(1, modulus):
li = []
for x in range(n - 1):
val = pow(r, x, n)
for x in range(modulus - 1):
val = pow(r, x, modulus)
if val in li:
break
li.append(val)
@ -15,18 +32,22 @@ def find_primitive(n: int) -> int | None:
if __name__ == "__main__":
q = int(input("Enter a prime number q: "))
a = find_primitive(q)
if a is None:
print(f"Cannot find the primitive for the value: {a!r}")
import doctest
doctest.testmod()
prime = int(input("Enter a prime number q: "))
primitive_root = find_primitive(prime)
if primitive_root is None:
print(f"Cannot find the primitive for the value: {primitive_root!r}")
else:
a_private = int(input("Enter private key of A: "))
a_public = pow(a, a_private, q)
a_public = pow(primitive_root, a_private, prime)
b_private = int(input("Enter private key of B: "))
b_public = pow(a, b_private, q)
b_public = pow(primitive_root, b_private, prime)
a_secret = pow(b_public, a_private, q)
b_secret = pow(a_public, b_private, q)
a_secret = pow(b_public, a_private, prime)
b_secret = pow(a_public, b_private, prime)
print("The key value generated by A is: ", a_secret)
print("The key value generated by B is: ", b_secret)

View File

@ -0,0 +1,167 @@
"""
Python program for the Fractionated Morse Cipher.
The Fractionated Morse cipher first converts the plaintext to Morse code,
then enciphers fixed-size blocks of Morse code back to letters.
This procedure means plaintext letters are mixed into the ciphertext letters,
making it more secure than substitution ciphers.
http://practicalcryptography.com/ciphers/fractionated-morse-cipher/
"""
import string
MORSE_CODE_DICT = {
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"G": "--.",
"H": "....",
"I": "..",
"J": ".---",
"K": "-.-",
"L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
" ": "",
}
# Define possible trigrams of Morse code
MORSE_COMBINATIONS = [
"...",
"..-",
"..x",
".-.",
".--",
".-x",
".x.",
".x-",
".xx",
"-..",
"-.-",
"-.x",
"--.",
"---",
"--x",
"-x.",
"-x-",
"-xx",
"x..",
"x.-",
"x.x",
"x-.",
"x--",
"x-x",
"xx.",
"xx-",
"xxx",
]
# Create a reverse dictionary for Morse code
REVERSE_DICT = {value: key for key, value in MORSE_CODE_DICT.items()}
def encode_to_morse(plaintext: str) -> str:
"""Encode a plaintext message into Morse code.
Args:
plaintext: The plaintext message to encode.
Returns:
The Morse code representation of the plaintext message.
Example:
>>> encode_to_morse("defend the east")
'-..x.x..-.x.x-.x-..xx-x....x.xx.x.-x...x-'
"""
return "x".join([MORSE_CODE_DICT.get(letter.upper(), "") for letter in plaintext])
def encrypt_fractionated_morse(plaintext: str, key: str) -> str:
"""Encrypt a plaintext message using Fractionated Morse Cipher.
Args:
plaintext: The plaintext message to encrypt.
key: The encryption key.
Returns:
The encrypted ciphertext.
Example:
>>> encrypt_fractionated_morse("defend the east","Roundtable")
'ESOAVVLJRSSTRX'
"""
morse_code = encode_to_morse(plaintext)
key = key.upper() + string.ascii_uppercase
key = "".join(sorted(set(key), key=key.find))
# Ensure morse_code length is a multiple of 3
padding_length = 3 - (len(morse_code) % 3)
morse_code += "x" * padding_length
fractionated_morse_dict = {v: k for k, v in zip(key, MORSE_COMBINATIONS)}
fractionated_morse_dict["xxx"] = ""
encrypted_text = "".join(
[
fractionated_morse_dict[morse_code[i : i + 3]]
for i in range(0, len(morse_code), 3)
]
)
return encrypted_text
def decrypt_fractionated_morse(ciphertext: str, key: str) -> str:
"""Decrypt a ciphertext message encrypted with Fractionated Morse Cipher.
Args:
ciphertext: The ciphertext message to decrypt.
key: The decryption key.
Returns:
The decrypted plaintext message.
Example:
>>> decrypt_fractionated_morse("ESOAVVLJRSSTRX","Roundtable")
'DEFEND THE EAST'
"""
key = key.upper() + string.ascii_uppercase
key = "".join(sorted(set(key), key=key.find))
inverse_fractionated_morse_dict = dict(zip(key, MORSE_COMBINATIONS))
morse_code = "".join(
[inverse_fractionated_morse_dict.get(letter, "") for letter in ciphertext]
)
decrypted_text = "".join(
[REVERSE_DICT[code] for code in morse_code.split("x")]
).strip()
return decrypted_text
if __name__ == "__main__":
"""
Example usage of Fractionated Morse Cipher.
"""
plaintext = "defend the east"
print("Plain Text:", plaintext)
key = "ROUNDTABLE"
ciphertext = encrypt_fractionated_morse(plaintext, key)
print("Encrypted:", ciphertext)
decrypted_text = decrypt_fractionated_morse(ciphertext, key)
print("Decrypted:", decrypted_text)

View File

@ -39,19 +39,7 @@ import string
import numpy
def greatest_common_divisor(a: int, b: int) -> int:
"""
>>> greatest_common_divisor(4, 8)
4
>>> greatest_common_divisor(8, 4)
4
>>> greatest_common_divisor(4, 7)
1
>>> greatest_common_divisor(0, 10)
10
"""
return b if a == 0 else greatest_common_divisor(b % a, a)
from maths.greatest_common_divisor import greatest_common_divisor
class HillCipher:

View File

@ -2,6 +2,8 @@ import os
import random
import sys
from maths.greatest_common_divisor import gcd_by_iterative
from . import cryptomath_module, rabin_miller
@ -27,7 +29,7 @@ def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]:
# Generate e that is relatively prime to (p - 1) * (q - 1)
while True:
e = random.randrange(2 ** (key_size - 1), 2 ** (key_size))
if cryptomath_module.gcd(e, (p - 1) * (q - 1)) == 1:
if gcd_by_iterative(e, (p - 1) * (q - 1)) == 1:
break
# Calculate d that is mod inverse of e

View File

@ -0,0 +1,59 @@
"""
Find the Equilibrium Index of an Array.
Reference: https://www.geeksforgeeks.org/equilibrium-index-of-an-array/
Python doctests can be run with the following command:
python -m doctest -v equilibrium_index.py
Given a sequence arr[] of size n, this function returns
an equilibrium index (if any) or -1 if no equilibrium index exists.
The equilibrium index of an array is an index such that the sum of
elements at lower indexes is equal to the sum of elements at higher indexes.
Example Input:
arr = [-7, 1, 5, 2, -4, 3, 0]
Output: 3
"""
def equilibrium_index(arr: list[int], size: int) -> int:
"""
Find the equilibrium index of an array.
Args:
arr : The input array of integers.
size : The size of the array.
Returns:
int: The equilibrium index or -1 if no equilibrium index exists.
Examples:
>>> equilibrium_index([-7, 1, 5, 2, -4, 3, 0], 7)
3
>>> equilibrium_index([1, 2, 3, 4, 5], 5)
-1
>>> equilibrium_index([1, 1, 1, 1, 1], 5)
2
>>> equilibrium_index([2, 4, 6, 8, 10, 3], 6)
-1
"""
total_sum = sum(arr)
left_sum = 0
for i in range(size):
total_sum -= arr[i]
if left_sum == total_sum:
return i
left_sum += arr[i]
return -1
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,65 +1,167 @@
def is_palindrome(head):
from __future__ import annotations
from dataclasses import dataclass
@dataclass
class ListNode:
val: int = 0
next_node: ListNode | None = None
def is_palindrome(head: ListNode | None) -> bool:
"""
Check if a linked list is a palindrome.
Args:
head: The head of the linked list.
Returns:
bool: True if the linked list is a palindrome, False otherwise.
Examples:
>>> is_palindrome(None)
True
>>> is_palindrome(ListNode(1))
True
>>> is_palindrome(ListNode(1, ListNode(2)))
False
>>> is_palindrome(ListNode(1, ListNode(2, ListNode(1))))
True
>>> is_palindrome(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
True
"""
if not head:
return True
# split the list to two parts
fast, slow = head.next, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
second = slow.next
slow.next = None # Don't forget here! But forget still works!
fast: ListNode | None = head.next_node
slow: ListNode | None = head
while fast and fast.next_node:
fast = fast.next_node.next_node
slow = slow.next_node if slow else None
if slow:
# slow will always be defined,
# adding this check to resolve mypy static check
second = slow.next_node
slow.next_node = None # Don't forget here! But forget still works!
# reverse the second part
node = None
node: ListNode | None = None
while second:
nxt = second.next
second.next = node
nxt = second.next_node
second.next_node = node
node = second
second = nxt
# compare two parts
# second part has the same or one less node
while node:
while node and head:
if node.val != head.val:
return False
node = node.next
head = head.next
node = node.next_node
head = head.next_node
return True
def is_palindrome_stack(head):
if not head or not head.next:
def is_palindrome_stack(head: ListNode | None) -> bool:
"""
Check if a linked list is a palindrome using a stack.
Args:
head (ListNode): The head of the linked list.
Returns:
bool: True if the linked list is a palindrome, False otherwise.
Examples:
>>> is_palindrome_stack(None)
True
>>> is_palindrome_stack(ListNode(1))
True
>>> is_palindrome_stack(ListNode(1, ListNode(2)))
False
>>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(1))))
True
>>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
True
"""
if not head or not head.next_node:
return True
# 1. Get the midpoint (slow)
slow = fast = cur = head
while fast and fast.next:
fast, slow = fast.next.next, slow.next
slow: ListNode | None = head
fast: ListNode | None = head
while fast and fast.next_node:
fast = fast.next_node.next_node
slow = slow.next_node if slow else None
# 2. Push the second half into the stack
stack = [slow.val]
while slow.next:
slow = slow.next
stack.append(slow.val)
# slow will always be defined,
# adding this check to resolve mypy static check
if slow:
stack = [slow.val]
# 3. Comparison
while stack:
if stack.pop() != cur.val:
return False
cur = cur.next
# 2. Push the second half into the stack
while slow.next_node:
slow = slow.next_node
stack.append(slow.val)
# 3. Comparison
cur: ListNode | None = head
while stack and cur:
if stack.pop() != cur.val:
return False
cur = cur.next_node
return True
def is_palindrome_dict(head):
if not head or not head.next:
def is_palindrome_dict(head: ListNode | None) -> bool:
"""
Check if a linked list is a palindrome using a dictionary.
Args:
head (ListNode): The head of the linked list.
Returns:
bool: True if the linked list is a palindrome, False otherwise.
Examples:
>>> is_palindrome_dict(None)
True
>>> is_palindrome_dict(ListNode(1))
True
>>> is_palindrome_dict(ListNode(1, ListNode(2)))
False
>>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(1))))
True
>>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
True
>>> is_palindrome_dict(\
ListNode(\
1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1)))))))
False
"""
if not head or not head.next_node:
return True
d = {}
d: dict[int, list[int]] = {}
pos = 0
while head:
if head.val in d:
d[head.val].append(pos)
else:
d[head.val] = [pos]
head = head.next
head = head.next_node
pos += 1
checksum = pos - 1
middle = 0
@ -75,3 +177,9 @@ def is_palindrome_dict(head):
if middle > 1:
return False
return True
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -96,9 +96,16 @@ def test_nearest_neighbour(
def test_local_binary_pattern():
file_path = "digital_image_processing/image_data/lena.jpg"
# pull request 10161 before:
# "digital_image_processing/image_data/lena.jpg"
# after: "digital_image_processing/image_data/lena_small.jpg"
# Reading the image and converting it to grayscale.
from os import getenv # Speed up our Continuous Integration tests
file_name = "lena_small.jpg" if getenv("CI") else "lena.jpg"
file_path = f"digital_image_processing/image_data/{file_name}"
# Reading the image and converting it to grayscale
image = imread(file_path, 0)
# Test for get_neighbors_pixel function() return not None

View File

@ -0,0 +1,42 @@
"""
Given a list of stock prices calculate the maximum profit that can be made from a
single buy and sell of one share of stock. We only allowed to complete one buy
transaction and one sell transaction but must buy before we sell.
Example : prices = [7, 1, 5, 3, 6, 4]
max_profit will return 5 - which is by buying at price 1 and selling at price 6.
This problem can be solved using the concept of "GREEDY ALGORITHM".
We iterate over the price array once, keeping track of the lowest price point
(buy) and the maximum profit we can get at each point. The greedy choice at each point
is to either buy at the current price if it's less than our current buying price, or
sell at the current price if the profit is more than our current maximum profit.
"""
def max_profit(prices: list[int]) -> int:
"""
>>> max_profit([7, 1, 5, 3, 6, 4])
5
>>> max_profit([7, 6, 4, 3, 1])
0
"""
if not prices:
return 0
min_price = prices[0]
max_profit: int = 0
for price in prices:
min_price = min(price, min_price)
max_profit = max(price - min_price, max_profit)
return max_profit
if __name__ == "__main__":
import doctest
doctest.testmod()
print(max_profit([7, 1, 5, 3, 6, 4]))

View File

@ -0,0 +1,59 @@
"""
Binary Cross-Entropy (BCE) Loss Function
Description:
Quantifies dissimilarity between true labels (0 or 1) and predicted probabilities.
It's widely used in binary classification tasks.
Formula:
BCE = -Σ(y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred))
Source:
[Wikipedia - Cross entropy](https://en.wikipedia.org/wiki/Cross_entropy)
"""
import numpy as np
def binary_cross_entropy(
y_true: np.ndarray, y_pred: np.ndarray, epsilon: float = 1e-15
) -> float:
"""
Calculate the BCE Loss between true labels and predicted probabilities.
Parameters:
- y_true: True binary labels (0 or 1).
- y_pred: Predicted probabilities for class 1.
- epsilon: Small constant to avoid numerical instability.
Returns:
- bce_loss: Binary Cross-Entropy Loss.
Example Usage:
>>> true_labels = np.array([0, 1, 1, 0, 1])
>>> predicted_probs = np.array([0.2, 0.7, 0.9, 0.3, 0.8])
>>> binary_cross_entropy(true_labels, predicted_probs)
0.2529995012327421
>>> true_labels = np.array([0, 1, 1, 0, 1])
>>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2])
>>> binary_cross_entropy(true_labels, predicted_probs)
Traceback (most recent call last):
...
ValueError: Input arrays must have the same length.
"""
if len(y_true) != len(y_pred):
raise ValueError("Input arrays must have the same length.")
# Clip predicted probabilities to avoid log(0) and log(1)
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
# Calculate binary cross-entropy loss
bce_loss = -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
# Take the mean over all samples
return np.mean(bce_loss)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,52 @@
"""
Huber Loss Function
Description:
Huber loss function describes the penalty incurred by an estimation procedure.
It serves as a measure of the model's accuracy in regression tasks.
Formula:
Huber Loss = if |y_true - y_pred| <= delta then 0.5 * (y_true - y_pred)^2
else delta * |y_true - y_pred| - 0.5 * delta^2
Source:
[Wikipedia - Huber Loss](https://en.wikipedia.org/wiki/Huber_loss)
"""
import numpy as np
def huber_loss(y_true: np.ndarray, y_pred: np.ndarray, delta: float) -> float:
"""
Calculate the mean of Huber Loss.
Parameters:
- y_true: The true values (ground truth).
- y_pred: The predicted values.
Returns:
- huber_loss: The mean of Huber Loss between y_true and y_pred.
Example usage:
>>> true_values = np.array([0.9, 10.0, 2.0, 1.0, 5.2])
>>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2])
>>> np.isclose(huber_loss(true_values, predicted_values, 1.0), 2.102)
True
>>> true_labels = np.array([11.0, 21.0, 3.32, 4.0, 5.0])
>>> predicted_probs = np.array([8.3, 20.8, 2.9, 11.2, 5.0])
>>> np.isclose(huber_loss(true_labels, predicted_probs, 1.0), 1.80164)
True
"""
if len(y_true) != len(y_pred):
raise ValueError("Input arrays must have the same length.")
huber_mse = 0.5 * (y_true - y_pred) ** 2
huber_mae = delta * (np.abs(y_true - y_pred) - 0.5 * delta)
return np.where(np.abs(y_true - y_pred) <= delta, huber_mse, huber_mae).mean()
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,51 @@
"""
Mean Squared Error (MSE) Loss Function
Description:
MSE measures the mean squared difference between true values and predicted values.
It serves as a measure of the model's accuracy in regression tasks.
Formula:
MSE = (1/n) * Σ(y_true - y_pred)^2
Source:
[Wikipedia - Mean squared error](https://en.wikipedia.org/wiki/Mean_squared_error)
"""
import numpy as np
def mean_squared_error(y_true: np.ndarray, y_pred: np.ndarray) -> float:
"""
Calculate the Mean Squared Error (MSE) between two arrays.
Parameters:
- y_true: The true values (ground truth).
- y_pred: The predicted values.
Returns:
- mse: The Mean Squared Error between y_true and y_pred.
Example usage:
>>> true_values = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
>>> predicted_values = np.array([0.8, 2.1, 2.9, 4.2, 5.2])
>>> mean_squared_error(true_values, predicted_values)
0.028000000000000032
>>> true_labels = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
>>> predicted_probs = np.array([0.3, 0.8, 0.9, 0.2])
>>> mean_squared_error(true_labels, predicted_probs)
Traceback (most recent call last):
...
ValueError: Input arrays must have the same length.
"""
if len(y_true) != len(y_pred):
raise ValueError("Input arrays must have the same length.")
squared_errors = (y_true - y_pred) ** 2
return np.mean(squared_errors)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -10,14 +10,7 @@ satisfies the following modular arithmetic condition:
Examples of Carmichael Numbers: 561, 1105, ...
https://en.wikipedia.org/wiki/Carmichael_number
"""
def gcd(a: int, b: int) -> int:
if a < b:
return gcd(b, a)
if a % b == 0:
return b
return gcd(b, a % b)
from maths.greatest_common_divisor import greatest_common_divisor
def power(x: int, y: int, mod: int) -> int:
@ -33,7 +26,7 @@ def power(x: int, y: int, mod: int) -> int:
def is_carmichael_number(n: int) -> bool:
b = 2
while b < n:
if gcd(b, n) == 1 and power(b, n - 1, n) != 1:
if greatest_common_divisor(b, n) == 1 and power(b, n - 1, n) != 1:
return False
b += 1
return True

View File

@ -1,7 +1,6 @@
"""
https://en.wikipedia.org/wiki/Combination
"""
from math import factorial
def combinations(n: int, k: int) -> int:
@ -35,7 +34,11 @@ def combinations(n: int, k: int) -> int:
# to calculate a factorial of a negative number, which is not possible
if n < k or k < 0:
raise ValueError("Please enter positive integers for n and k where n >= k")
return factorial(n) // (factorial(k) * factorial(n - k))
res = 1
for i in range(k):
res *= n - i
res //= i + 1
return res
if __name__ == "__main__":

72
maths/germain_primes.py Normal file
View File

@ -0,0 +1,72 @@
"""
A Sophie Germain prime is any prime p, where 2p + 1 is also prime.
The second number, 2p + 1 is called a safe prime.
Examples of Germain primes include: 2, 3, 5, 11, 23
Their corresponding safe primes: 5, 7, 11, 23, 47
https://en.wikipedia.org/wiki/Safe_and_Sophie_Germain_primes
"""
from maths.prime_check import is_prime
def is_germain_prime(number: int) -> bool:
"""Checks if input number and 2*number + 1 are prime.
>>> is_germain_prime(3)
True
>>> is_germain_prime(11)
True
>>> is_germain_prime(4)
False
>>> is_germain_prime(23)
True
>>> is_germain_prime(13)
False
>>> is_germain_prime(20)
False
>>> is_germain_prime('abc')
Traceback (most recent call last):
...
TypeError: Input value must be a positive integer. Input value: abc
"""
if not isinstance(number, int) or number < 1:
msg = f"Input value must be a positive integer. Input value: {number}"
raise TypeError(msg)
return is_prime(number) and is_prime(2 * number + 1)
def is_safe_prime(number: int) -> bool:
"""Checks if input number and (number - 1)/2 are prime.
The smallest safe prime is 5, with the Germain prime is 2.
>>> is_safe_prime(5)
True
>>> is_safe_prime(11)
True
>>> is_safe_prime(1)
False
>>> is_safe_prime(2)
False
>>> is_safe_prime(3)
False
>>> is_safe_prime(47)
True
>>> is_safe_prime('abc')
Traceback (most recent call last):
...
TypeError: Input value must be a positive integer. Input value: abc
"""
if not isinstance(number, int) or number < 1:
msg = f"Input value must be a positive integer. Input value: {number}"
raise TypeError(msg)
return (number - 1) % 2 == 0 and is_prime(number) and is_prime((number - 1) // 2)
if __name__ == "__main__":
from doctest import testmod
testmod()

View File

@ -1,6 +1,8 @@
import unittest
from timeit import timeit
from maths.greatest_common_divisor import greatest_common_divisor
def least_common_multiple_slow(first_num: int, second_num: int) -> int:
"""
@ -20,26 +22,6 @@ def least_common_multiple_slow(first_num: int, second_num: int) -> int:
return common_mult
def greatest_common_divisor(a: int, b: int) -> int:
"""
Calculate Greatest Common Divisor (GCD).
see greatest_common_divisor.py
>>> greatest_common_divisor(24, 40)
8
>>> greatest_common_divisor(1, 1)
1
>>> greatest_common_divisor(1, 800)
1
>>> greatest_common_divisor(11, 37)
1
>>> greatest_common_divisor(3, 5)
1
>>> greatest_common_divisor(16, 4)
4
"""
return b if a == 0 else greatest_common_divisor(b % a, a)
def least_common_multiple_fast(first_num: int, second_num: int) -> int:
"""
Find the least common multiple of two numbers.

View File

@ -21,7 +21,6 @@ get_primes_between(pNumber1, pNumber2)
is_even(number)
is_odd(number)
gcd(number1, number2) // greatest common divisor
kg_v(number1, number2) // least common multiple
get_divisors(number) // all divisors of 'number' inclusive 1, number
is_perfect_number(number)
@ -40,6 +39,8 @@ goldbach(number) // Goldbach's assumption
from math import sqrt
from maths.greatest_common_divisor import gcd_by_iterative
def is_prime(number: int) -> bool:
"""
@ -317,39 +318,6 @@ def goldbach(number):
# ----------------------------------------------
def gcd(number1, number2):
"""
Greatest common divisor
input: two positive integer 'number1' and 'number2'
returns the greatest common divisor of 'number1' and 'number2'
"""
# precondition
assert (
isinstance(number1, int)
and isinstance(number2, int)
and (number1 >= 0)
and (number2 >= 0)
), "'number1' and 'number2' must been positive integer."
rest = 0
while number2 != 0:
rest = number1 % number2
number1 = number2
number2 = rest
# precondition
assert isinstance(number1, int) and (
number1 >= 0
), "'number' must been from type int and positive"
return number1
# ----------------------------------------------------
def kg_v(number1, number2):
"""
Least common multiple
@ -567,14 +535,14 @@ def simplify_fraction(numerator, denominator):
), "The arguments must been from type int and 'denominator' != 0"
# build the greatest common divisor of numerator and denominator.
gcd_of_fraction = gcd(abs(numerator), abs(denominator))
gcd_of_fraction = gcd_by_iterative(abs(numerator), abs(denominator))
# precondition
assert (
isinstance(gcd_of_fraction, int)
and (numerator % gcd_of_fraction == 0)
and (denominator % gcd_of_fraction == 0)
), "Error in function gcd(...,...)"
), "Error in function gcd_by_iterative(...,...)"
return (numerator // gcd_of_fraction, denominator // gcd_of_fraction)

View File

@ -0,0 +1,36 @@
"""
This script demonstrates the implementation of the Binary Step function.
It's an activation function in which the neuron is activated if the input is positive
or 0, else it is deactivated
It's a simple activation function which is mentioned in this wikipedia article:
https://en.wikipedia.org/wiki/Activation_function
"""
import numpy as np
def binary_step(vector: np.ndarray) -> np.ndarray:
"""
Implements the binary step function
Parameters:
vector (ndarray): A vector that consists of numeric values
Returns:
vector (ndarray): Input vector after applying binary step function
>>> vector = np.array([-1.2, 0, 2, 1.45, -3.7, 0.3])
>>> binary_step(vector)
array([0, 1, 1, 1, 0, 1])
"""
return np.where(vector >= 0, 1, 0)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -7,6 +7,7 @@ https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Mish
"""
import numpy as np
from softplus import softplus
def mish(vector: np.ndarray) -> np.ndarray:
@ -30,7 +31,7 @@ def mish(vector: np.ndarray) -> np.ndarray:
array([-0.00092952, -0.15113318, 0.33152014, -0.04745745])
"""
return vector * np.tanh(np.log(1 + np.exp(vector)))
return vector * np.tanh(softplus(vector))
if __name__ == "__main__":

View File

@ -0,0 +1,49 @@
"""
This script implements the Soboleva Modified Hyperbolic Tangent function.
The function applies the Soboleva Modified Hyperbolic Tangent function
to each element of the vector.
More details about the activation function can be found on:
https://en.wikipedia.org/wiki/Soboleva_modified_hyperbolic_tangent
"""
import numpy as np
def soboleva_modified_hyperbolic_tangent(
vector: np.ndarray, a_value: float, b_value: float, c_value: float, d_value: float
) -> np.ndarray:
"""
Implements the Soboleva Modified Hyperbolic Tangent function
Parameters:
vector (ndarray): A vector that consists of numeric values
a_value (float): parameter a of the equation
b_value (float): parameter b of the equation
c_value (float): parameter c of the equation
d_value (float): parameter d of the equation
Returns:
vector (ndarray): Input array after applying SMHT function
>>> vector = np.array([5.4, -2.4, 6.3, -5.23, 3.27, 0.56])
>>> soboleva_modified_hyperbolic_tangent(vector, 0.2, 0.4, 0.6, 0.8)
array([ 0.11075085, -0.28236685, 0.07861169, -0.1180085 , 0.22999056,
0.1566043 ])
"""
# Separate the numerator and denominator for simplicity
# Calculate the numerator and denominator element-wise
numerator = np.exp(a_value * vector) - np.exp(-b_value * vector)
denominator = np.exp(c_value * vector) + np.exp(-d_value * vector)
# Calculate and return the final result element-wise
return numerator / denominator
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,38 @@
"""
Squareplus Activation Function
Use Case: Squareplus designed to enhance positive values and suppress negative values.
For more detailed information, you can refer to the following link:
https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Squareplus
"""
import numpy as np
def squareplus(vector: np.ndarray, beta: float) -> np.ndarray:
"""
Implements the SquarePlus activation function.
Parameters:
vector (np.ndarray): The input array for the SquarePlus activation.
beta (float): size of the curved region
Returns:
np.ndarray: The input array after applying the SquarePlus activation.
Formula: f(x) = ( x + sqrt(x^2 + b) ) / 2
Examples:
>>> squareplus(np.array([2.3, 0.6, -2, -3.8]), beta=2)
array([2.5 , 1.06811457, 0.22474487, 0.12731349])
>>> squareplus(np.array([-9.2, -0.3, 0.45, -4.56]), beta=3)
array([0.0808119 , 0.72891979, 1.11977651, 0.15893419])
"""
return (vector + np.sqrt(vector**2 + beta)) / 2
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -4,52 +4,76 @@
# Function to print upper half of diamond (pyramid)
def floyd(n):
"""
Parameters:
n : size of pattern
Print the upper half of a diamond pattern with '*' characters.
Args:
n (int): Size of the pattern.
Examples:
>>> floyd(3)
' * \\n * * \\n* * * \\n'
>>> floyd(5)
' * \\n * * \\n * * * \\n * * * * \\n* * * * * \\n'
"""
result = ""
for i in range(n):
for _ in range(n - i - 1): # printing spaces
print(" ", end="")
result += " "
for _ in range(i + 1): # printing stars
print("* ", end="")
print()
result += "* "
result += "\n"
return result
# Function to print lower half of diamond (pyramid)
def reverse_floyd(n):
"""
Parameters:
n : size of pattern
Print the lower half of a diamond pattern with '*' characters.
Args:
n (int): Size of the pattern.
Examples:
>>> reverse_floyd(3)
'* * * \\n * * \\n * \\n '
>>> reverse_floyd(5)
'* * * * * \\n * * * * \\n * * * \\n * * \\n * \\n '
"""
result = ""
for i in range(n, 0, -1):
for _ in range(i, 0, -1): # printing stars
print("* ", end="")
print()
result += "* "
result += "\n"
for _ in range(n - i + 1, 0, -1): # printing spaces
print(" ", end="")
result += " "
return result
# Function to print complete diamond pattern of "*"
def pretty_print(n):
"""
Parameters:
n : size of pattern
Print a complete diamond pattern with '*' characters.
Args:
n (int): Size of the pattern.
Examples:
>>> pretty_print(0)
' ... .... nothing printing :('
>>> pretty_print(3)
' * \\n * * \\n* * * \\n* * * \\n * * \\n * \\n '
"""
if n <= 0:
print(" ... .... nothing printing :(")
return
floyd(n) # upper half
reverse_floyd(n) # lower half
return " ... .... nothing printing :("
upper_half = floyd(n) # upper half
lower_half = reverse_floyd(n) # lower half
return upper_half + lower_half
if __name__ == "__main__":
print(r"| /\ | |- | |- |--| |\ /| |-")
print(r"|/ \| |- |_ |_ |__| | \/ | |_")
K = 1
while K:
user_number = int(input("enter the number and , and see the magic : "))
print()
pretty_print(user_number)
K = int(input("press 0 to exit... and 1 to continue..."))
import doctest
print("Good Bye...")
doctest.testmod()

View File

@ -1,3 +1,5 @@
from maths.greatest_common_divisor import greatest_common_divisor
"""
Project Euler Problem 5: https://projecteuler.net/problem=5
@ -16,23 +18,6 @@ References:
"""
def greatest_common_divisor(x: int, y: int) -> int:
"""
Euclidean Greatest Common Divisor algorithm
>>> greatest_common_divisor(0, 0)
0
>>> greatest_common_divisor(23, 42)
1
>>> greatest_common_divisor(15, 33)
3
>>> greatest_common_divisor(12345, 67890)
15
"""
return x if y == 0 else greatest_common_divisor(y, x % y)
def lcm(x: int, y: int) -> int:
"""
Least Common Multiple.

View File

@ -0,0 +1,107 @@
"""
A Python implementation of the Median of Medians algorithm
to select pivots for quick_select, which is efficient for
calculating the value that would appear in the index of a
list if it would be sorted, even if it is not already
sorted. Search in time complexity O(n) at any rank
deterministically
https://en.wikipedia.org/wiki/Median_of_medians
"""
def median_of_five(arr: list) -> int:
"""
Return the median of the input list
:param arr: Array to find median of
:return: median of arr
>>> median_of_five([2, 4, 5, 7, 899])
5
>>> median_of_five([5, 7, 899, 54, 32])
32
>>> median_of_five([5, 4, 3, 2])
4
>>> median_of_five([3, 5, 7, 10, 2])
5
"""
arr = sorted(arr)
return arr[len(arr) // 2]
def median_of_medians(arr: list) -> int:
"""
Return a pivot to partition data on by calculating
Median of medians of input data
:param arr: The data to be checked (a list)
:return: median of medians of input array
>>> median_of_medians([2, 4, 5, 7, 899, 54, 32])
54
>>> median_of_medians([5, 7, 899, 54, 32])
32
>>> median_of_medians([5, 4, 3, 2])
4
>>> median_of_medians([3, 5, 7, 10, 2, 12])
12
"""
if len(arr) <= 5:
return median_of_five(arr)
medians = []
i = 0
while i < len(arr):
if (i + 4) <= len(arr):
medians.append(median_of_five(arr[i:].copy()))
else:
medians.append(median_of_five(arr[i : i + 5].copy()))
i += 5
return median_of_medians(medians)
def quick_select(arr: list, target: int) -> int:
"""
Two way partition the data into smaller and greater lists,
in relationship to the pivot
:param arr: The data to be searched (a list)
:param target: The rank to be searched
:return: element at rank target
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 5)
32
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 1)
2
>>> quick_select([5, 4, 3, 2], 2)
3
>>> quick_select([3, 5, 7, 10, 2, 12], 3)
5
"""
# Invalid Input
if target > len(arr):
return -1
# x is the estimated pivot by median of medians algorithm
x = median_of_medians(arr)
left = []
right = []
check = False
for i in range(len(arr)):
if arr[i] < x:
left.append(arr[i])
elif arr[i] > x:
right.append(arr[i])
elif arr[i] == x and not check:
check = True
else:
right.append(arr[i])
rank_x = len(left) + 1
if rank_x == target:
answer = x
elif rank_x > target:
answer = quick_select(left, target)
elif rank_x < target:
answer = quick_select(right, target - rank_x)
return answer
print(median_of_five([5, 4, 3, 2]))

View File

@ -1,19 +1,24 @@
def reverse_letters(input_str: str) -> str:
def reverse_letters(sentence: str, length: int = 0) -> str:
"""
Reverses letters in a given string without adjusting the position of the words
>>> reverse_letters('The cat in the hat')
'ehT tac ni eht tah'
>>> reverse_letters('The quick brown fox jumped over the lazy dog.')
'ehT kciuq nworb xof depmuj revo eht yzal .god'
>>> reverse_letters('Is this true?')
'sI siht ?eurt'
>>> reverse_letters("I love Python")
'I evol nohtyP'
Reverse all words that are longer than the given length of characters in a sentence.
If unspecified, length is taken as 0
>>> reverse_letters("Hey wollef sroirraw", 3)
'Hey fellow warriors'
>>> reverse_letters("nohtyP is nohtyP", 2)
'Python is Python'
>>> reverse_letters("1 12 123 1234 54321 654321", 0)
'1 21 321 4321 12345 123456'
>>> reverse_letters("racecar")
'racecar'
"""
return " ".join([word[::-1] for word in input_str.split()])
return " ".join(
"".join(word[::-1]) if len(word) > length else word for word in sentence.split()
)
if __name__ == "__main__":
import doctest
doctest.testmod()
print(reverse_letters("Hey wollef sroirraw"))

View File

@ -1,21 +0,0 @@
def reverse_long_words(sentence: str) -> str:
"""
Reverse all words that are longer than 4 characters in a sentence.
>>> reverse_long_words("Hey wollef sroirraw")
'Hey fellow warriors'
>>> reverse_long_words("nohtyP is nohtyP")
'Python is Python'
>>> reverse_long_words("1 12 123 1234 54321 654321")
'1 12 123 1234 12345 123456'
"""
return " ".join(
"".join(word[::-1]) if len(word) > 4 else word for word in sentence.split()
)
if __name__ == "__main__":
import doctest
doctest.testmod()
print(reverse_long_words("Hey wollef sroirraw"))

33
strings/strip.py Normal file
View File

@ -0,0 +1,33 @@
def strip(user_string: str, characters: str = " \t\n\r") -> str:
"""
Remove leading and trailing characters (whitespace by default) from a string.
Args:
user_string (str): The input string to be stripped.
characters (str, optional): Optional characters to be removed
(default is whitespace).
Returns:
str: The stripped string.
Examples:
>>> strip(" hello ")
'hello'
>>> strip("...world...", ".")
'world'
>>> strip("123hello123", "123")
'hello'
>>> strip("")
''
"""
start = 0
end = len(user_string)
while start < end and user_string[start] in characters:
start += 1
while end > start and user_string[end - 1] in characters:
end -= 1
return user_string[start:end]