Merge remote-tracking branch 'origin/naive-bayes-text-classifier' into naive-bayes-text-classifier

This commit is contained in:
ricca 2023-10-03 20:18:19 +02:00
commit 9d19489c1f
52 changed files with 1221 additions and 488 deletions

View File

@ -1,5 +1,5 @@
# https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md
ARG VARIANT=3.11-bookworm
ARG VARIANT=3.12-bookworm
FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
COPY requirements.txt /tmp/pip-tmp/
RUN python3 -m pip install --upgrade pip \

1
.devcontainer/README.md Normal file
View File

@ -0,0 +1 @@
https://code.visualstudio.com/docs/devcontainers/tutorial

View File

@ -4,10 +4,10 @@
"dockerfile": "Dockerfile",
"context": "..",
"args": {
// Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6
// Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"VARIANT": "3.11-bookworm",
"VARIANT": "3.12-bookworm",
}
},

2
.github/CODEOWNERS vendored
View File

@ -69,7 +69,7 @@
# /other/ @cclauss # TODO: Uncomment this line after Hacktoberfest
/project_euler/ @dhruvmanila
# /project_euler/
# /quantum/

View File

@ -9,10 +9,11 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: 3.11
python-version: 3.12
allow-prereleases: true
- uses: actions/cache@v3
with:
path: ~/.cache/pip

View File

@ -11,6 +11,6 @@ jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: pip install --user ruff
- run: ruff --format=github .
- run: ruff --output-format=github .

View File

@ -16,7 +16,7 @@ repos:
- id: auto-walrus
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.291
rev: v0.0.292
hooks:
- id: ruff
@ -33,7 +33,7 @@ repos:
- tomli
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "1.1.0"
rev: "1.2.0"
hooks:
- id: pyproject-fmt

View File

@ -73,7 +73,7 @@ pre-commit run --all-files --show-diff-on-failure
We want your work to be readable by others; therefore, we encourage you to note the following:
- Please write in Python 3.11+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
- Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments.
- Single letter variable names are *old school* so please avoid them unless their life only spans a few lines.
- Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not.

View File

@ -26,7 +26,6 @@
* [Hamiltonian Cycle](backtracking/hamiltonian_cycle.py)
* [Knight Tour](backtracking/knight_tour.py)
* [Minimax](backtracking/minimax.py)
* [Minmax](backtracking/minmax.py)
* [N Queens](backtracking/n_queens.py)
* [N Queens Math](backtracking/n_queens_math.py)
* [Power Sum](backtracking/power_sum.py)
@ -51,6 +50,7 @@
* [Index Of Rightmost Set Bit](bit_manipulation/index_of_rightmost_set_bit.py)
* [Is Even](bit_manipulation/is_even.py)
* [Is Power Of Two](bit_manipulation/is_power_of_two.py)
* [Missing Number](bit_manipulation/missing_number.py)
* [Numbers Different Signs](bit_manipulation/numbers_different_signs.py)
* [Reverse Bits](bit_manipulation/reverse_bits.py)
* [Single Bit Manipulation Operations](bit_manipulation/single_bit_manipulation_operations.py)
@ -132,7 +132,6 @@
* [Run Length Encoding](compression/run_length_encoding.py)
## Computer Vision
* [Cnn Classification](computer_vision/cnn_classification.py)
* [Flip Augmentation](computer_vision/flip_augmentation.py)
* [Haralick Descriptors](computer_vision/haralick_descriptors.py)
* [Harris Corner](computer_vision/harris_corner.py)
@ -232,6 +231,8 @@
* [Merge Two Lists](data_structures/linked_list/merge_two_lists.py)
* [Middle Element Of Linked List](data_structures/linked_list/middle_element_of_linked_list.py)
* [Print Reverse](data_structures/linked_list/print_reverse.py)
* [Reverse K Group](data_structures/linked_list/reverse_k_group.py)
* [Rotate To The Right](data_structures/linked_list/rotate_to_the_right.py)
* [Singly Linked List](data_structures/linked_list/singly_linked_list.py)
* [Skip List](data_structures/linked_list/skip_list.py)
* [Swap Nodes](data_structures/linked_list/swap_nodes.py)
@ -318,7 +319,6 @@
* [Floyd Warshall](dynamic_programming/floyd_warshall.py)
* [Integer Partition](dynamic_programming/integer_partition.py)
* [Iterating Through Submasks](dynamic_programming/iterating_through_submasks.py)
* [K Means Clustering Tensorflow](dynamic_programming/k_means_clustering_tensorflow.py)
* [Knapsack](dynamic_programming/knapsack.py)
* [Longest Common Subsequence](dynamic_programming/longest_common_subsequence.py)
* [Longest Common Substring](dynamic_programming/longest_common_substring.py)
@ -341,6 +341,7 @@
* [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py)
* [Regex Match](dynamic_programming/regex_match.py)
* [Rod Cutting](dynamic_programming/rod_cutting.py)
* [Smith Waterman](dynamic_programming/smith_waterman.py)
* [Subset Generation](dynamic_programming/subset_generation.py)
* [Sum Of Subset](dynamic_programming/sum_of_subset.py)
* [Tribonacci](dynamic_programming/tribonacci.py)
@ -380,9 +381,6 @@
* [Mandelbrot](fractals/mandelbrot.py)
* [Sierpinski Triangle](fractals/sierpinski_triangle.py)
## Fuzzy Logic
* [Fuzzy Operations](fuzzy_logic/fuzzy_operations.py)
## Genetic Algorithm
* [Basic String](genetic_algorithm/basic_string.py)
@ -513,8 +511,6 @@
* Local Weighted Learning
* [Local Weighted Learning](machine_learning/local_weighted_learning/local_weighted_learning.py)
* [Logistic Regression](machine_learning/logistic_regression.py)
* Lstm
* [Lstm Prediction](machine_learning/lstm/lstm_prediction.py)
* [Mfcc](machine_learning/mfcc.py)
* [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py)
* [Polynomial Regression](machine_learning/polynomial_regression.py)
@ -529,7 +525,6 @@
## Maths
* [Abs](maths/abs.py)
* [Add](maths/add.py)
* [Addition Without Arithmetic](maths/addition_without_arithmetic.py)
* [Aliquot Sum](maths/aliquot_sum.py)
* [Allocation Number](maths/allocation_number.py)
@ -567,7 +562,6 @@
* [Dual Number Automatic Differentiation](maths/dual_number_automatic_differentiation.py)
* [Entropy](maths/entropy.py)
* [Euclidean Distance](maths/euclidean_distance.py)
* [Euclidean Gcd](maths/euclidean_gcd.py)
* [Euler Method](maths/euler_method.py)
* [Euler Modified](maths/euler_modified.py)
* [Eulers Totient](maths/eulers_totient.py)
@ -611,7 +605,6 @@
* [Matrix Exponentiation](maths/matrix_exponentiation.py)
* [Max Sum Sliding Window](maths/max_sum_sliding_window.py)
* [Median Of Two Arrays](maths/median_of_two_arrays.py)
* [Miller Rabin](maths/miller_rabin.py)
* [Mobius Function](maths/mobius_function.py)
* [Modular Exponential](maths/modular_exponential.py)
* [Monte Carlo](maths/monte_carlo.py)
@ -677,6 +670,7 @@
* [Sylvester Sequence](maths/sylvester_sequence.py)
* [Tanh](maths/tanh.py)
* [Test Prime Check](maths/test_prime_check.py)
* [Three Sum](maths/three_sum.py)
* [Trapezoidal Rule](maths/trapezoidal_rule.py)
* [Triplet Sum](maths/triplet_sum.py)
* [Twin Prime](maths/twin_prime.py)
@ -747,6 +741,7 @@
* [Scoring Algorithm](other/scoring_algorithm.py)
* [Sdes](other/sdes.py)
* [Tower Of Hanoi](other/tower_of_hanoi.py)
* [Word Search](other/word_search.py)
## Physics
* [Altitude Pressure](physics/altitude_pressure.py)
@ -1067,17 +1062,7 @@
* [Sol1](project_euler/problem_800/sol1.py)
## Quantum
* [Bb84](quantum/bb84.py)
* [Deutsch Jozsa](quantum/deutsch_jozsa.py)
* [Half Adder](quantum/half_adder.py)
* [Not Gate](quantum/not_gate.py)
* [Q Fourier Transform](quantum/q_fourier_transform.py)
* [Q Full Adder](quantum/q_full_adder.py)
* [Quantum Entanglement](quantum/quantum_entanglement.py)
* [Quantum Teleportation](quantum/quantum_teleportation.py)
* [Ripple Adder Classic](quantum/ripple_adder_classic.py)
* [Single Qubit Measure](quantum/single_qubit_measure.py)
* [Superdense Coding](quantum/superdense_coding.py)
## Scheduling
* [First Come First Served](scheduling/first_come_first_served.py)
@ -1140,8 +1125,6 @@
* [Quick Sort](sorts/quick_sort.py)
* [Quick Sort 3 Partition](sorts/quick_sort_3_partition.py)
* [Radix Sort](sorts/radix_sort.py)
* [Random Normal Distribution Quicksort](sorts/random_normal_distribution_quicksort.py)
* [Random Pivot Quick Sort](sorts/random_pivot_quick_sort.py)
* [Recursive Bubble Sort](sorts/recursive_bubble_sort.py)
* [Recursive Insertion Sort](sorts/recursive_insertion_sort.py)
* [Recursive Mergesort Array](sorts/recursive_mergesort_array.py)

View File

@ -47,7 +47,7 @@ def combination_sum(candidates: list, target: int) -> list:
>>> combination_sum([-8, 2.3, 0], 1)
Traceback (most recent call last):
...
RecursionError: maximum recursion depth exceeded in comparison
RecursionError: maximum recursion depth exceeded
"""
path = [] # type: list[int]
answer = [] # type: list[int]

View File

@ -1,69 +0,0 @@
"""
Minimax helps to achieve maximum score in a game by checking all possible moves.
"""
from __future__ import annotations
import math
def minimax(
depth: int, node_index: int, is_max: bool, scores: list[int], height: float
) -> int:
"""
depth is current depth in game tree.
node_index is index of current node in scores[].
scores[] contains the leaves of game tree.
height is maximum height of game tree.
>>> scores = [90, 23, 6, 33, 21, 65, 123, 34423]
>>> height = math.log(len(scores), 2)
>>> minimax(0, 0, True, scores, height)
65
>>> minimax(-1, 0, True, scores, height)
Traceback (most recent call last):
...
ValueError: Depth cannot be less than 0
>>> minimax(0, 0, True, [], 2)
Traceback (most recent call last):
...
ValueError: Scores cannot be empty
>>> scores = [3, 5, 2, 9, 12, 5, 23, 23]
>>> height = math.log(len(scores), 2)
>>> minimax(0, 0, True, scores, height)
12
"""
if depth < 0:
raise ValueError("Depth cannot be less than 0")
if not scores:
raise ValueError("Scores cannot be empty")
if depth == height:
return scores[node_index]
return (
max(
minimax(depth + 1, node_index * 2, False, scores, height),
minimax(depth + 1, node_index * 2 + 1, False, scores, height),
)
if is_max
else min(
minimax(depth + 1, node_index * 2, True, scores, height),
minimax(depth + 1, node_index * 2 + 1, True, scores, height),
)
)
def main() -> None:
scores = [90, 23, 6, 33, 21, 65, 123, 34423]
height = math.log(len(scores), 2)
print(f"Optimal value : {minimax(0, 0, True, scores, height)}")
if __name__ == "__main__":
import doctest
doctest.testmod()
main()

View File

@ -0,0 +1,21 @@
def find_missing_number(nums: list[int]) -> int:
"""
Finds the missing number in a list of consecutive integers.
Args:
nums: A list of integers.
Returns:
The missing number.
Example:
>>> find_missing_number([0, 1, 3, 4])
2
"""
n = len(nums)
missing_number = n
for i in range(n):
missing_number ^= i ^ nums[i]
return missing_number

View File

@ -10,7 +10,7 @@ def permute_recursive(nums: list[int]) -> list[list[int]]:
return [[]]
for _ in range(len(nums)):
n = nums.pop(0)
permutations = permute_recursive(nums)
permutations = permute_recursive(nums.copy())
for perm in permutations:
perm.append(n)
result.extend(permutations)
@ -43,6 +43,6 @@ def permute_backtrack(nums: list[int]) -> list[list[int]]:
if __name__ == "__main__":
import doctest
res = permute_backtrack([1, 2, 3])
print(res)
result = permute_backtrack([1, 2, 3])
print(result)
doctest.testmod()

View File

@ -1,12 +1,11 @@
# https://en.wikipedia.org/wiki/Tree_traversal
from __future__ import annotations
from collections import deque
from collections.abc import Sequence
from collections.abc import Generator
from dataclasses import dataclass
from typing import Any
# https://en.wikipedia.org/wiki/Tree_traversal
@dataclass
class Node:
data: int
@ -31,44 +30,56 @@ def make_tree() -> Node | None:
return tree
def preorder(root: Node | None) -> list[int]:
def preorder(root: Node | None) -> Generator[int, None, None]:
"""
Pre-order traversal visits root node, left subtree, right subtree.
>>> preorder(make_tree())
>>> list(preorder(make_tree()))
[1, 2, 4, 5, 3]
"""
return [root.data, *preorder(root.left), *preorder(root.right)] if root else []
if not root:
return
yield root.data
yield from preorder(root.left)
yield from preorder(root.right)
def postorder(root: Node | None) -> list[int]:
def postorder(root: Node | None) -> Generator[int, None, None]:
"""
Post-order traversal visits left subtree, right subtree, root node.
>>> postorder(make_tree())
>>> list(postorder(make_tree()))
[4, 5, 2, 3, 1]
"""
return postorder(root.left) + postorder(root.right) + [root.data] if root else []
if not root:
return
yield from postorder(root.left)
yield from postorder(root.right)
yield root.data
def inorder(root: Node | None) -> list[int]:
def inorder(root: Node | None) -> Generator[int, None, None]:
"""
In-order traversal visits left subtree, root node, right subtree.
>>> inorder(make_tree())
>>> list(inorder(make_tree()))
[4, 2, 5, 1, 3]
"""
return [*inorder(root.left), root.data, *inorder(root.right)] if root else []
if not root:
return
yield from inorder(root.left)
yield root.data
yield from inorder(root.right)
def reverse_inorder(root: Node | None) -> list[int]:
def reverse_inorder(root: Node | None) -> Generator[int, None, None]:
"""
Reverse in-order traversal visits right subtree, root node, left subtree.
>>> reverse_inorder(make_tree())
>>> list(reverse_inorder(make_tree()))
[3, 1, 5, 2, 4]
"""
return (
[*reverse_inorder(root.right), root.data, *reverse_inorder(root.left)]
if root
else []
)
if not root:
return
yield from reverse_inorder(root.right)
yield root.data
yield from reverse_inorder(root.left)
def height(root: Node | None) -> int:
@ -82,119 +93,109 @@ def height(root: Node | None) -> int:
return (max(height(root.left), height(root.right)) + 1) if root else 0
def level_order(root: Node | None) -> Sequence[Node | None]:
def level_order(root: Node | None) -> Generator[int, None, None]:
"""
Returns a list of nodes value from a whole binary tree in Level Order Traverse.
Level Order traverse: Visit nodes of the tree level-by-level.
"""
output: list[Any] = []
if root is None:
return output
return
process_queue = deque([root])
while process_queue:
node = process_queue.popleft()
output.append(node.data)
yield node.data
if node.left:
process_queue.append(node.left)
if node.right:
process_queue.append(node.right)
return output
def get_nodes_from_left_to_right(
root: Node | None, level: int
) -> Sequence[Node | None]:
) -> Generator[int, None, None]:
"""
Returns a list of nodes value from a particular level:
Left to right direction of the binary tree.
"""
output: list[Any] = []
def populate_output(root: Node | None, level: int) -> None:
def populate_output(root: Node | None, level: int) -> Generator[int, None, None]:
if not root:
return
if level == 1:
output.append(root.data)
yield root.data
elif level > 1:
populate_output(root.left, level - 1)
populate_output(root.right, level - 1)
yield from populate_output(root.left, level - 1)
yield from populate_output(root.right, level - 1)
populate_output(root, level)
return output
yield from populate_output(root, level)
def get_nodes_from_right_to_left(
root: Node | None, level: int
) -> Sequence[Node | None]:
) -> Generator[int, None, None]:
"""
Returns a list of nodes value from a particular level:
Right to left direction of the binary tree.
"""
output: list[Any] = []
def populate_output(root: Node | None, level: int) -> None:
def populate_output(root: Node | None, level: int) -> Generator[int, None, None]:
if root is None:
return
if level == 1:
output.append(root.data)
yield root.data
elif level > 1:
populate_output(root.right, level - 1)
populate_output(root.left, level - 1)
yield from populate_output(root.right, level - 1)
yield from populate_output(root.left, level - 1)
populate_output(root, level)
return output
yield from populate_output(root, level)
def zigzag(root: Node | None) -> Sequence[Node | None] | list[Any]:
def zigzag(root: Node | None) -> Generator[int, None, None]:
"""
ZigZag traverse:
Returns a list of nodes value from left to right and right to left, alternatively.
"""
if root is None:
return []
output: list[Sequence[Node | None]] = []
return
flag = 0
height_tree = height(root)
for h in range(1, height_tree + 1):
if not flag:
output.append(get_nodes_from_left_to_right(root, h))
yield from get_nodes_from_left_to_right(root, h)
flag = 1
else:
output.append(get_nodes_from_right_to_left(root, h))
yield from get_nodes_from_right_to_left(root, h)
flag = 0
return output
def main() -> None: # Main function for testing.
# Create binary tree.
root = make_tree()
# All Traversals of the binary are as follows:
print(f"In-order Traversal: {inorder(root)}")
print(f"Reverse In-order Traversal: {reverse_inorder(root)}")
print(f"Pre-order Traversal: {preorder(root)}")
print(f"Post-order Traversal: {postorder(root)}", "\n")
print(f"In-order Traversal: {list(inorder(root))}")
print(f"Reverse In-order Traversal: {list(reverse_inorder(root))}")
print(f"Pre-order Traversal: {list(preorder(root))}")
print(f"Post-order Traversal: {list(postorder(root))}", "\n")
print(f"Height of Tree: {height(root)}", "\n")
print("Complete Level Order Traversal: ")
print(level_order(root), "\n")
print(f"{list(level_order(root))} \n")
print("Level-wise order Traversal: ")
for level in range(1, height(root) + 1):
print(f"Level {level}:", get_nodes_from_left_to_right(root, level=level))
print(f"Level {level}:", list(get_nodes_from_left_to_right(root, level=level)))
print("\nZigZag order Traversal: ")
print(zigzag(root))
print(f"{list(zigzag(root))}")
if __name__ == "__main__":

View File

@ -0,0 +1,118 @@
from __future__ import annotations
from collections.abc import Iterable, Iterator
from dataclasses import dataclass
@dataclass
class Node:
data: int
next_node: Node | None = None
class LinkedList:
def __init__(self, ints: Iterable[int]) -> None:
self.head: Node | None = None
for i in ints:
self.append(i)
def __iter__(self) -> Iterator[int]:
"""
>>> ints = []
>>> list(LinkedList(ints)) == ints
True
>>> ints = tuple(range(5))
>>> tuple(LinkedList(ints)) == ints
True
"""
node = self.head
while node:
yield node.data
node = node.next_node
def __len__(self) -> int:
"""
>>> for i in range(3):
... len(LinkedList(range(i))) == i
True
True
True
>>> len(LinkedList("abcdefgh"))
8
"""
return sum(1 for _ in self)
def __str__(self) -> str:
"""
>>> str(LinkedList([]))
''
>>> str(LinkedList(range(5)))
'0 -> 1 -> 2 -> 3 -> 4'
"""
return " -> ".join([str(node) for node in self])
def append(self, data: int) -> None:
"""
>>> ll = LinkedList([1, 2])
>>> tuple(ll)
(1, 2)
>>> ll.append(3)
>>> tuple(ll)
(1, 2, 3)
>>> ll.append(4)
>>> tuple(ll)
(1, 2, 3, 4)
>>> len(ll)
4
"""
if not self.head:
self.head = Node(data)
return
node = self.head
while node.next_node:
node = node.next_node
node.next_node = Node(data)
def reverse_k_nodes(self, group_size: int) -> None:
"""
reverse nodes within groups of size k
>>> ll = LinkedList([1, 2, 3, 4, 5])
>>> ll.reverse_k_nodes(2)
>>> tuple(ll)
(2, 1, 4, 3, 5)
>>> str(ll)
'2 -> 1 -> 4 -> 3 -> 5'
"""
if self.head is None or self.head.next_node is None:
return
length = len(self)
dummy_head = Node(0)
dummy_head.next_node = self.head
previous_node = dummy_head
while length >= group_size:
current_node = previous_node.next_node
assert current_node
next_node = current_node.next_node
for _ in range(1, group_size):
assert next_node, current_node
current_node.next_node = next_node.next_node
assert previous_node
next_node.next_node = previous_node.next_node
previous_node.next_node = next_node
next_node = current_node.next_node
previous_node = current_node
length -= group_size
self.head = dummy_head.next_node
if __name__ == "__main__":
import doctest
doctest.testmod()
ll = LinkedList([1, 2, 3, 4, 5])
print(f"Original Linked List: {ll}")
k = 2
ll.reverse_k_nodes(k)
print(f"After reversing groups of size {k}: {ll}")

View File

@ -0,0 +1,156 @@
from __future__ import annotations
from dataclasses import dataclass
@dataclass
class Node:
data: int
next_node: Node | None = None
def print_linked_list(head: Node | None) -> None:
"""
Print the entire linked list iteratively.
This function prints the elements of a linked list separated by '->'.
Parameters:
head (Node | None): The head of the linked list to be printed,
or None if the linked list is empty.
>>> head = insert_node(None, 0)
>>> head = insert_node(head, 2)
>>> head = insert_node(head, 1)
>>> print_linked_list(head)
0->2->1
>>> head = insert_node(head, 4)
>>> head = insert_node(head, 5)
>>> print_linked_list(head)
0->2->1->4->5
"""
if head is None:
return
while head.next_node is not None:
print(head.data, end="->")
head = head.next_node
print(head.data)
def insert_node(head: Node | None, data: int) -> Node:
"""
Insert a new node at the end of a linked list and return the new head.
Parameters:
head (Node | None): The head of the linked list.
data (int): The data to be inserted into the new node.
Returns:
Node: The new head of the linked list.
>>> head = insert_node(None, 10)
>>> head = insert_node(head, 9)
>>> head = insert_node(head, 8)
>>> print_linked_list(head)
10->9->8
"""
new_node = Node(data)
# If the linked list is empty, the new_node becomes the head
if head is None:
return new_node
temp_node = head
while temp_node.next_node:
temp_node = temp_node.next_node
temp_node.next_node = new_node # type: ignore
return head
def rotate_to_the_right(head: Node, places: int) -> Node:
"""
Rotate a linked list to the right by places times.
Parameters:
head: The head of the linked list.
places: The number of places to rotate.
Returns:
Node: The head of the rotated linked list.
>>> rotate_to_the_right(None, places=1)
Traceback (most recent call last):
...
ValueError: The linked list is empty.
>>> head = insert_node(None, 1)
>>> rotate_to_the_right(head, places=1) == head
True
>>> head = insert_node(None, 1)
>>> head = insert_node(head, 2)
>>> head = insert_node(head, 3)
>>> head = insert_node(head, 4)
>>> head = insert_node(head, 5)
>>> new_head = rotate_to_the_right(head, places=2)
>>> print_linked_list(new_head)
4->5->1->2->3
"""
# Check if the list is empty or has only one element
if not head:
raise ValueError("The linked list is empty.")
if head.next_node is None:
return head
# Calculate the length of the linked list
length = 1
temp_node = head
while temp_node.next_node is not None:
length += 1
temp_node = temp_node.next_node
# Adjust the value of places to avoid places longer than the list.
places %= length
if places == 0:
return head # As no rotation is needed.
# Find the new head position after rotation.
new_head_index = length - places
# Traverse to the new head position
temp_node = head
for _ in range(new_head_index - 1):
assert temp_node.next_node
temp_node = temp_node.next_node
# Update pointers to perform rotation
assert temp_node.next_node
new_head = temp_node.next_node
temp_node.next_node = None
temp_node = new_head
while temp_node.next_node:
temp_node = temp_node.next_node
temp_node.next_node = head
assert new_head
return new_head
if __name__ == "__main__":
import doctest
doctest.testmod()
head = insert_node(None, 5)
head = insert_node(head, 1)
head = insert_node(head, 2)
head = insert_node(head, 4)
head = insert_node(head, 3)
print("Original list: ", end="")
print_linked_list(head)
places = 3
new_head = rotate_to_the_right(head, places)
print(f"After {places} iterations: ", end="")
print_linked_list(new_head)

View File

@ -0,0 +1,193 @@
"""
https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm
The Smith-Waterman algorithm is a dynamic programming algorithm used for sequence
alignment. It is particularly useful for finding similarities between two sequences,
such as DNA or protein sequences. In this implementation, gaps are penalized
linearly, meaning that the score is reduced by a fixed amount for each gap introduced
in the alignment. However, it's important to note that the Smith-Waterman algorithm
supports other gap penalty methods as well.
"""
def score_function(
source_char: str,
target_char: str,
match: int = 1,
mismatch: int = -1,
gap: int = -2,
) -> int:
"""
Calculate the score for a character pair based on whether they match or mismatch.
Returns 1 if the characters match, -1 if they mismatch, and -2 if either of the
characters is a gap.
>>> score_function('A', 'A')
1
>>> score_function('A', 'C')
-1
>>> score_function('-', 'A')
-2
>>> score_function('A', '-')
-2
>>> score_function('-', '-')
-2
"""
if "-" in (source_char, target_char):
return gap
return match if source_char == target_char else mismatch
def smith_waterman(
query: str,
subject: str,
match: int = 1,
mismatch: int = -1,
gap: int = -2,
) -> list[list[int]]:
"""
Perform the Smith-Waterman local sequence alignment algorithm.
Returns a 2D list representing the score matrix. Each value in the matrix
corresponds to the score of the best local alignment ending at that point.
>>> smith_waterman('ACAC', 'CA')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('acac', 'ca')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('ACAC', 'ca')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('acac', 'CA')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('ACAC', '')
[[0], [0], [0], [0], [0]]
>>> smith_waterman('', 'CA')
[[0, 0, 0]]
>>> smith_waterman('ACAC', 'CA')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('acac', 'ca')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('ACAC', 'ca')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('acac', 'CA')
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]]
>>> smith_waterman('ACAC', '')
[[0], [0], [0], [0], [0]]
>>> smith_waterman('', 'CA')
[[0, 0, 0]]
>>> smith_waterman('AGT', 'AGT')
[[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]
>>> smith_waterman('AGT', 'GTA')
[[0, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 2, 0]]
>>> smith_waterman('AGT', 'GTC')
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0]]
>>> smith_waterman('AGT', 'G')
[[0, 0], [0, 0], [0, 1], [0, 0]]
>>> smith_waterman('G', 'AGT')
[[0, 0, 0, 0], [0, 0, 1, 0]]
>>> smith_waterman('AGT', 'AGTCT')
[[0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 2, 0, 0, 0], [0, 0, 0, 3, 1, 1]]
>>> smith_waterman('AGTCT', 'AGT')
[[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3], [0, 0, 0, 1], [0, 0, 0, 1]]
>>> smith_waterman('AGTCT', 'GTC')
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3], [0, 0, 1, 1]]
"""
# make both query and subject uppercase
query = query.upper()
subject = subject.upper()
# Initialize score matrix
m = len(query)
n = len(subject)
score = [[0] * (n + 1) for _ in range(m + 1)]
kwargs = {"match": match, "mismatch": mismatch, "gap": gap}
for i in range(1, m + 1):
for j in range(1, n + 1):
# Calculate scores for each cell
match = score[i - 1][j - 1] + score_function(
query[i - 1], subject[j - 1], **kwargs
)
delete = score[i - 1][j] + gap
insert = score[i][j - 1] + gap
# Take maximum score
score[i][j] = max(0, match, delete, insert)
return score
def traceback(score: list[list[int]], query: str, subject: str) -> str:
r"""
Perform traceback to find the optimal local alignment.
Starts from the highest scoring cell in the matrix and traces back recursively
until a 0 score is found. Returns the alignment strings.
>>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'ACAC', 'CA')
'CA\nCA'
>>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'acac', 'ca')
'CA\nCA'
>>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'ACAC', 'ca')
'CA\nCA'
>>> traceback([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 0, 2], [0, 1, 0]], 'acac', 'CA')
'CA\nCA'
>>> traceback([[0, 0, 0]], 'ACAC', '')
''
"""
# make both query and subject uppercase
query = query.upper()
subject = subject.upper()
# find the indices of the maximum value in the score matrix
max_value = float("-inf")
i_max = j_max = 0
for i, row in enumerate(score):
for j, value in enumerate(row):
if value > max_value:
max_value = value
i_max, j_max = i, j
# Traceback logic to find optimal alignment
i = i_max
j = j_max
align1 = ""
align2 = ""
gap = score_function("-", "-")
# guard against empty query or subject
if i == 0 or j == 0:
return ""
while i > 0 and j > 0:
if score[i][j] == score[i - 1][j - 1] + score_function(
query[i - 1], subject[j - 1]
):
# optimal path is a diagonal take both letters
align1 = query[i - 1] + align1
align2 = subject[j - 1] + align2
i -= 1
j -= 1
elif score[i][j] == score[i - 1][j] + gap:
# optimal path is a vertical
align1 = query[i - 1] + align1
align2 = f"-{align2}"
i -= 1
else:
# optimal path is a horizontal
align1 = f"-{align1}"
align2 = subject[j - 1] + align2
j -= 1
return f"{align1}\n{align2}"
if __name__ == "__main__":
query = "HEAGAWGHEE"
subject = "PAWHEAE"
score = smith_waterman(query, subject, match=1, mismatch=-1, gap=-2)
print(traceback(score, query, subject))

View File

@ -122,7 +122,7 @@ def local_weight_regression(
"""
y_pred = np.zeros(len(x_train)) # Initialize array of predictions
for i, item in enumerate(x_train):
y_pred[i] = item @ local_weight(item, x_train, y_train, tau)
y_pred[i] = np.dot(item, local_weight(item, x_train, y_train, tau)).item()
return y_pred

View File

@ -75,9 +75,13 @@ class MultinomialNBClassifier:
prior_class_i = data_class_i.shape[0] / n_examples
self.priors[i] = prior_class_i
tot_features_count = data_class_i.sum() # count of all features in class_i
features_count = np.array(data_class_i.sum(axis=0))[0] # count of each feature x_j in class_i
features_count = np.array(data_class_i.sum(axis=0))[
0
] # count of each feature x_j in class_i
for j, n_j in enumerate(features_count):
self.features_probs[i][j] = (self.alpha + n_j) / (tot_features_count + self.alpha * n_features)
self.features_probs[i][j] = (self.alpha + n_j) / (
tot_features_count + self.alpha * n_features
)
def predict(self, data: sparse.csr_matrix) -> np.array:
"""
@ -109,7 +113,10 @@ class MultinomialNBClassifier:
log_priors = np.log(self.priors)
for instance in data:
theta = instance.multiply(log_features_probs).sum(axis=1)
likelihood = [log_prior_class_i + theta[i] for i, log_prior_class_i in enumerate(log_priors)]
likelihood = [
log_prior_class_i + theta[i]
for i, log_prior_class_i in enumerate(log_priors)
]
y_pred.append(self.classes[np.argmax(likelihood)])
return np.array(y_pred)
@ -118,13 +125,13 @@ def main() -> None:
"""
Performs the text classification on the twenty_newsgroup dataset from sklearn
"""
newsgroups_train = fetch_20newsgroups(subset='train')
newsgroups_test = fetch_20newsgroups(subset='test')
x_train = newsgroups_train['data']
y_train = newsgroups_train['target']
x_test = newsgroups_test['data']
y_test = newsgroups_test['target']
vectorizer = TfidfVectorizer(stop_words='english')
newsgroups_train = fetch_20newsgroups(subset="train")
newsgroups_test = fetch_20newsgroups(subset="test")
x_train = newsgroups_train["data"]
y_train = newsgroups_train["target"]
x_test = newsgroups_test["data"]
y_test = newsgroups_test["target"]
vectorizer = TfidfVectorizer(stop_words="english")
x_train = vectorizer.fit_transform(x_train)
x_test = vectorizer.transform(x_test)
@ -133,10 +140,12 @@ def main() -> None:
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
print("Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred)))
print(
"Accuracy of naive bayes text classifier: "
+ str(accuracy_score(y_test, y_pred))
)
if __name__ == "__main__":
main()
doctest.testmod()

View File

@ -1,19 +0,0 @@
"""
Just to check
"""
def add(a: float, b: float) -> float:
"""
>>> add(2, 2)
4
>>> add(2, -2)
0
"""
return a + b
if __name__ == "__main__":
a = 5
b = 6
print(f"The sum of {a} + {b} is {add(a, b)}")

View File

@ -1,4 +1,4 @@
def bin_exp_mod(a, n, b):
def bin_exp_mod(a: int, n: int, b: int) -> int:
"""
>>> bin_exp_mod(3, 4, 5)
1
@ -13,7 +13,7 @@ def bin_exp_mod(a, n, b):
if n % 2 == 1:
return (bin_exp_mod(a, n - 1, b) * a) % b
r = bin_exp_mod(a, n / 2, b)
r = bin_exp_mod(a, n // 2, b)
return (r * r) % b

View File

@ -4,7 +4,7 @@
# Time Complexity : O(logn)
def binary_exponentiation(a, n):
def binary_exponentiation(a: int, n: int) -> int:
if n == 0:
return 1
@ -12,7 +12,7 @@ def binary_exponentiation(a, n):
return binary_exponentiation(a, n - 1) * a
else:
b = binary_exponentiation(a, n / 2)
b = binary_exponentiation(a, n // 2)
return b * b

View File

@ -1,50 +0,0 @@
"""
* Binary Exponentiation with Multiplication
* This is a method to find a*b in a time complexity of O(log b)
* This is one of the most commonly used methods of finding result of multiplication.
* Also useful in cases where solution to (a*b)%c is required,
* where a,b,c can be numbers over the computers calculation limits.
* Done using iteration, can also be done using recursion
* @author chinmoy159
* @version 1.0 dated 10/08/2017
"""
def b_expo(a, b):
res = 0
while b > 0:
if b & 1:
res += a
a += a
b >>= 1
return res
def b_expo_mod(a, b, c):
res = 0
while b > 0:
if b & 1:
res = ((res % c) + (a % c)) % c
a += a
b >>= 1
return res
"""
* Wondering how this method works !
* It's pretty simple.
* Let's say you need to calculate a ^ b
* RULE 1 : a * b = (a+a) * (b/2) ---- example : 4 * 4 = (4+4) * (4/2) = 8 * 2
* RULE 2 : IF b is ODD, then ---- a * b = a + (a * (b - 1)) :: where (b - 1) is even.
* Once b is even, repeat the process to get a * b
* Repeat the process till b = 1 OR b = 0, because a*1 = a AND a*0 = 0
*
* As far as the modulo is concerned,
* the fact : (a+b) % c = ((a%c) + (b%c)) % c
* Now apply RULE 1 OR 2, whichever is required.
"""

View File

@ -11,7 +11,7 @@
"""
def b_expo(a, b):
def b_expo(a: int, b: int) -> int:
res = 1
while b > 0:
if b & 1:
@ -23,7 +23,7 @@ def b_expo(a, b):
return res
def b_expo_mod(a, b, c):
def b_expo_mod(a: int, b: int, c: int) -> int:
res = 1
while b > 0:
if b & 1:

View File

@ -0,0 +1,101 @@
"""
Binary Multiplication
This is a method to find a*b in a time complexity of O(log b)
This is one of the most commonly used methods of finding result of multiplication.
Also useful in cases where solution to (a*b)%c is required,
where a,b,c can be numbers over the computers calculation limits.
Done using iteration, can also be done using recursion
Let's say you need to calculate a * b
RULE 1 : a * b = (a+a) * (b/2) ---- example : 4 * 4 = (4+4) * (4/2) = 8 * 2
RULE 2 : IF b is odd, then ---- a * b = a + (a * (b - 1)), where (b - 1) is even.
Once b is even, repeat the process to get a * b
Repeat the process until b = 1 or b = 0, because a*1 = a and a*0 = 0
As far as the modulo is concerned,
the fact : (a+b) % c = ((a%c) + (b%c)) % c
Now apply RULE 1 or 2, whichever is required.
@author chinmoy159
"""
def binary_multiply(a: int, b: int) -> int:
"""
Multiply 'a' and 'b' using bitwise multiplication.
Parameters:
a (int): The first number.
b (int): The second number.
Returns:
int: a * b
Examples:
>>> binary_multiply(2, 3)
6
>>> binary_multiply(5, 0)
0
>>> binary_multiply(3, 4)
12
>>> binary_multiply(10, 5)
50
>>> binary_multiply(0, 5)
0
>>> binary_multiply(2, 1)
2
>>> binary_multiply(1, 10)
10
"""
res = 0
while b > 0:
if b & 1:
res += a
a += a
b >>= 1
return res
def binary_mod_multiply(a: int, b: int, modulus: int) -> int:
"""
Calculate (a * b) % c using binary multiplication and modular arithmetic.
Parameters:
a (int): The first number.
b (int): The second number.
modulus (int): The modulus.
Returns:
int: (a * b) % modulus.
Examples:
>>> binary_mod_multiply(2, 3, 5)
1
>>> binary_mod_multiply(5, 0, 7)
0
>>> binary_mod_multiply(3, 4, 6)
0
>>> binary_mod_multiply(10, 5, 13)
11
>>> binary_mod_multiply(2, 1, 5)
2
>>> binary_mod_multiply(1, 10, 3)
1
"""
res = 0
while b > 0:
if b & 1:
res = ((res % modulus) + (a % modulus)) % modulus
a += a
b >>= 1
return res
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,4 +1,4 @@
def binomial_coefficient(n, r):
def binomial_coefficient(n: int, r: int) -> int:
"""
Find binomial coefficient using pascals triangle.

60
maths/double_factorial.py Normal file
View File

@ -0,0 +1,60 @@
def double_factorial_recursive(n: int) -> int:
"""
Compute double factorial using recursive method.
Recursion can be costly for large numbers.
To learn about the theory behind this algorithm:
https://en.wikipedia.org/wiki/Double_factorial
>>> from math import prod
>>> all(double_factorial_recursive(i) == prod(range(i, 0, -2)) for i in range(20))
True
>>> double_factorial_recursive(0.1)
Traceback (most recent call last):
...
ValueError: double_factorial_recursive() only accepts integral values
>>> double_factorial_recursive(-1)
Traceback (most recent call last):
...
ValueError: double_factorial_recursive() not defined for negative values
"""
if not isinstance(n, int):
raise ValueError("double_factorial_recursive() only accepts integral values")
if n < 0:
raise ValueError("double_factorial_recursive() not defined for negative values")
return 1 if n <= 1 else n * double_factorial_recursive(n - 2)
def double_factorial_iterative(num: int) -> int:
"""
Compute double factorial using iterative method.
To learn about the theory behind this algorithm:
https://en.wikipedia.org/wiki/Double_factorial
>>> from math import prod
>>> all(double_factorial_iterative(i) == prod(range(i, 0, -2)) for i in range(20))
True
>>> double_factorial_iterative(0.1)
Traceback (most recent call last):
...
ValueError: double_factorial_iterative() only accepts integral values
>>> double_factorial_iterative(-1)
Traceback (most recent call last):
...
ValueError: double_factorial_iterative() not defined for negative values
"""
if not isinstance(num, int):
raise ValueError("double_factorial_iterative() only accepts integral values")
if num < 0:
raise ValueError("double_factorial_iterative() not defined for negative values")
value = 1
for i in range(num, 0, -2):
value *= i
return value
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,33 +0,0 @@
def double_factorial(num: int) -> int:
"""
Compute double factorial using iterative method.
To learn about the theory behind this algorithm:
https://en.wikipedia.org/wiki/Double_factorial
>>> import math
>>> all(double_factorial(i) == math.prod(range(i, 0, -2)) for i in range(20))
True
>>> double_factorial(0.1)
Traceback (most recent call last):
...
ValueError: double_factorial() only accepts integral values
>>> double_factorial(-1)
Traceback (most recent call last):
...
ValueError: double_factorial() not defined for negative values
"""
if not isinstance(num, int):
raise ValueError("double_factorial() only accepts integral values")
if num < 0:
raise ValueError("double_factorial() not defined for negative values")
value = 1
for i in range(num, 0, -2):
value *= i
return value
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -1,31 +0,0 @@
def double_factorial(n: int) -> int:
"""
Compute double factorial using recursive method.
Recursion can be costly for large numbers.
To learn about the theory behind this algorithm:
https://en.wikipedia.org/wiki/Double_factorial
>>> import math
>>> all(double_factorial(i) == math.prod(range(i, 0, -2)) for i in range(20))
True
>>> double_factorial(0.1)
Traceback (most recent call last):
...
ValueError: double_factorial() only accepts integral values
>>> double_factorial(-1)
Traceback (most recent call last):
...
ValueError: double_factorial() not defined for negative values
"""
if not isinstance(n, int):
raise ValueError("double_factorial() only accepts integral values")
if n < 0:
raise ValueError("double_factorial() not defined for negative values")
return 1 if n <= 1 else n * double_factorial(n - 2)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -17,13 +17,13 @@ def maclaurin_sin(theta: float, accuracy: int = 30) -> float:
>>> all(isclose(maclaurin_sin(x, 50), sin(x)) for x in range(-25, 25))
True
>>> maclaurin_sin(10)
-0.544021110889369
-0.5440211108893691
>>> maclaurin_sin(-10)
0.5440211108893703
0.5440211108893704
>>> maclaurin_sin(10, 15)
-0.5440211108893689
-0.544021110889369
>>> maclaurin_sin(-10, 15)
0.5440211108893703
0.5440211108893704
>>> maclaurin_sin("10")
Traceback (most recent call last):
...
@ -69,11 +69,11 @@ def maclaurin_cos(theta: float, accuracy: int = 30) -> float:
>>> all(isclose(maclaurin_cos(x, 50), cos(x)) for x in range(-25, 25))
True
>>> maclaurin_cos(5)
0.28366218546322675
0.2836621854632268
>>> maclaurin_cos(-5)
0.2836621854632266
0.2836621854632265
>>> maclaurin_cos(10, 15)
-0.8390715290764525
-0.8390715290764524
>>> maclaurin_cos(-10, 15)
-0.8390715290764521
>>> maclaurin_cos("10")

View File

@ -1,51 +0,0 @@
import random
from .binary_exp_mod import bin_exp_mod
# This is a probabilistic check to test primality, useful for big numbers!
# if it's a prime, it will return true
# if it's not a prime, the chance of it returning true is at most 1/4**prec
def is_prime_big(n, prec=1000):
"""
>>> from maths.prime_check import is_prime
>>> # all(is_prime_big(i) == is_prime(i) for i in range(1000)) # 3.45s
>>> all(is_prime_big(i) == is_prime(i) for i in range(256))
True
"""
if n < 2:
return False
if n % 2 == 0:
return n == 2
# this means n is odd
d = n - 1
exp = 0
while d % 2 == 0:
d /= 2
exp += 1
# n - 1=d*(2**exp)
count = 0
while count < prec:
a = random.randint(2, n - 1)
b = bin_exp_mod(a, d, n)
if b != 1:
flag = True
for _ in range(exp):
if b == n - 1:
flag = False
break
b = b * b
b %= n
if flag:
return False
count += 1
return True
if __name__ == "__main__":
n = abs(int(input("Enter bound : ").strip()))
print("Here's the list of primes:")
print(", ".join(str(i) for i in range(n + 1) if is_prime_big(i)))

47
maths/three_sum.py Normal file
View File

@ -0,0 +1,47 @@
"""
https://en.wikipedia.org/wiki/3SUM
"""
def three_sum(nums: list[int]) -> list[list[int]]:
"""
Find all unique triplets in a sorted array of integers that sum up to zero.
Args:
nums: A sorted list of integers.
Returns:
A list of lists containing unique triplets that sum up to zero.
>>> three_sum([-1, 0, 1, 2, -1, -4])
[[-1, -1, 2], [-1, 0, 1]]
>>> three_sum([1, 2, 3, 4])
[]
"""
nums.sort()
ans = []
for i in range(len(nums) - 2):
if i == 0 or (nums[i] != nums[i - 1]):
low, high, c = i + 1, len(nums) - 1, 0 - nums[i]
while low < high:
if nums[low] + nums[high] == c:
ans.append([nums[i], nums[low], nums[high]])
while low < high and nums[low] == nums[low + 1]:
low += 1
while low < high and nums[high] == nums[high - 1]:
high -= 1
low += 1
high -= 1
elif nums[low] + nums[high] < c:
low += 1
else:
high -= 1
return ans
if __name__ == "__main__":
import doctest
doctest.testmod()

396
other/word_search.py Normal file
View File

@ -0,0 +1,396 @@
"""
Creates a random wordsearch with eight different directions
that are best described as compass locations.
@ https://en.wikipedia.org/wiki/Word_search
"""
from random import choice, randint, shuffle
# The words to display on the word search -
# can be made dynamic by randonly selecting a certain number of
# words from a predefined word file, while ensuring the character
# count fits within the matrix size (n x m)
WORDS = ["cat", "dog", "snake", "fish"]
WIDTH = 10
HEIGHT = 10
class WordSearch:
"""
>>> ws = WordSearch(WORDS, WIDTH, HEIGHT)
>>> ws.board # doctest: +ELLIPSIS
[[None, ..., None], ..., [None, ..., None]]
>>> ws.generate_board()
"""
def __init__(self, words: list[str], width: int, height: int) -> None:
self.words = words
self.width = width
self.height = height
# Board matrix holding each letter
self.board: list[list[str | None]] = [[None] * width for _ in range(height)]
def insert_north(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_north("cat", [2], [2])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, 't'],
[None, None, 'a'],
[None, None, 'c']]
>>> ws.insert_north("at", [0, 1, 2], [2, 1])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, 't', 't'],
[None, 'a', 'a'],
[None, None, 'c']]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space above the row to fit in the word
if word_length > row + 1:
continue
# Attempt to insert the word into each column
for col in cols:
# Only check to be made here is if there are existing letters
# above the column that will be overwritten
letters_above = [self.board[row - i][col] for i in range(word_length)]
if all(letter is None for letter in letters_above):
# Successful, insert the word north
for i in range(word_length):
self.board[row - i][col] = word[i]
return
def insert_northeast(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_northeast("cat", [2], [0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, 't'],
[None, 'a', None],
['c', None, None]]
>>> ws.insert_northeast("at", [0, 1], [2, 1, 0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, 't', 't'],
['a', 'a', None],
['c', None, None]]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space for the word above the row
if word_length > row + 1:
continue
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the right of the word as well as above
if word_length + col > self.width:
continue
# Check if there are existing letters
# to the right of the column that will be overwritten
letters_diagonal_left = [
self.board[row - i][col + i] for i in range(word_length)
]
if all(letter is None for letter in letters_diagonal_left):
# Successful, insert the word northeast
for i in range(word_length):
self.board[row - i][col + i] = word[i]
return
def insert_east(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_east("cat", [1], [0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, None],
['c', 'a', 't'],
[None, None, None]]
>>> ws.insert_east("at", [1, 0], [2, 1, 0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, 'a', 't'],
['c', 'a', 't'],
[None, None, None]]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the right of the word
if word_length + col > self.width:
continue
# Check if there are existing letters
# to the right of the column that will be overwritten
letters_left = [self.board[row][col + i] for i in range(word_length)]
if all(letter is None for letter in letters_left):
# Successful, insert the word east
for i in range(word_length):
self.board[row][col + i] = word[i]
return
def insert_southeast(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_southeast("cat", [0], [0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['c', None, None],
[None, 'a', None],
[None, None, 't']]
>>> ws.insert_southeast("at", [1, 0], [2, 1, 0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['c', None, None],
['a', 'a', None],
[None, 't', 't']]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space for the word below the row
if word_length + row > self.height:
continue
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the right of the word as well as below
if word_length + col > self.width:
continue
# Check if there are existing letters
# to the right of the column that will be overwritten
letters_diagonal_left = [
self.board[row + i][col + i] for i in range(word_length)
]
if all(letter is None for letter in letters_diagonal_left):
# Successful, insert the word southeast
for i in range(word_length):
self.board[row + i][col + i] = word[i]
return
def insert_south(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_south("cat", [0], [0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['c', None, None],
['a', None, None],
['t', None, None]]
>>> ws.insert_south("at", [2, 1, 0], [0, 1, 2])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['c', None, None],
['a', 'a', None],
['t', 't', None]]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space below the row to fit in the word
if word_length + row > self.height:
continue
# Attempt to insert the word into each column
for col in cols:
# Only check to be made here is if there are existing letters
# below the column that will be overwritten
letters_below = [self.board[row + i][col] for i in range(word_length)]
if all(letter is None for letter in letters_below):
# Successful, insert the word south
for i in range(word_length):
self.board[row + i][col] = word[i]
return
def insert_southwest(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_southwest("cat", [0], [2])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, 'c'],
[None, 'a', None],
['t', None, None]]
>>> ws.insert_southwest("at", [1, 2], [2, 1, 0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, 'c'],
[None, 'a', 'a'],
['t', 't', None]]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space for the word below the row
if word_length + row > self.height:
continue
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the left of the word as well as below
if word_length > col + 1:
continue
# Check if there are existing letters
# to the right of the column that will be overwritten
letters_diagonal_left = [
self.board[row + i][col - i] for i in range(word_length)
]
if all(letter is None for letter in letters_diagonal_left):
# Successful, insert the word southwest
for i in range(word_length):
self.board[row + i][col - i] = word[i]
return
def insert_west(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_west("cat", [1], [2])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[[None, None, None],
['t', 'a', 'c'],
[None, None, None]]
>>> ws.insert_west("at", [1, 0], [1, 2, 0])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['t', 'a', None],
['t', 'a', 'c'],
[None, None, None]]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the left of the word
if word_length > col + 1:
continue
# Check if there are existing letters
# to the left of the column that will be overwritten
letters_left = [self.board[row][col - i] for i in range(word_length)]
if all(letter is None for letter in letters_left):
# Successful, insert the word west
for i in range(word_length):
self.board[row][col - i] = word[i]
return
def insert_northwest(self, word: str, rows: list[int], cols: list[int]) -> None:
"""
>>> ws = WordSearch(WORDS, 3, 3)
>>> ws.insert_northwest("cat", [2], [2])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['t', None, None],
[None, 'a', None],
[None, None, 'c']]
>>> ws.insert_northwest("at", [1, 2], [0, 1])
>>> ws.board # doctest: +NORMALIZE_WHITESPACE
[['t', None, None],
['t', 'a', None],
[None, 'a', 'c']]
"""
word_length = len(word)
# Attempt to insert the word into each row and when successful, exit
for row in rows:
# Check if there is space for the word above the row
if word_length > row + 1:
continue
# Attempt to insert the word into each column
for col in cols:
# Check if there is space to the left of the word as well as above
if word_length > col + 1:
continue
# Check if there are existing letters
# to the right of the column that will be overwritten
letters_diagonal_left = [
self.board[row - i][col - i] for i in range(word_length)
]
if all(letter is None for letter in letters_diagonal_left):
# Successful, insert the word northwest
for i in range(word_length):
self.board[row - i][col - i] = word[i]
return
def generate_board(self) -> None:
"""
Generates a board with a random direction for each word.
>>> wt = WordSearch(WORDS, WIDTH, HEIGHT)
>>> wt.generate_board()
>>> len(list(filter(lambda word: word is not None, sum(wt.board, start=[])))
... ) == sum(map(lambda word: len(word), WORDS))
True
"""
directions = (
self.insert_north,
self.insert_northeast,
self.insert_east,
self.insert_southeast,
self.insert_south,
self.insert_southwest,
self.insert_west,
self.insert_northwest,
)
for word in self.words:
# Shuffle the row order and column order that is used when brute forcing
# the insertion of the word
rows, cols = list(range(self.height)), list(range(self.width))
shuffle(rows)
shuffle(cols)
# Insert the word via the direction
choice(directions)(word, rows, cols)
def visualise_word_search(
board: list[list[str | None]] | None = None, *, add_fake_chars: bool = True
) -> None:
"""
Graphically displays the word search in the terminal.
>>> ws = WordSearch(WORDS, 5, 5)
>>> ws.insert_north("cat", [4], [4])
>>> visualise_word_search(
... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE
# # # # #
# # # # #
# # # # t
# # # # a
# # # # c
>>> ws.insert_northeast("snake", [4], [4, 3, 2, 1, 0])
>>> visualise_word_search(
... ws.board, add_fake_chars=False) # doctest: +NORMALIZE_WHITESPACE
# # # # e
# # # k #
# # a # t
# n # # a
s # # # c
"""
if board is None:
word_search = WordSearch(WORDS, WIDTH, HEIGHT)
word_search.generate_board()
board = word_search.board
result = ""
for row in range(len(board)):
for col in range(len(board[0])):
character = "#"
if (letter := board[row][col]) is not None:
character = letter
# Empty char, so add a fake char
elif add_fake_chars:
character = chr(randint(97, 122))
result += f"{character} "
result += "\n"
print(result, end="")
if __name__ == "__main__":
import doctest
doctest.testmod()
visualise_word_search()

View File

@ -21,6 +21,8 @@ Sum of phi(d), for all d|n = n. This result can be used to find phi(n) using a s
Time: 1 sec
"""
import numpy as np
def solution(limit: int = 1_000_000) -> int:
"""
@ -33,14 +35,15 @@ def solution(limit: int = 1_000_000) -> int:
304191
"""
phi = [i - 1 for i in range(limit + 1)]
# generating an array from -1 to limit
phi = np.arange(-1, limit)
for i in range(2, limit + 1):
if phi[i] == i - 1:
for j in range(2 * i, limit + 1, i):
phi[j] -= phi[j] // i
ind = np.arange(2 * i, limit + 1, i) # indexes for selection
phi[ind] -= phi[ind] // i
return sum(phi[2 : limit + 1])
return np.sum(phi[2 : limit + 1])
if __name__ == "__main__":

View File

@ -1,3 +1,4 @@
# DISABLED!!
#!/usr/bin/env python3
"""
Deutsch-Jozsa Algorithm is one of the first examples of a quantum

View File

@ -1,3 +1,4 @@
# DISABLED!!
#!/usr/bin/env python3
"""
Build a half-adder quantum circuit that takes two bits as input,

View File

@ -9,15 +9,15 @@ opencv-python
pandas
pillow
projectq
qiskit
qiskit-aer
qiskit ; python_version < '3.12'
qiskit-aer ; python_version < '3.12'
requests
rich
scikit-fuzzy
scikit-learn
statsmodels
sympy
tensorflow
tensorflow ; python_version < '3.12'
texttable
tweepy
xgboost

View File

@ -1,62 +0,0 @@
from random import randint
from tempfile import TemporaryFile
import numpy as np
def _in_place_quick_sort(a, start, end):
count = 0
if start < end:
pivot = randint(start, end)
temp = a[end]
a[end] = a[pivot]
a[pivot] = temp
p, count = _in_place_partition(a, start, end)
count += _in_place_quick_sort(a, start, p - 1)
count += _in_place_quick_sort(a, p + 1, end)
return count
def _in_place_partition(a, start, end):
count = 0
pivot = randint(start, end)
temp = a[end]
a[end] = a[pivot]
a[pivot] = temp
new_pivot_index = start - 1
for index in range(start, end):
count += 1
if a[index] < a[end]: # check if current val is less than pivot value
new_pivot_index = new_pivot_index + 1
temp = a[new_pivot_index]
a[new_pivot_index] = a[index]
a[index] = temp
temp = a[new_pivot_index + 1]
a[new_pivot_index + 1] = a[end]
a[end] = temp
return new_pivot_index + 1, count
outfile = TemporaryFile()
p = 100 # 1000 elements are to be sorted
mu, sigma = 0, 1 # mean and standard deviation
X = np.random.normal(mu, sigma, p)
np.save(outfile, X)
print("The array is")
print(X)
outfile.seek(0) # using the same array
M = np.load(outfile)
r = len(M) - 1
z = _in_place_quick_sort(M, 0, r)
print(
"No of Comparisons for 100 elements selected from a standard normal distribution"
"is :"
)
print(z)

View File

@ -1,44 +0,0 @@
"""
Picks the random index as the pivot
"""
import random
def partition(a, left_index, right_index):
pivot = a[left_index]
i = left_index + 1
for j in range(left_index + 1, right_index):
if a[j] < pivot:
a[j], a[i] = a[i], a[j]
i += 1
a[left_index], a[i - 1] = a[i - 1], a[left_index]
return i - 1
def quick_sort_random(a, left, right):
if left < right:
pivot = random.randint(left, right - 1)
a[pivot], a[left] = (
a[left],
a[pivot],
) # switches the pivot with the left most bound
pivot_index = partition(a, left, right)
quick_sort_random(
a, left, pivot_index
) # recursive quicksort to the left of the pivot point
quick_sort_random(
a, pivot_index + 1, right
) # recursive quicksort to the right of the pivot point
def main():
user_input = input("Enter numbers separated by a comma:\n").strip()
arr = [int(item) for item in user_input.split(",")]
quick_sort_random(arr, 0, len(arr))
print(arr)
if __name__ == "__main__":
main()

View File

@ -33,7 +33,7 @@ def get_subreddit_data(
headers={"User-agent": "A random string"},
)
if response.status_code == 429:
raise requests.HTTPError
raise requests.HTTPError(response=response)
data = response.json()
if not wanted_data: