diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b5a5347c6..6aa0073bf 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -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 \ diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000..ec3cdb61d --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1 @@ +https://code.visualstudio.com/docs/devcontainers/tutorial diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c5a855b25..ae1d4fb74 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -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", } }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index abf99ab22..05cd709a8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -69,7 +69,7 @@ # /other/ @cclauss # TODO: Uncomment this line after Hacktoberfest -/project_euler/ @dhruvmanila +# /project_euler/ # /quantum/ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc8cb6369..60c1d6d11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index ca2d5be47..496f1460e 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -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 . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 809b841d0..dbf7ff341 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a1bb6527..7a67ce33c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/DIRECTORY.md b/DIRECTORY.md index e596d96e5..9a913aa78 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -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) diff --git a/backtracking/combination_sum.py b/backtracking/combination_sum.py index f555adb75..3c6ed81f4 100644 --- a/backtracking/combination_sum.py +++ b/backtracking/combination_sum.py @@ -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] diff --git a/backtracking/minmax.py b/backtracking/minmax.py deleted file mode 100644 index 9b87183cf..000000000 --- a/backtracking/minmax.py +++ /dev/null @@ -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() diff --git a/bit_manipulation/missing_number.py b/bit_manipulation/missing_number.py new file mode 100644 index 000000000..92502a778 --- /dev/null +++ b/bit_manipulation/missing_number.py @@ -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 diff --git a/computer_vision/cnn_classification.py b/computer_vision/cnn_classification.py.DISABLED.txt similarity index 100% rename from computer_vision/cnn_classification.py rename to computer_vision/cnn_classification.py.DISABLED.txt diff --git a/data_structures/arrays/permutations.py b/data_structures/arrays/permutations.py index 0f029187b..4906dd5c2 100644 --- a/data_structures/arrays/permutations.py +++ b/data_structures/arrays/permutations.py @@ -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() diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py index 2afb7604f..2b33cdca4 100644 --- a/data_structures/binary_tree/binary_tree_traversals.py +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -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__": diff --git a/data_structures/linked_list/reverse_k_group.py b/data_structures/linked_list/reverse_k_group.py new file mode 100644 index 000000000..5fc45491a --- /dev/null +++ b/data_structures/linked_list/reverse_k_group.py @@ -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}") diff --git a/data_structures/linked_list/rotate_to_the_right.py b/data_structures/linked_list/rotate_to_the_right.py new file mode 100644 index 000000000..51b10481c --- /dev/null +++ b/data_structures/linked_list/rotate_to_the_right.py @@ -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) diff --git a/dynamic_programming/k_means_clustering_tensorflow.py b/dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt similarity index 100% rename from dynamic_programming/k_means_clustering_tensorflow.py rename to dynamic_programming/k_means_clustering_tensorflow.py.DISABLED.txt diff --git a/dynamic_programming/smith_waterman.py b/dynamic_programming/smith_waterman.py new file mode 100644 index 000000000..4c5d58379 --- /dev/null +++ b/dynamic_programming/smith_waterman.py @@ -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)) diff --git a/fuzzy_logic/fuzzy_operations.py b/fuzzy_logic/fuzzy_operations.py.DISABLED.txt similarity index 100% rename from fuzzy_logic/fuzzy_operations.py rename to fuzzy_logic/fuzzy_operations.py.DISABLED.txt diff --git a/machine_learning/local_weighted_learning/local_weighted_learning.py b/machine_learning/local_weighted_learning/local_weighted_learning.py index 8dd0e55d4..f3056da40 100644 --- a/machine_learning/local_weighted_learning/local_weighted_learning.py +++ b/machine_learning/local_weighted_learning/local_weighted_learning.py @@ -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 diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py.DISABLED.txt similarity index 100% rename from machine_learning/lstm/lstm_prediction.py rename to machine_learning/lstm/lstm_prediction.py.DISABLED.txt diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index eddbec074..46b72e88b 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -74,10 +74,14 @@ class MultinomialNBClassifier: data_class_i = data[grouped_indices[class_i]] 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 + 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 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() - diff --git a/maths/add.py b/maths/add.py deleted file mode 100644 index c89252c64..000000000 --- a/maths/add.py +++ /dev/null @@ -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)}") diff --git a/maths/binary_exp_mod.py b/maths/binary_exp_mod.py index df688892d..8893182a3 100644 --- a/maths/binary_exp_mod.py +++ b/maths/binary_exp_mod.py @@ -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 diff --git a/maths/binary_exponentiation.py b/maths/binary_exponentiation.py index 147b4285f..05de939d1 100644 --- a/maths/binary_exponentiation.py +++ b/maths/binary_exponentiation.py @@ -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 diff --git a/maths/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py deleted file mode 100644 index 51ec4baf2..000000000 --- a/maths/binary_exponentiation_2.py +++ /dev/null @@ -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. -""" diff --git a/maths/binary_exponentiation_3.py b/maths/binary_exponentiation_3.py index dd4e70e74..9cd143e09 100644 --- a/maths/binary_exponentiation_3.py +++ b/maths/binary_exponentiation_3.py @@ -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: diff --git a/maths/binary_multiplication.py b/maths/binary_multiplication.py new file mode 100644 index 000000000..0cc5a575f --- /dev/null +++ b/maths/binary_multiplication.py @@ -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() diff --git a/maths/binomial_coefficient.py b/maths/binomial_coefficient.py index 0d4b3d1a8..6d5b46cb5 100644 --- a/maths/binomial_coefficient.py +++ b/maths/binomial_coefficient.py @@ -1,4 +1,4 @@ -def binomial_coefficient(n, r): +def binomial_coefficient(n: int, r: int) -> int: """ Find binomial coefficient using pascals triangle. diff --git a/maths/double_factorial.py b/maths/double_factorial.py new file mode 100644 index 000000000..3c3a28304 --- /dev/null +++ b/maths/double_factorial.py @@ -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() diff --git a/maths/double_factorial_iterative.py b/maths/double_factorial_iterative.py deleted file mode 100644 index b2b58aa04..000000000 --- a/maths/double_factorial_iterative.py +++ /dev/null @@ -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() diff --git a/maths/double_factorial_recursive.py b/maths/double_factorial_recursive.py deleted file mode 100644 index 05c9b2968..000000000 --- a/maths/double_factorial_recursive.py +++ /dev/null @@ -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() diff --git a/maths/maclaurin_series.py b/maths/maclaurin_series.py index e55839bc1..d5c3c3ab9 100644 --- a/maths/maclaurin_series.py +++ b/maths/maclaurin_series.py @@ -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") diff --git a/maths/miller_rabin.py b/maths/miller_rabin.py deleted file mode 100644 index 9f2668dba..000000000 --- a/maths/miller_rabin.py +++ /dev/null @@ -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))) diff --git a/maths/three_sum.py b/maths/three_sum.py new file mode 100644 index 000000000..09956f841 --- /dev/null +++ b/maths/three_sum.py @@ -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() diff --git a/other/word_search.py b/other/word_search.py new file mode 100644 index 000000000..a4796e220 --- /dev/null +++ b/other/word_search.py @@ -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() diff --git a/project_euler/problem_072/sol1.py b/project_euler/problem_072/sol1.py index a2a0eeeb3..5a28be564 100644 --- a/project_euler/problem_072/sol1.py +++ b/project_euler/problem_072/sol1.py @@ -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__": diff --git a/quantum/bb84.py b/quantum/bb84.py.DISABLED.txt similarity index 100% rename from quantum/bb84.py rename to quantum/bb84.py.DISABLED.txt diff --git a/quantum/deutsch_jozsa.py b/quantum/deutsch_jozsa.py.DISABLED.txt old mode 100755 new mode 100644 similarity index 99% rename from quantum/deutsch_jozsa.py rename to quantum/deutsch_jozsa.py.DISABLED.txt index 95c3e65b5..5c8a379de --- a/quantum/deutsch_jozsa.py +++ b/quantum/deutsch_jozsa.py.DISABLED.txt @@ -1,3 +1,4 @@ +# DISABLED!! #!/usr/bin/env python3 """ Deutsch-Jozsa Algorithm is one of the first examples of a quantum diff --git a/quantum/half_adder.py b/quantum/half_adder.py.DISABLED.txt old mode 100755 new mode 100644 similarity index 99% rename from quantum/half_adder.py rename to quantum/half_adder.py.DISABLED.txt index 21a57ddcf..800d563ec --- a/quantum/half_adder.py +++ b/quantum/half_adder.py.DISABLED.txt @@ -1,3 +1,4 @@ +# DISABLED!! #!/usr/bin/env python3 """ Build a half-adder quantum circuit that takes two bits as input, diff --git a/quantum/not_gate.py b/quantum/not_gate.py.DISABLED.txt similarity index 100% rename from quantum/not_gate.py rename to quantum/not_gate.py.DISABLED.txt diff --git a/quantum/q_full_adder.py b/quantum/q_full_adder.py.DISABLED.txt similarity index 100% rename from quantum/q_full_adder.py rename to quantum/q_full_adder.py.DISABLED.txt diff --git a/quantum/quantum_entanglement.py b/quantum/quantum_entanglement.py.DISABLED.txt similarity index 100% rename from quantum/quantum_entanglement.py rename to quantum/quantum_entanglement.py.DISABLED.txt diff --git a/quantum/quantum_teleportation.py b/quantum/quantum_teleportation.py.DISABLED.txt similarity index 100% rename from quantum/quantum_teleportation.py rename to quantum/quantum_teleportation.py.DISABLED.txt diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py.DISABLED.txt similarity index 100% rename from quantum/ripple_adder_classic.py rename to quantum/ripple_adder_classic.py.DISABLED.txt diff --git a/quantum/single_qubit_measure.py b/quantum/single_qubit_measure.py.DISABLED.txt similarity index 100% rename from quantum/single_qubit_measure.py rename to quantum/single_qubit_measure.py.DISABLED.txt diff --git a/quantum/superdense_coding.py b/quantum/superdense_coding.py.DISABLED.txt similarity index 100% rename from quantum/superdense_coding.py rename to quantum/superdense_coding.py.DISABLED.txt diff --git a/requirements.txt b/requirements.txt index 1128e9d66..25dba6f5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/sorts/random_normal_distribution_quicksort.py b/sorts/random_normal_distribution_quicksort.py deleted file mode 100644 index f7f60903c..000000000 --- a/sorts/random_normal_distribution_quicksort.py +++ /dev/null @@ -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) diff --git a/sorts/random_pivot_quick_sort.py b/sorts/random_pivot_quick_sort.py deleted file mode 100644 index 748b67410..000000000 --- a/sorts/random_pivot_quick_sort.py +++ /dev/null @@ -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() diff --git a/web_programming/reddit.py b/web_programming/reddit.py index 5ca5f828c..1c165ecc4 100644 --- a/web_programming/reddit.py +++ b/web_programming/reddit.py @@ -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: