From d939324ab9e1d2b4e86fd8ff1c0fdf8fb1374c44 Mon Sep 17 00:00:00 2001 From: Akash G Krishnan Date: Fri, 16 Oct 2020 18:43:45 +0530 Subject: [PATCH 001/195] New doubly linkedlist PR: pull/2573 (#3380) https://github.com/TheAlgorithms/Python/pull/2573 the second implementation of the Doubly linked list --- .../linked_list/doubly_linked_list_two.py | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 data_structures/linked_list/doubly_linked_list_two.py diff --git a/data_structures/linked_list/doubly_linked_list_two.py b/data_structures/linked_list/doubly_linked_list_two.py new file mode 100644 index 000000000000..184b6966b5a9 --- /dev/null +++ b/data_structures/linked_list/doubly_linked_list_two.py @@ -0,0 +1,253 @@ +""" +- A linked list is similar to an array, it holds values. However, links in a linked + list do not have indexes. +- This is an example of a double ended, doubly linked list. +- Each link references the next link and the previous one. +- A Doubly Linked List (DLL) contains an extra pointer, typically called previous + pointer, together with next pointer and data which are there in singly linked list. + - Advantages over SLL - It can be traversed in both forward and backward direction. + Delete operation is more efficient +""" + + +class Node: + def __init__(self, data: int, previous=None, next_node=None): + self.data = data + self.previous = previous + self.next = next_node + + def __str__(self) -> str: + return f"{self.data}" + + def get_data(self) -> int: + return self.data + + def get_next(self): + return self.next + + def get_previous(self): + return self.previous + + +class LinkedListIterator: + def __init__(self, head): + self.current = head + + def __iter__(self): + return self + + def __next__(self): + if not self.current: + raise StopIteration + else: + value = self.current.get_data() + self.current = self.current.get_next() + return value + + +class LinkedList: + def __init__(self): + self.head = None # First node in list + self.tail = None # Last node in list + + def __str__(self): + current = self.head + nodes = [] + while current is not None: + nodes.append(current.get_data()) + current = current.get_next() + return " ".join(str(node) for node in nodes) + + def __contains__(self, value: int): + current = self.head + while current: + if current.get_data() == value: + return True + current = current.get_next() + return False + + def __iter__(self): + return LinkedListIterator(self.head) + + def get_head_data(self): + if self.head: + return self.head.get_data() + return None + + def get_tail_data(self): + if self.tail: + return self.tail.get_data() + return None + + def set_head(self, node: Node) -> None: + + if self.head is None: + self.head = node + self.tail = node + else: + self.insert_before_node(self.head, node) + + def set_tail(self, node: Node) -> None: + if self.head is None: + self.set_head(node) + else: + self.insert_after_node(self.tail, node) + + def insert(self, value: int) -> None: + node = Node(value) + if self.head is None: + self.set_head(node) + else: + self.set_tail(node) + + def insert_before_node(self, node: Node, node_to_insert: Node) -> None: + node_to_insert.next = node + node_to_insert.previous = node.previous + + if node.get_previous() is None: + self.head = node_to_insert + else: + node.previous.next = node_to_insert + + node.previous = node_to_insert + + def insert_after_node(self, node: Node, node_to_insert: Node) -> None: + node_to_insert.previous = node + node_to_insert.next = node.next + + if node.get_next() is None: + self.tail = node_to_insert + else: + node.next.previous = node_to_insert + + node.next = node_to_insert + + def insert_at_position(self, position: int, value: int) -> None: + current_position = 1 + new_node = Node(value) + node = self.head + while node: + if current_position == position: + self.insert_before_node(node, new_node) + return None + current_position += 1 + node = node.next + self.insert_after_node(self.tail, new_node) + + def get_node(self, item: int) -> Node: + node = self.head + while node: + if node.get_data() == item: + return node + node = node.get_next() + raise Exception("Node not found") + + def delete_value(self, value): + node = self.get_node(value) + + if node is not None: + if node == self.head: + self.head = self.head.get_next() + + if node == self.tail: + self.tail = self.tail.get_previous() + + self.remove_node_pointers(node) + + @staticmethod + def remove_node_pointers(node: Node) -> None: + if node.get_next(): + node.next.previous = node.previous + + if node.get_previous(): + node.previous.next = node.next + + node.next = None + node.previous = None + + def is_empty(self): + return self.head is None + + +def create_linked_list() -> None: + """ + >>> new_linked_list = LinkedList() + >>> new_linked_list.get_head_data() is None + True + >>> new_linked_list.get_tail_data() is None + True + >>> new_linked_list.is_empty() + True + >>> new_linked_list.insert(10) + >>> new_linked_list.get_head_data() + 10 + >>> new_linked_list.get_tail_data() + 10 + >>> new_linked_list.insert_at_position(position=3, value=20) + >>> new_linked_list.get_head_data() + 10 + >>> new_linked_list.get_tail_data() + 20 + >>> new_linked_list.set_head(Node(1000)) + >>> new_linked_list.get_head_data() + 1000 + >>> new_linked_list.get_tail_data() + 20 + >>> new_linked_list.set_tail(Node(2000)) + >>> new_linked_list.get_head_data() + 1000 + >>> new_linked_list.get_tail_data() + 2000 + >>> for value in new_linked_list: + ... print(value) + 1000 + 10 + 20 + 2000 + >>> new_linked_list.is_empty() + False + >>> for value in new_linked_list: + ... print(value) + 1000 + 10 + 20 + 2000 + >>> 10 in new_linked_list + True + >>> new_linked_list.delete_value(value=10) + >>> 10 in new_linked_list + False + >>> new_linked_list.delete_value(value=2000) + >>> new_linked_list.get_tail_data() + 20 + >>> new_linked_list.delete_value(value=1000) + >>> new_linked_list.get_tail_data() + 20 + >>> new_linked_list.get_head_data() + 20 + >>> for value in new_linked_list: + ... print(value) + 20 + >>> new_linked_list.delete_value(value=20) + >>> for value in new_linked_list: + ... print(value) + >>> for value in range(1,10): + ... new_linked_list.insert(value=value) + >>> for value in new_linked_list: + ... print(value) + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + """ + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From c965366ec50ebd7ed586e309672c6720faaa43de Mon Sep 17 00:00:00 2001 From: Tanay Karve Date: Fri, 16 Oct 2020 18:45:20 +0530 Subject: [PATCH 002/195] Hacktoberfest 2020: Added computer vision algorithm (#2946) * Create meanthresholding.py * Rename meanthresholding.py to meanthreshold.py * Update meanthreshold.py * Update computer_vision/meanthreshold.py Verified this part works, thanks. Co-authored-by: Christian Clauss * Update computer_vision/meanthreshold.py Co-authored-by: Christian Clauss Co-authored-by: Christian Clauss --- computer_vision/meanthreshold.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 computer_vision/meanthreshold.py diff --git a/computer_vision/meanthreshold.py b/computer_vision/meanthreshold.py new file mode 100644 index 000000000000..76657933d6a9 --- /dev/null +++ b/computer_vision/meanthreshold.py @@ -0,0 +1,30 @@ +from PIL import Image + +""" +Mean thresholding algorithm for image processing +https://en.wikipedia.org/wiki/Thresholding_(image_processing) +""" + + +def mean_threshold(image: Image) -> Image: + """ + image: is a grayscale PIL image object + """ + height, width = image.size + mean = 0 + pixels = image.load() + for i in range(width): + for j in range(height): + pixel = pixels[j, i] + mean += pixel + mean //= width * height + + for j in range(width): + for i in range(height): + pixels[i, j] = 255 if pixels[i, j] > mean else 0 + return image + + +if __name__ == "__main__": + image = mean_threshold(Image.open("path_to_image").convert("L")) + image.save("output_image_path") From 4db79cb066e442e0f6513d4dba46f891e6a67cae Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Fri, 16 Oct 2020 17:47:35 +0200 Subject: [PATCH 003/195] Project Euler 57 - Square root convergents (#3259) * include solution for problem 57 * fix line to long errors * update filenames and code to comply with new regulations * more descriptive local variables --- project_euler/problem_057/__init__.py | 0 project_euler/problem_057/sol1.py | 48 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 project_euler/problem_057/__init__.py create mode 100644 project_euler/problem_057/sol1.py diff --git a/project_euler/problem_057/__init__.py b/project_euler/problem_057/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_057/sol1.py b/project_euler/problem_057/sol1.py new file mode 100644 index 000000000000..04b6199f4717 --- /dev/null +++ b/project_euler/problem_057/sol1.py @@ -0,0 +1,48 @@ +""" +Project Euler Problem 57: https://projecteuler.net/problem=57 +It is possible to show that the square root of two can be expressed as an infinite +continued fraction. + +sqrt(2) = 1 + 1 / (2 + 1 / (2 + 1 / (2 + ...))) + +By expanding this for the first four iterations, we get: +1 + 1 / 2 = 3 / 2 = 1.5 +1 + 1 / (2 + 1 / 2} = 7 / 5 = 1.4 +1 + 1 / (2 + 1 / (2 + 1 / 2)) = 17 / 12 = 1.41666... +1 + 1 / (2 + 1 / (2 + 1 / (2 + 1 / 2))) = 41/ 29 = 1.41379... + +The next three expansions are 99/70, 239/169, and 577/408, but the eighth expansion, +1393/985, is the first example where the number of digits in the numerator exceeds +the number of digits in the denominator. + +In the first one-thousand expansions, how many fractions contain a numerator with +more digits than the denominator? +""" + + +def solution(n: int = 1000) -> int: + """ + returns number of fractions containing a numerator with more digits than + the denominator in the first n expansions. + >>> solution(14) + 2 + >>> solution(100) + 15 + >>> solution(10000) + 1508 + """ + prev_numerator, prev_denominator = 1, 1 + result = [] + for i in range(1, n + 1): + numerator = prev_numerator + 2 * prev_denominator + denominator = prev_numerator + prev_denominator + if len(str(numerator)) > len(str(denominator)): + result.append(i) + prev_numerator = numerator + prev_denominator = denominator + + return len(result) + + +if __name__ == "__main__": + print(f"{solution() = }") From d612d9b8c3768e1e15505cbb31de1cf9a16566bf Mon Sep 17 00:00:00 2001 From: Akash G Krishnan Date: Sat, 17 Oct 2020 00:15:26 +0530 Subject: [PATCH 004/195] Adding in the evaluate postfix notation using Stack (#2598) * Create evaluate_postfix_notations.py Adding in the evaluate postfix notation using Stacks one of the common use with simple stack question creating a new file for the data structure of stacks * Create evaluate_postfix_notations.py Adding in the evaluate postfix notation using Stacks one of the common use with simple stack question creating a new file for the data structure of stacks * Delete evaluate_postfix_notations.py * Evaluate postfix expression stack clean approach Sending in the PR again as the Previous request failed in pre commit * Update evaluate_postfix_notations.py * Update evaluate_postfix_notations.py Made changes as per the required for fixing the failing pre-commits. * Update evaluate_postfix_notations.py Made changes as suggested by @cclauss * Update evaluate_postfix_notations.py fixed pre-commit fails * Update evaluate_postfix_notations.py fixing pre-commit fails * Update evaluate_postfix_notations.py Deleted trailing white spaces causing pre-commits to fail * Update data_structures/stacks/evaluate_postfix_notations.py Co-authored-by: Christian Clauss * Update data_structures/stacks/evaluate_postfix_notations.py Co-authored-by: Christian Clauss Co-authored-by: Christian Clauss --- .../stacks/evaluate_postfix_notations.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 data_structures/stacks/evaluate_postfix_notations.py diff --git a/data_structures/stacks/evaluate_postfix_notations.py b/data_structures/stacks/evaluate_postfix_notations.py new file mode 100644 index 000000000000..a03cb43bb020 --- /dev/null +++ b/data_structures/stacks/evaluate_postfix_notations.py @@ -0,0 +1,49 @@ +""" +The Reverse Polish Nation also known as Polish postfix notation +or simply postfix notation. +https://en.wikipedia.org/wiki/Reverse_Polish_notation +Classic examples of simple stack implementations +Valid operators are +, -, *, /. +Each operand may be an integer or another expression. +""" + + +def evaluate_postfix(postfix_notation: list) -> int: + """ + >>> evaluate_postfix(["2", "1", "+", "3", "*"]) + 9 + >>> evaluate_postfix(["4", "13", "5", "/", "+"]) + 6 + >>> evaluate_postfix([]) + 0 + """ + if not postfix_notation: + return 0 + + operations = {"+", "-", "*", "/"} + stack = [] + + for token in postfix_notation: + if token in operations: + b, a = stack.pop(), stack.pop() + if token == "+": + stack.append(a + b) + elif token == "-": + stack.append(a - b) + elif token == "*": + stack.append(a * b) + else: + if a * b < 0 and a % b != 0: + stack.append(a // b + 1) + else: + stack.append(a // b) + else: + stack.append(int(token)) + + return stack.pop() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 867f7e26cc25304050be5b8f05c23e7f35e33f7d Mon Sep 17 00:00:00 2001 From: Dhruv Date: Sat, 17 Oct 2020 08:23:17 +0530 Subject: [PATCH 005/195] Create GitHub action only for Project Euler (#3378) * Add GitHub action for Project Euler only * Add second job for Project Euler * Remove Project Euler jobs from Travis CI * Fix typo for actions/setup-python * Rename the workflow file * Change name of file in workflow * Remove comments from Travis config file --- .github/workflows/project_euler.yml | 30 +++++++++++++++++++++++++++++ .travis.yml | 10 ---------- 2 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/project_euler.yml diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml new file mode 100644 index 000000000000..852b0adbcb56 --- /dev/null +++ b/.github/workflows/project_euler.yml @@ -0,0 +1,30 @@ +on: + pull_request: + # only check if a file is changed within the project_euler directory + paths: + - 'project_euler/**' + - '.github/workflows/project_euler.yml' + +name: 'Project Euler' + +jobs: + project-euler: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install pytest and pytest-cov + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade pytest pytest-cov + - run: pytest --doctest-modules --durations=10 --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ + validate-solutions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install pytest + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade pytest + - run: pytest --durations=10 project_euler/validate_solutions.py diff --git a/.travis.yml b/.travis.yml index f31dae8467d6..2a4a6392d4e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,16 +10,6 @@ jobs: install: pip install pytest-cov -r requirements.txt script: - pytest --doctest-modules --ignore=project_euler/ --durations=10 --cov-report=term-missing:skip-covered --cov=. . - - name: Project Euler - install: - - pip install pytest-cov - script: - - pytest --doctest-modules --durations=10 --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ - - name: Project Euler Solution - install: - - pip install pytest - script: - - pytest --tb=short --durations=10 project_euler/validate_solutions.py after_success: - scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md notifications: From a121f1690cc8c89f1b2044471b039b80261d6a4b Mon Sep 17 00:00:00 2001 From: acoder77 <73009264+acoder77@users.noreply.github.com> Date: Sat, 17 Oct 2020 10:55:25 +0530 Subject: [PATCH 006/195] Create .gitattributes for Cross OS compatibility (#3410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this set, Windows users will have text files converted from Windows style line endings (\r\n) to Unix style line endings (\n) when they’re added to the repository. https://www.edwardthomson.com/blog/git_for_windows_line_endings.html --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..176a458f94e0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto From bf5a5e78f357c5fc55ff306b6b9dbbbc8c8bfc8d Mon Sep 17 00:00:00 2001 From: Abhishek Jaisingh Date: Sat, 17 Oct 2020 11:11:24 +0530 Subject: [PATCH 007/195] Qiskit: Add Quantum Half Adder (#3405) * Qiskit: Add Quantum Half Adder * fixup! Format Python code with psf/black push Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- quantum/half_adder.py | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 quantum/half_adder.py diff --git a/quantum/half_adder.py b/quantum/half_adder.py new file mode 100755 index 000000000000..1310edbbfdf1 --- /dev/null +++ b/quantum/half_adder.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Build a half-adder quantum circuit that takes two bits as input, +encodes them into qubits, then runs the half-adder circuit calculating +the sum and carry qubits, observed over 1000 runs of the experiment +. + +References: +- https://en.wikipedia.org/wiki/Adder_(electronics) +- https://qiskit.org/textbook/ch-states/atoms-computation.html#4.2-Remembering-how-to-add- +""" + +import qiskit as q + + +def half_adder(bit0: int, bit1: int) -> q.result.counts.Counts: + """ + >>> half_adder(0, 0) + {'00': 1000} + >>> half_adder(0, 1) + {'01': 1000} + >>> half_adder(1, 0) + {'01': 1000} + >>> half_adder(1, 1) + {'10': 1000} + """ + # Use Aer's qasm_simulator + simulator = q.Aer.get_backend("qasm_simulator") + + qc_ha = q.QuantumCircuit(4, 2) + # encode inputs in qubits 0 and 1 + if bit0 == 1: + qc_ha.x(0) + if bit1 == 1: + qc_ha.x(1) + qc_ha.barrier() + + # use cnots to write XOR of the inputs on qubit2 + qc_ha.cx(0, 2) + qc_ha.cx(1, 2) + + # use ccx / toffoli gate to write AND of the inputs on qubit3 + qc_ha.ccx(0, 1, 3) + qc_ha.barrier() + + # extract outputs + qc_ha.measure(2, 0) # extract XOR value + qc_ha.measure(3, 1) # extract AND value + + # Execute the circuit on the qasm simulator + job = q.execute(qc_ha, simulator, shots=1000) + + # Return the histogram data of the results of the experiment. + return job.result().get_counts(qc_ha) + + +if __name__ == "__main__": + counts = half_adder(1, 1) + print(f"Half Adder Output Qubit Counts: {counts}") From ac5e00d9d530d2b1a80f5d362b6f4fec3a3e82fc Mon Sep 17 00:00:00 2001 From: CapofWeird <40702379+CapofWeird@users.noreply.github.com> Date: Sat, 17 Oct 2020 03:56:11 -0400 Subject: [PATCH 008/195] Fixed typo in caesar_cipher.py (#2979) * Fixed typo in caesar_cipher.py * Typo fixes --- ciphers/caesar_cipher.py | 2 +- ciphers/xor_cipher.py | 2 +- hashes/sha1.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 7bda519767a1..4038919e5dde 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -220,7 +220,7 @@ def brute_force(input_string: str, alphabet=None) -> dict: def main(): while True: print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') - print(*["1.Encrpyt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") + print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") # get user input choice = input("\nWhat would you like to do?: ").strip() or "4" diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 818dec64131a..27e4262bc924 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -183,7 +183,7 @@ def decrypt_file(self, file: str, key: int) -> bool: # crypt = XORCipher() # key = 67 -# # test enrcypt +# # test encrypt # print(crypt.encrypt("hallo welt",key)) # # test decrypt # print(crypt.decrypt(crypt.encrypt("hallo welt",key), key)) diff --git a/hashes/sha1.py b/hashes/sha1.py index 04ecdd788039..cca38b7c3fdc 100644 --- a/hashes/sha1.py +++ b/hashes/sha1.py @@ -8,7 +8,7 @@ Also contains a Test class to verify that the generated Hash is same as that returned by the hashlib library -SHA1 hash or SHA1 sum of a string is a crytpographic function which means it is easy +SHA1 hash or SHA1 sum of a string is a cryptographic function which means it is easy to calculate forwards but extremely difficult to calculate backwards. What this means is, you can easily calculate the hash of a string, but it is extremely difficult to know the original string if you have its hash. This property is useful to communicate From 0821240a685613cab8990262cf590221ec72499f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 17 Oct 2020 13:42:29 +0200 Subject: [PATCH 009/195] Fix the build -- 88 chars per line max. (#3437) * Fix the build -- 88 chars per line max. * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 13 +++++++++++++ quantum/half_adder.py | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4e67ad5156d9..72133a60b18a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -86,6 +86,7 @@ ## Computer Vision * [Harriscorner](https://github.com/TheAlgorithms/Python/blob/master/computer_vision/harriscorner.py) + * [Meanthreshold](https://github.com/TheAlgorithms/Python/blob/master/computer_vision/meanthreshold.py) ## Conversions * [Binary To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/binary_to_decimal.py) @@ -139,6 +140,7 @@ * [Circular Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/circular_linked_list.py) * [Deque Doubly](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/deque_doubly.py) * [Doubly Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/doubly_linked_list.py) + * [Doubly Linked List Two](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/doubly_linked_list_two.py) * [From Sequence](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/from_sequence.py) * [Has Loop](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/has_loop.py) * [Is Palindrome](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/is_palindrome.py) @@ -157,6 +159,7 @@ * Stacks * [Balanced Parentheses](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/balanced_parentheses.py) * [Dijkstras Two Stack Algorithm](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/dijkstras_two_stack_algorithm.py) + * [Evaluate Postfix Notations](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/evaluate_postfix_notations.py) * [Infix To Postfix Conversion](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/infix_to_postfix_conversion.py) * [Infix To Prefix Conversion](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/infix_to_prefix_conversion.py) * [Linked Stack](https://github.com/TheAlgorithms/Python/blob/master/data_structures/stacks/linked_stack.py) @@ -655,6 +658,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_055/sol1.py) * Problem 056 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_056/sol1.py) + * Problem 057 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_057/sol1.py) * Problem 062 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_062/sol1.py) * Problem 063 @@ -667,12 +672,17 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_071/sol1.py) * Problem 072 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_072/sol1.py) + * [Sol2](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_072/sol2.py) * Problem 074 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_074/sol1.py) + * Problem 075 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_075/sol1.py) * Problem 076 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_076/sol1.py) * Problem 080 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) + * Problem 091 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_091/sol1.py) * Problem 097 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_097/sol1.py) * Problem 099 @@ -689,6 +699,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_125/sol1.py) * Problem 173 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_173/sol1.py) + * Problem 174 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) * Problem 191 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_191/sol1.py) * Problem 234 @@ -698,6 +710,7 @@ * [Validate Solutions](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py) ## Quantum + * [Half Adder](https://github.com/TheAlgorithms/Python/blob/master/quantum/half_adder.py) * [Not Gate](https://github.com/TheAlgorithms/Python/blob/master/quantum/not_gate.py) * [Quantum Entanglement](https://github.com/TheAlgorithms/Python/blob/master/quantum/quantum_entanglement.py) * [Single Qubit Measure](https://github.com/TheAlgorithms/Python/blob/master/quantum/single_qubit_measure.py) diff --git a/quantum/half_adder.py b/quantum/half_adder.py index 1310edbbfdf1..4af704e640be 100755 --- a/quantum/half_adder.py +++ b/quantum/half_adder.py @@ -6,8 +6,8 @@ . References: -- https://en.wikipedia.org/wiki/Adder_(electronics) -- https://qiskit.org/textbook/ch-states/atoms-computation.html#4.2-Remembering-how-to-add- +https://en.wikipedia.org/wiki/Adder_(electronics) +https://qiskit.org/textbook/ch-states/atoms-computation.html#4.2-Remembering-how-to-add- """ import qiskit as q From 3bda07ed981dbcf1ff0d387dddd2d01715ec05f6 Mon Sep 17 00:00:00 2001 From: RadadiyaMohit <30775542+radadiyamohit81@users.noreply.github.com> Date: Sat, 17 Oct 2020 19:20:53 +0530 Subject: [PATCH 010/195] create beaufort cipher (#3206) * create beaufort cipher if you like my code, merge it and add the label as `hacktoberfest-accepted` * update the file * Update beaufort_cipher.py * Update beaufort_cipher.py * update as per black formatter * Update beaufort_cipher.py * update the file * update file * update file * update file * update file --- ciphers/beaufort_cipher.py | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ciphers/beaufort_cipher.py diff --git a/ciphers/beaufort_cipher.py b/ciphers/beaufort_cipher.py new file mode 100644 index 000000000000..c885dec74001 --- /dev/null +++ b/ciphers/beaufort_cipher.py @@ -0,0 +1,82 @@ +""" +Author: Mohit Radadiya +""" + +from string import ascii_uppercase + +dict1 = {char: i for i, char in enumerate(ascii_uppercase)} +dict2 = {i: char for i, char in enumerate(ascii_uppercase)} + + +# This function generates the key in +# a cyclic manner until it's length isn't +# equal to the length of original text +def generate_key(message: str, key: str) -> str: + """ + >>> generate_key("THE GERMAN ATTACK","SECRET") + 'SECRETSECRETSECRE' + """ + x = len(message) + i = 0 + while True: + if x == i: + i = 0 + if len(key) == len(message): + break + key += key[i] + i += 1 + return key + + +# This function returns the encrypted text +# generated with the help of the key +def cipher_text(message: str, key_new: str) -> str: + """ + >>> cipher_text("THE GERMAN ATTACK","SECRETSECRETSECRE") + 'BDC PAYUWL JPAIYI' + """ + cipher_text = "" + i = 0 + for letter in message: + if letter == " ": + cipher_text += " " + else: + x = (dict1[letter] - dict1[key_new[i]]) % 26 + i += 1 + cipher_text += dict2[x] + return cipher_text + + +# This function decrypts the encrypted text +# and returns the original text +def original_text(cipher_text: str, key_new: str) -> str: + """ + >>> original_text("BDC PAYUWL JPAIYI","SECRETSECRETSECRE") + 'THE GERMAN ATTACK' + """ + or_txt = "" + i = 0 + for letter in cipher_text: + if letter == " ": + or_txt += " " + else: + x = (dict1[letter] + dict1[key_new[i]] + 26) % 26 + i += 1 + or_txt += dict2[x] + return or_txt + + +def main(): + message = "THE GERMAN ATTACK" + key = "SECRET" + key_new = generate_key(message, key) + s = cipher_text(message, key_new) + print(f"Encrypted Text = {s}") + print(f"Original Text = {original_text(s, key_new)}") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() From f66ba85764230c99c5dd62e3a7b4d4281eda7fd6 Mon Sep 17 00:00:00 2001 From: RadadiyaMohit <30775542+radadiyamohit81@users.noreply.github.com> Date: Sat, 17 Oct 2020 23:30:46 +0530 Subject: [PATCH 011/195] create monoalphabetic cipher (#3449) * create monoalphabetic cipher * update file * update file * update file * update file * update file * update after testing flake8 on this code * update file * update file * update file * update file * update file * update file --- ciphers/mono_alphabetic_ciphers.py | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ciphers/mono_alphabetic_ciphers.py diff --git a/ciphers/mono_alphabetic_ciphers.py b/ciphers/mono_alphabetic_ciphers.py new file mode 100644 index 000000000000..0a29d6442896 --- /dev/null +++ b/ciphers/mono_alphabetic_ciphers.py @@ -0,0 +1,59 @@ +LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + +def translate_message(key, message, mode): + """ + >>> translate_message("QWERTYUIOPASDFGHJKLZXCVBNM","Hello World","encrypt") + 'Pcssi Bidsm' + """ + chars_a = LETTERS if mode == "decrypt" else key + chars_b = key if mode == "decrypt" else LETTERS + translated = "" + # loop through each symbol in the message + for symbol in message: + if symbol.upper() in chars_a: + # encrypt/decrypt the symbol + sym_index = chars_a.find(symbol.upper()) + if symbol.isupper(): + translated += chars_b[sym_index].upper() + else: + translated += chars_b[sym_index].lower() + else: + # symbol is not in LETTERS, just add it + translated += symbol + return translated + + +def encrypt_message(key: str, message: str) -> str: + """ + >>> encrypt_message("QWERTYUIOPASDFGHJKLZXCVBNM", "Hello World") + 'Pcssi Bidsm' + """ + return translate_message(key, message, "encrypt") + + +def decrypt_message(key: str, message: str) -> str: + """ + >>> decrypt_message("QWERTYUIOPASDFGHJKLZXCVBNM", "Hello World") + 'Itssg Vgksr' + """ + return translate_message(key, message, "decrypt") + + +def main(): + message = "Hello World" + key = "QWERTYUIOPASDFGHJKLZXCVBNM" + mode = "decrypt" # set to 'encrypt' or 'decrypt' + + if mode == "encrypt": + translated = encrypt_message(key, message) + elif mode == "decrypt": + translated = decrypt_message(key, message) + print(f"Using the key {key}, the {mode}ed message is: {translated}") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() From ab4b2044c7e99951e59b08b0392ffa2dc8529091 Mon Sep 17 00:00:00 2001 From: Abhishek Jaisingh Date: Sun, 18 Oct 2020 20:24:46 +0530 Subject: [PATCH 012/195] Implement Deutsch-Jozsa Algorithm In Qiskit (#3447) * Implement Deutsch-Jozsa Algorithm In Qiskit Signed-off-by: Abhishek Jaisingh * Add Changes Requested In Review Signed-off-by: Abhishek Jaisingh * Address Further Review Comments * fixup! Format Python code with psf/black push Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- quantum/deutsch_jozsa.py | 122 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 quantum/deutsch_jozsa.py diff --git a/quantum/deutsch_jozsa.py b/quantum/deutsch_jozsa.py new file mode 100755 index 000000000000..da1b6e4e9434 --- /dev/null +++ b/quantum/deutsch_jozsa.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +""" +Deutsch-Josza Algorithm is one of the first examples of a quantum +algorithm that is exponentially faster than any possible deterministic +classical algorithm + +Premise: +We are given a hidden Boolean function f, +which takes as input a string of bits, and returns either 0 or 1: + +f({x0,x1,x2,...}) -> 0 or 1, where xn is 0 or 1 + +The property of the given Boolean function is that it is guaranteed to +either be balanced or constant. A constant function returns all 0's +or all 1's for any input, while a balanced function returns 0's for +exactly half of all inputs and 1's for the other half. Our task is to +determine whether the given function is balanced or constant. + +References: +- https://en.wikipedia.org/wiki/Deutsch-Jozsa_algorithm +- https://qiskit.org/textbook/ch-algorithms/deutsch-jozsa.html +""" + +import numpy as np +import qiskit as q + + +def dj_oracle(case: str, num_qubits: int) -> q.QuantumCircuit: + """ + Returns a Quantum Circuit for the Oracle function. + The circuit returned can represent balanced or constant function, + according to the arguments passed + """ + # This circuit has num_qubits+1 qubits: the size of the input, + # plus one output qubit + oracle_qc = q.QuantumCircuit(num_qubits + 1) + + # First, let's deal with the case in which oracle is balanced + if case == "balanced": + # First generate a random number that tells us which CNOTs to + # wrap in X-gates: + b = np.random.randint(1, 2 ** num_qubits) + # Next, format 'b' as a binary string of length 'n', padded with zeros: + b_str = format(b, f"0{num_qubits}b") + # Next, we place the first X-gates. Each digit in our binary string + # correspopnds to a qubit, if the digit is 0, we do nothing, if it's 1 + # we apply an X-gate to that qubit: + for index, bit in enumerate(b_str): + if bit == "1": + oracle_qc.x(index) + # Do the controlled-NOT gates for each qubit, using the output qubit + # as the target: + for index in range(num_qubits): + oracle_qc.cx(index, num_qubits) + # Next, place the final X-gates + for index, bit in enumerate(b_str): + if bit == "1": + oracle_qc.x(index) + + # Case in which oracle is constant + if case == "constant": + # First decide what the fixed output of the oracle will be + # (either always 0 or always 1) + output = np.random.randint(2) + if output == 1: + oracle_qc.x(num_qubits) + + oracle_gate = oracle_qc.to_gate() + oracle_gate.name = "Oracle" # To show when we display the circuit + return oracle_gate + + +def dj_algorithm(oracle: q.QuantumCircuit, num_qubits: int) -> q.QuantumCircuit: + """ + Returns the complete Deustch-Jozsa Quantum Circuit, + adding Input & Output registers and Hadamard & Measurement Gates, + to the Oracle Circuit passed in arguments + """ + dj_circuit = q.QuantumCircuit(num_qubits + 1, num_qubits) + # Set up the output qubit: + dj_circuit.x(num_qubits) + dj_circuit.h(num_qubits) + # And set up the input register: + for qubit in range(num_qubits): + dj_circuit.h(qubit) + # Let's append the oracle gate to our circuit: + dj_circuit.append(oracle, range(num_qubits + 1)) + # Finally, perform the H-gates again and measure: + for qubit in range(num_qubits): + dj_circuit.h(qubit) + + for i in range(num_qubits): + dj_circuit.measure(i, i) + + return dj_circuit + + +def deutsch_jozsa(case: str, num_qubits: int) -> q.result.counts.Counts: + """ + Main function that builds the circuit using other helper functions, + runs the experiment 1000 times & returns the resultant qubit counts + >>> deutsch_jozsa("constant", 3) + {'000': 1000} + >>> deutsch_jozsa("balanced", 3) + {'111': 1000} + """ + # Use Aer's qasm_simulator + simulator = q.Aer.get_backend("qasm_simulator") + + oracle_gate = dj_oracle(case, num_qubits) + dj_circuit = dj_algorithm(oracle_gate, num_qubits) + + # Execute the circuit on the qasm simulator + job = q.execute(dj_circuit, simulator, shots=1000) + + # Return the histogram data of the results of the experiment. + return job.result().get_counts(dj_circuit) + + +if __name__ == "__main__": + print(f"Deutsch Jozsa - Constant Oracle: {deutsch_jozsa('constant', 3)}") + print(f"Deutsch Jozsa - Balanced Oracle: {deutsch_jozsa('balanced', 3)}") From c30a1debf4f7255ec43b3b80984773188d56ce3b Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 18 Oct 2020 18:07:27 +0200 Subject: [PATCH 013/195] Fix broken build: Remove trailing spaces (#3501) * Fix broken build: Remove trailing spaces * updating DIRECTORY.md * One more trailing space Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 +++ quantum/deutsch_jozsa.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 72133a60b18a..0f4aeefe3b82 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -48,6 +48,7 @@ * [Base32](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base32.py) * [Base64 Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_cipher.py) * [Base85](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base85.py) + * [Beaufort Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/beaufort_cipher.py) * [Brute Force Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/brute_force_caesar_cipher.py) * [Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/caesar_cipher.py) * [Cryptomath Module](https://github.com/TheAlgorithms/Python/blob/master/ciphers/cryptomath_module.py) @@ -58,6 +59,7 @@ * [Enigma Machine2](https://github.com/TheAlgorithms/Python/blob/master/ciphers/enigma_machine2.py) * [Hill Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/hill_cipher.py) * [Mixed Keyword Cypher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/mixed_keyword_cypher.py) + * [Mono Alphabetic Ciphers](https://github.com/TheAlgorithms/Python/blob/master/ciphers/mono_alphabetic_ciphers.py) * [Morse Code Implementation](https://github.com/TheAlgorithms/Python/blob/master/ciphers/morse_code_implementation.py) * [Onepad Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/onepad_cipher.py) * [Playfair Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/playfair_cipher.py) @@ -710,6 +712,7 @@ * [Validate Solutions](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py) ## Quantum + * [Deutsch Jozsa](https://github.com/TheAlgorithms/Python/blob/master/quantum/deutsch_jozsa.py) * [Half Adder](https://github.com/TheAlgorithms/Python/blob/master/quantum/half_adder.py) * [Not Gate](https://github.com/TheAlgorithms/Python/blob/master/quantum/not_gate.py) * [Quantum Entanglement](https://github.com/TheAlgorithms/Python/blob/master/quantum/quantum_entanglement.py) diff --git a/quantum/deutsch_jozsa.py b/quantum/deutsch_jozsa.py index da1b6e4e9434..304eea196e03 100755 --- a/quantum/deutsch_jozsa.py +++ b/quantum/deutsch_jozsa.py @@ -5,14 +5,14 @@ classical algorithm Premise: -We are given a hidden Boolean function f, +We are given a hidden Boolean function f, which takes as input a string of bits, and returns either 0 or 1: f({x0,x1,x2,...}) -> 0 or 1, where xn is 0 or 1 - + The property of the given Boolean function is that it is guaranteed to -either be balanced or constant. A constant function returns all 0's -or all 1's for any input, while a balanced function returns 0's for +either be balanced or constant. A constant function returns all 0's +or all 1's for any input, while a balanced function returns 0's for exactly half of all inputs and 1's for the other half. Our task is to determine whether the given function is balanced or constant. From 63a92b3843c949f9a98fb2301ac3eb4eba721237 Mon Sep 17 00:00:00 2001 From: Anselm Hahn Date: Sun, 18 Oct 2020 21:54:43 +0200 Subject: [PATCH 014/195] Replace main with __main__ (#3518) --- web_programming/slack_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_programming/slack_message.py b/web_programming/slack_message.py index 8ea9d5d0add2..f35aa3ca587e 100644 --- a/web_programming/slack_message.py +++ b/web_programming/slack_message.py @@ -13,7 +13,7 @@ def send_slack_message(message_body: str, slack_url: str) -> None: ) -if __name__ == "main": +if __name__ == "__main__": # Set the slack url to the one provided by Slack when you create the webhook at # https://my.slack.com/services/new/incoming-webhook/ send_slack_message("", "") From ced2cf5491ba3e7d661f574d82510ee93c2d9f51 Mon Sep 17 00:00:00 2001 From: JoaoVictorNascimento Date: Sun, 18 Oct 2020 18:44:19 -0300 Subject: [PATCH 015/195] Add Patience Sort (#3469) * Add Patience Sort * fix code for pre-commit * Fix params def * Adding new line at end of file * Remove Trailing Whitespace * Adding space between the methods of the Stack class * Removing Trailing Whitespace * Ordering Imports * Adding url patience sort Co-authored-by: jvnascimento --- DIRECTORY.md | 1 + sorts/patience_sort.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 sorts/patience_sort.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 0f4aeefe3b82..d9196e130196 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -761,6 +761,7 @@ * [Odd Even Transposition Parallel](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_parallel.py) * [Odd Even Transposition Single Threaded](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_single_threaded.py) * [Pancake Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/pancake_sort.py) + * [Patience Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/patience_sort.py) * [Pigeon Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/pigeon_sort.py) * [Pigeonhole Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/pigeonhole_sort.py) * [Quick Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/quick_sort.py) diff --git a/sorts/patience_sort.py b/sorts/patience_sort.py new file mode 100644 index 000000000000..f4e35d9a0ac6 --- /dev/null +++ b/sorts/patience_sort.py @@ -0,0 +1,64 @@ +from bisect import bisect_left +from functools import total_ordering +from heapq import merge + +""" +A pure Python implementation of the patience sort algorithm + +For more information: https://en.wikipedia.org/wiki/Patience_sorting + +This algorithm is based on the card game patience + +For doctests run following command: +python3 -m doctest -v patience_sort.py + +For manual testing run: +python3 patience_sort.py +""" + + +@total_ordering +class Stack(list): + def __lt__(self, other): + return self[-1] < other[-1] + + def __eq__(self, other): + return self[-1] == other[-1] + + +def patience_sort(collection: list) -> list: + """A pure implementation of quick sort algorithm in Python + + :param collection: some mutable ordered collection with heterogeneous + comparable items inside + :return: the same collection ordered by ascending + + Examples: + >>> patience_sort([1, 9, 5, 21, 17, 6]) + [1, 5, 6, 9, 17, 21] + + >>> patience_sort([]) + [] + + >>> patience_sort([-3, -17, -48]) + [-48, -17, -3] + """ + stacks = [] + # sort into stacks + for element in collection: + new_stacks = Stack([element]) + i = bisect_left(stacks, new_stacks) + if i != len(stacks): + stacks[i].append(element) + else: + stacks.append(new_stacks) + + # use a heap-based merge to merge stack efficiently + collection[:] = merge(*[reversed(stack) for stack in stacks]) + return collection + + +if __name__ == "__main__": + user_input = input("Enter numbers separated by a comma:\n").strip() + unsorted = [int(item) for item in user_input.split(",")] + print(patience_sort(unsorted)) From 03d480e11525afcbf28049d76022d03c0d96c2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Schr=C3=B6der?= <5970416+tbsschroeder@users.noreply.github.com> Date: Mon, 19 Oct 2020 03:07:18 +0200 Subject: [PATCH 016/195] Add a naive recursive implementation of 0-1 Knapsack Problem (#2743) * Add naive recursive implementation of 0-1 Knapsack problem * Fix shadowing * Add doctest * Fix type hints * Add link to wiki * Blacked the file * Fix isort * Move knapsack / add readme and more tests * Add missed main in tests --- knapsack/README.md | 32 ++++++++++++++++++++++++ knapsack/__init__.py | 0 knapsack/knapsack.py | 47 +++++++++++++++++++++++++++++++++++ knapsack/test_knapsack.py | 52 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 knapsack/README.md create mode 100644 knapsack/__init__.py create mode 100644 knapsack/knapsack.py create mode 100644 knapsack/test_knapsack.py diff --git a/knapsack/README.md b/knapsack/README.md new file mode 100644 index 000000000000..6041c1e48eb8 --- /dev/null +++ b/knapsack/README.md @@ -0,0 +1,32 @@ +# A naive recursive implementation of 0-1 Knapsack Problem + +This overview is taken from: + + https://en.wikipedia.org/wiki/Knapsack_problem + +--- + +## Overview + +The knapsack problem is a problem in combinatorial optimization: Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack and must fill it with the most valuable items. The problem often arises in resource allocation where the decision makers have to choose from a set of non-divisible projects or tasks under a fixed budget or time constraint, respectively. + +The knapsack problem has been studied for more than a century, with early works dating as far back as 1897 The name "knapsack problem" dates back to the early works of mathematician Tobias Dantzig (1884–1956), and refers to the commonplace problem of packing the most valuable or useful items without overloading the luggage. + +--- + +## Documentation + +This module uses docstrings to enable the use of Python's in-built `help(...)` function. +For instance, try `help(Vector)`, `help(unitBasisVector)`, and `help(CLASSNAME.METHODNAME)`. + +--- + +## Usage + +Import the module `knapsack.py` from the **.** directory into your project. + +--- + +## Tests + +`.` contains Python unit tests which can be run with `python3 -m unittest -v`. diff --git a/knapsack/__init__.py b/knapsack/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/knapsack/knapsack.py b/knapsack/knapsack.py new file mode 100644 index 000000000000..756443ea6163 --- /dev/null +++ b/knapsack/knapsack.py @@ -0,0 +1,47 @@ +from typing import List + +""" A naive recursive implementation of 0-1 Knapsack Problem + https://en.wikipedia.org/wiki/Knapsack_problem +""" + + +def knapsack(capacity: int, weights: List[int], values: List[int], counter: int) -> int: + """ + Returns the maximum value that can be put in a knapsack of a capacity cap, + whereby each weight w has a specific value val. + + >>> cap = 50 + >>> val = [60, 100, 120] + >>> w = [10, 20, 30] + >>> c = len(val) + >>> knapsack(cap, w, val, c) + 220 + + The result is 220 cause the values of 100 and 120 got the weight of 50 + which is the limit of the capacity. + """ + + # Base Case + if counter == 0 or capacity == 0: + return 0 + + # If weight of the nth item is more than Knapsack of capacity, + # then this item cannot be included in the optimal solution, + # else return the maximum of two cases: + # (1) nth item included + # (2) not included + if weights[counter - 1] > capacity: + return knapsack(capacity, weights, values, counter - 1) + else: + left_capacity = capacity - weights[counter - 1] + new_value_included = values[counter - 1] + knapsack( + left_capacity, weights, values, counter - 1 + ) + without_new_value = knapsack(capacity, weights, values, counter - 1) + return max(new_value_included, without_new_value) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/knapsack/test_knapsack.py b/knapsack/test_knapsack.py new file mode 100644 index 000000000000..248855fbce53 --- /dev/null +++ b/knapsack/test_knapsack.py @@ -0,0 +1,52 @@ +""" +Created on Fri Oct 16 09:31:07 2020 + +@author: Dr. Tobias Schröder +@license: MIT-license + +This file contains the test-suite for the knapsack problem. +""" +import unittest + +from knapsack import knapsack as k + + +class Test(unittest.TestCase): + def test_base_case(self): + """ + test for the base case + """ + cap = 0 + val = [0] + w = [0] + c = len(val) + self.assertEqual(k.knapsack(cap, w, val, c), 0) + + val = [60] + w = [10] + c = len(val) + self.assertEqual(k.knapsack(cap, w, val, c), 0) + + def test_easy_case(self): + """ + test for the base case + """ + cap = 3 + val = [1, 2, 3] + w = [3, 2, 1] + c = len(val) + self.assertEqual(k.knapsack(cap, w, val, c), 5) + + def test_knapsack(self): + """ + test for the knapsack + """ + cap = 50 + val = [60, 100, 120] + w = [10, 20, 30] + c = len(val) + self.assertEqual(k.knapsack(cap, w, val, c), 220) + + +if __name__ == "__main__": + unittest.main() From c66a7728f7d548b94c40b98d33f7b76e8b6522fd Mon Sep 17 00:00:00 2001 From: anneCoder1805 <66819522+anneCoder1805@users.noreply.github.com> Date: Tue, 20 Oct 2020 16:08:49 +0530 Subject: [PATCH 017/195] Median of Two Arrays (#3554) * Create medianOf TwoArrays.py This code finds the median of two arrays (which may or may not be sorted initially). Example: Enter elements of an array: 1 5 4 2 Enter elements of another array: 1 7 4 2 7 The median of two arrays is : 4 * Rename medianOf TwoArrays.py to median_of _two_arrays.py * Rename median_of _two_arrays.py to median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py * Update median_of_two_arrays.py --- other/median_of_two_arrays.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 other/median_of_two_arrays.py diff --git a/other/median_of_two_arrays.py b/other/median_of_two_arrays.py new file mode 100644 index 000000000000..cde12f5d7e3b --- /dev/null +++ b/other/median_of_two_arrays.py @@ -0,0 +1,33 @@ +from typing import List + + +def median_of_two_arrays(nums1: List[float], nums2: List[float]) -> float: + """ + >>> median_of_two_arrays([1, 2], [3]) + 2 + >>> median_of_two_arrays([0, -1.1], [2.5, 1]) + 0.5 + >>> median_of_two_arrays([], [2.5, 1]) + 1.75 + >>> median_of_two_arrays([], [0]) + 0 + >>> median_of_two_arrays([], []) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + all_numbers = sorted(nums1 + nums2) + div, mod = divmod(len(all_numbers), 2) + if mod == 1: + return all_numbers[div] + else: + return (all_numbers[div] + all_numbers[div - 1]) / 2 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + array_1 = [float(x) for x in input("Enter the elements of first array: ").split()] + array_2 = [float(x) for x in input("Enter the elements of second array: ").split()] + print(f"The median of two arrays is: {median_of_two_arrays(array_1, array_2)}") From b0b47e66e3c054da102dc72e73ae8f32d74d4ea1 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 21 Oct 2020 12:46:14 +0200 Subject: [PATCH 018/195] Pyupgrade to python3.8 (#3616) * Upgrade to Python 3.8 syntax * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 +++++ ciphers/simple_substitution_cipher.py | 2 +- ciphers/xor_cipher.py | 8 ++++---- compression/lempel_ziv.py | 6 +++--- compression/lempel_ziv_decompress.py | 6 +++--- data_structures/binary_tree/segment_tree_other.py | 6 +++--- data_structures/heap/heap.py | 2 +- data_structures/linked_list/deque_doubly.py | 2 +- graphs/minimum_spanning_tree_boruvka.py | 2 +- machine_learning/astar.py | 4 ++-- machine_learning/k_means_clust.py | 2 +- maths/entropy.py | 6 +++--- maths/numerical_integration.py | 2 +- project_euler/problem_013/sol1.py | 2 +- project_euler/problem_018/solution.py | 2 +- project_euler/problem_042/solution42.py | 2 +- project_euler/problem_049/sol1.py | 2 +- project_euler/problem_054/sol1.py | 4 ++-- project_euler/problem_054/test_poker_hand.py | 2 +- project_euler/problem_063/sol1.py | 2 +- project_euler/problem_067/sol1.py | 2 +- 21 files changed, 38 insertions(+), 33 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index d9196e130196..866e0d658bf6 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -323,6 +323,10 @@ * [Sdbm](https://github.com/TheAlgorithms/Python/blob/master/hashes/sdbm.py) * [Sha1](https://github.com/TheAlgorithms/Python/blob/master/hashes/sha1.py) +## Knapsack + * [Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/knapsack.py) + * [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/test_knapsack.py) + ## Linear Algebra * Src * [Lib](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/lib.py) @@ -502,6 +506,7 @@ * [Magicdiamondpattern](https://github.com/TheAlgorithms/Python/blob/master/other/magicdiamondpattern.py) * [Markov Chain](https://github.com/TheAlgorithms/Python/blob/master/other/markov_chain.py) * [Max Sum Sliding Window](https://github.com/TheAlgorithms/Python/blob/master/other/max_sum_sliding_window.py) + * [Median Of Two Arrays](https://github.com/TheAlgorithms/Python/blob/master/other/median_of_two_arrays.py) * [Nested Brackets](https://github.com/TheAlgorithms/Python/blob/master/other/nested_brackets.py) * [Palindrome](https://github.com/TheAlgorithms/Python/blob/master/other/palindrome.py) * [Password Generator](https://github.com/TheAlgorithms/Python/blob/master/other/password_generator.py) diff --git a/ciphers/simple_substitution_cipher.py b/ciphers/simple_substitution_cipher.py index f5b711e616af..646ea449fc06 100644 --- a/ciphers/simple_substitution_cipher.py +++ b/ciphers/simple_substitution_cipher.py @@ -18,7 +18,7 @@ def main(): mode = "decrypt" translated = decryptMessage(key, message) - print("\n{}ion: \n{}".format(mode.title(), translated)) + print(f"\n{mode.title()}ion: \n{translated}") def checkValidKey(key: str) -> None: diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 27e4262bc924..32a350d4e61c 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -141,14 +141,14 @@ def encrypt_file(self, file: str, key: int = 0) -> bool: assert isinstance(file, str) and isinstance(key, int) try: - with open(file, "r") as fin: + with open(file) as fin: with open("encrypt.out", "w+") as fout: # actual encrypt-process for line in fin: fout.write(self.encrypt_string(line, key)) - except IOError: + except OSError: return False return True @@ -166,14 +166,14 @@ def decrypt_file(self, file: str, key: int) -> bool: assert isinstance(file, str) and isinstance(key, int) try: - with open(file, "r") as fin: + with open(file) as fin: with open("decrypt.out", "w+") as fout: # actual encrypt-process for line in fin: fout.write(self.decrypt_string(line, key)) - except IOError: + except OSError: return False return True diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index 3ac8573c43d8..2d0601b27b34 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -17,10 +17,10 @@ def read_file_binary(file_path: str) -> str: with open(file_path, "rb") as binary_file: data = binary_file.read() for dat in data: - curr_byte = "{0:08b}".format(dat) + curr_byte = f"{dat:08b}" result += curr_byte return result - except IOError: + except OSError: print("File not accessible") sys.exit() @@ -105,7 +105,7 @@ def write_file_binary(file_path: str, to_write: str) -> None: for elem in result_byte_array: opened_file.write(int(elem, 2).to_bytes(1, byteorder="big")) - except IOError: + except OSError: print("File not accessible") sys.exit() diff --git a/compression/lempel_ziv_decompress.py b/compression/lempel_ziv_decompress.py index 05c26740bf62..4d3c2c0d2cf3 100644 --- a/compression/lempel_ziv_decompress.py +++ b/compression/lempel_ziv_decompress.py @@ -16,10 +16,10 @@ def read_file_binary(file_path: str) -> str: with open(file_path, "rb") as binary_file: data = binary_file.read() for dat in data: - curr_byte = "{0:08b}".format(dat) + curr_byte = f"{dat:08b}" result += curr_byte return result - except IOError: + except OSError: print("File not accessible") sys.exit() @@ -76,7 +76,7 @@ def write_file_binary(file_path: str, to_write: str) -> None: for elem in result_byte_array[:-1]: opened_file.write(int(elem, 2).to_bytes(1, byteorder="big")) - except IOError: + except OSError: print("File not accessible") sys.exit() diff --git a/data_structures/binary_tree/segment_tree_other.py b/data_structures/binary_tree/segment_tree_other.py index df98eeffb3c6..90afd7ca8b71 100644 --- a/data_structures/binary_tree/segment_tree_other.py +++ b/data_structures/binary_tree/segment_tree_other.py @@ -7,7 +7,7 @@ from queue import Queue -class SegmentTreeNode(object): +class SegmentTreeNode: def __init__(self, start, end, val, left=None, right=None): self.start = start self.end = end @@ -17,10 +17,10 @@ def __init__(self, start, end, val, left=None, right=None): self.right = right def __str__(self): - return "val: %s, start: %s, end: %s" % (self.val, self.start, self.end) + return f"val: {self.val}, start: {self.start}, end: {self.end}" -class SegmentTree(object): +class SegmentTree: """ >>> import operator >>> num_arr = SegmentTree([2, 1, 5, 3, 4], operator.add) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index b901c54a4284..2dc047436a77 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 -class Heap(object): +class Heap: """ >>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5] >>> h = Heap() diff --git a/data_structures/linked_list/deque_doubly.py b/data_structures/linked_list/deque_doubly.py index b93fb8c4005e..894f91d561cc 100644 --- a/data_structures/linked_list/deque_doubly.py +++ b/data_structures/linked_list/deque_doubly.py @@ -20,7 +20,7 @@ def __init__(self, link_p, element, link_n): self._next = link_n def has_next_and_prev(self): - return " Prev -> {0}, Next -> {1}".format( + return " Prev -> {}, Next -> {}".format( self._prev is not None, self._next is not None ) diff --git a/graphs/minimum_spanning_tree_boruvka.py b/graphs/minimum_spanning_tree_boruvka.py index 3b05f94b5140..32548b2ecb6c 100644 --- a/graphs/minimum_spanning_tree_boruvka.py +++ b/graphs/minimum_spanning_tree_boruvka.py @@ -99,7 +99,7 @@ def build(vertices=None, edges=None): g.add_edge(*edge) return g - class UnionFind(object): + class UnionFind: """ Disjoint set Union and Find for Boruvka's algorithm """ diff --git a/machine_learning/astar.py b/machine_learning/astar.py index 2f5c21a2bd5f..ee3fcff0b7bf 100644 --- a/machine_learning/astar.py +++ b/machine_learning/astar.py @@ -13,7 +13,7 @@ import numpy as np -class Cell(object): +class Cell: """ Class cell represents a cell in the world which have the property position : The position of the represented by tupleof x and y @@ -45,7 +45,7 @@ def showcell(self): print(self.position) -class Gridworld(object): +class Gridworld: """ Gridworld class represents the external world here a grid M*M matrix diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 130e7f1ad669..f155d4845f41 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -251,7 +251,7 @@ def ReportGenerator( lambda x: np.mean( np.nan_to_num( sorted(x)[ - round((len(x) * 25 / 100)) : round(len(x) * 75 / 100) + round(len(x) * 25 / 100) : round(len(x) * 75 / 100) ] ) ), diff --git a/maths/entropy.py b/maths/entropy.py index 74980ef9e0c6..43bb3860fc12 100644 --- a/maths/entropy.py +++ b/maths/entropy.py @@ -68,7 +68,7 @@ def calculate_prob(text: str) -> None: my_fir_sum += prob * math.log2(prob) # entropy formula. # print entropy - print("{0:.1f}".format(round(-1 * my_fir_sum))) + print("{:.1f}".format(round(-1 * my_fir_sum))) # two len string all_sum = sum(two_char_strings.values()) @@ -83,10 +83,10 @@ def calculate_prob(text: str) -> None: my_sec_sum += prob * math.log2(prob) # print second entropy - print("{0:.1f}".format(round(-1 * my_sec_sum))) + print("{:.1f}".format(round(-1 * my_sec_sum))) # print the difference between them - print("{0:.1f}".format(round(((-1 * my_sec_sum) - (-1 * my_fir_sum))))) + print("{:.1f}".format(round((-1 * my_sec_sum) - (-1 * my_fir_sum)))) def analyze_text(text: str) -> tuple[dict, dict]: diff --git a/maths/numerical_integration.py b/maths/numerical_integration.py index 67fbc0ddbf30..87184a76b740 100644 --- a/maths/numerical_integration.py +++ b/maths/numerical_integration.py @@ -62,5 +62,5 @@ def f(x): i = 10 while i <= 100000: area = trapezoidal_area(f, -5, 5, i) - print("with {} steps: {}".format(i, area)) + print(f"with {i} steps: {area}") i *= 10 diff --git a/project_euler/problem_013/sol1.py b/project_euler/problem_013/sol1.py index 19b427337c3d..1ea08b12ee93 100644 --- a/project_euler/problem_013/sol1.py +++ b/project_euler/problem_013/sol1.py @@ -17,7 +17,7 @@ def solution(): '5537376230' """ file_path = os.path.join(os.path.dirname(__file__), "num.txt") - with open(file_path, "r") as file_hand: + with open(file_path) as file_hand: return str(sum([int(line) for line in file_hand]))[:10] diff --git a/project_euler/problem_018/solution.py b/project_euler/problem_018/solution.py index 38593813901e..82fc3ce3c9db 100644 --- a/project_euler/problem_018/solution.py +++ b/project_euler/problem_018/solution.py @@ -41,7 +41,7 @@ def solution(): script_dir = os.path.dirname(os.path.realpath(__file__)) triangle = os.path.join(script_dir, "triangle.txt") - with open(triangle, "r") as f: + with open(triangle) as f: triangle = f.readlines() a = [[int(y) for y in x.rstrip("\r\n").split(" ")] for x in triangle] diff --git a/project_euler/problem_042/solution42.py b/project_euler/problem_042/solution42.py index 1e9bb49c7a06..b3aecf4cf144 100644 --- a/project_euler/problem_042/solution42.py +++ b/project_euler/problem_042/solution42.py @@ -30,7 +30,7 @@ def solution(): wordsFilePath = os.path.join(script_dir, "words.txt") words = "" - with open(wordsFilePath, "r") as f: + with open(wordsFilePath) as f: words = f.readline() words = list(map(lambda word: word.strip('"'), words.strip("\r\n").split(","))) diff --git a/project_euler/problem_049/sol1.py b/project_euler/problem_049/sol1.py index 6c3d69ad0d11..c0d0715be91c 100644 --- a/project_euler/problem_049/sol1.py +++ b/project_euler/problem_049/sol1.py @@ -114,7 +114,7 @@ def solution(): if ( abs(candidate[i] - candidate[j]) == abs(candidate[j] - candidate[k]) - and len(set([candidate[i], candidate[j], candidate[k]])) == 3 + and len({candidate[i], candidate[j], candidate[k]}) == 3 ): passed.append( sorted([candidate[i], candidate[j], candidate[k]]) diff --git a/project_euler/problem_054/sol1.py b/project_euler/problem_054/sol1.py index 4d75271784de..d2fd810d1b69 100644 --- a/project_euler/problem_054/sol1.py +++ b/project_euler/problem_054/sol1.py @@ -45,7 +45,7 @@ import os -class PokerHand(object): +class PokerHand: """Create an object representing a Poker Hand based on an input of a string which represents the best 5 card combination from the player's hand and board cards. @@ -366,7 +366,7 @@ def solution() -> int: answer = 0 script_dir = os.path.abspath(os.path.dirname(__file__)) poker_hands = os.path.join(script_dir, "poker_hands.txt") - with open(poker_hands, "r") as file_hand: + with open(poker_hands) as file_hand: for line in file_hand: player_hand = line[:14].strip() opponent_hand = line[15:].strip() diff --git a/project_euler/problem_054/test_poker_hand.py b/project_euler/problem_054/test_poker_hand.py index f60c3aba6616..96317fc7df33 100644 --- a/project_euler/problem_054/test_poker_hand.py +++ b/project_euler/problem_054/test_poker_hand.py @@ -217,7 +217,7 @@ def test_euler_project(): answer = 0 script_dir = os.path.abspath(os.path.dirname(__file__)) poker_hands = os.path.join(script_dir, "poker_hands.txt") - with open(poker_hands, "r") as file_hand: + with open(poker_hands) as file_hand: for line in file_hand: player_hand = line[:14].strip() opponent_hand = line[15:].strip() diff --git a/project_euler/problem_063/sol1.py b/project_euler/problem_063/sol1.py index f6a8d3240ffd..29efddba4216 100644 --- a/project_euler/problem_063/sol1.py +++ b/project_euler/problem_063/sol1.py @@ -26,7 +26,7 @@ def solution(max_base: int = 10, max_power: int = 22) -> int: bases = range(1, max_base) powers = range(1, max_power) return sum( - 1 for power in powers for base in bases if len(str((base ** power))) == power + 1 for power in powers for base in bases if len(str(base ** power)) == power ) diff --git a/project_euler/problem_067/sol1.py b/project_euler/problem_067/sol1.py index 9494ff7bbabd..ebfa865a9479 100644 --- a/project_euler/problem_067/sol1.py +++ b/project_euler/problem_067/sol1.py @@ -25,7 +25,7 @@ def solution(): script_dir = os.path.dirname(os.path.realpath(__file__)) triangle = os.path.join(script_dir, "triangle.txt") - with open(triangle, "r") as f: + with open(triangle) as f: triangle = f.readlines() a = map(lambda x: x.rstrip("\r\n").split(" "), triangle) From 9b391b136ef854e1aaaa70bc9531dc97fbe000ed Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Wed, 21 Oct 2020 22:31:09 +0800 Subject: [PATCH 019/195] Update doubly linked list (#3619) * update doubly linked list * reformat code add more test * add test to iter * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .../linked_list/doubly_linked_list.py | 264 ++++++++++++------ 1 file changed, 185 insertions(+), 79 deletions(-) diff --git a/data_structures/linked_list/doubly_linked_list.py b/data_structures/linked_list/doubly_linked_list.py index 1b4005f59fae..0eb3cf101a3e 100644 --- a/data_structures/linked_list/doubly_linked_list.py +++ b/data_structures/linked_list/doubly_linked_list.py @@ -1,89 +1,156 @@ """ -- A linked list is similar to an array, it holds values. However, links in a linked - list do not have indexes. -- This is an example of a double ended, doubly linked list. -- Each link references the next link and the previous one. -- A Doubly Linked List (DLL) contains an extra pointer, typically called previous - pointer, together with next pointer and data which are there in singly linked list. - - Advantages over SLL - It can be traversed in both forward and backward direction. - Delete operation is more efficient""" - +https://en.wikipedia.org/wiki/Doubly_linked_list +""" -class LinkedList: - """ - >>> linked_list = LinkedList() - >>> linked_list.insert_at_head("a") - >>> linked_list.insert_at_tail("b") - >>> linked_list.delete_tail() - 'b' - >>> linked_list.is_empty - False - >>> linked_list.delete_head() - 'a' - >>> linked_list.is_empty - True - """ - def __init__(self): - self.head = None # First node in list - self.tail = None # Last node in list +class Node: + def __init__(self, data): + self.data = data + self.previous = None + self.next = None def __str__(self): - current = self.head - nodes = [] - while current is not None: - nodes.append(current) - current = current.next - return " ".join(str(node) for node in nodes) - - def insert_at_head(self, data): - new_node = Node(data) - if self.is_empty: - self.tail = new_node - self.head = new_node - else: - self.head.previous = new_node - new_node.next = self.head - self.head = new_node + return f"{self.data}" - def delete_head(self) -> str: - if self.is_empty: - return "List is empty" - head_data = self.head.data - if self.head.next: - self.head = self.head.next - self.head.previous = None +class DoublyLinkedList: + def __init__(self): + self.head = None + self.tail = None + + def __iter__(self): + """ + >>> linked_list = DoublyLinkedList() + >>> linked_list.insert_at_head('b') + >>> linked_list.insert_at_head('a') + >>> linked_list.insert_at_tail('c') + >>> tuple(linked_list) + ('a', 'b', 'c') + """ + node = self.head + while node: + yield node.data + node = node.next - else: # If there is no next previous node - self.head = None - self.tail = None + def __str__(self): + """ + >>> linked_list = DoublyLinkedList() + >>> linked_list.insert_at_tail('a') + >>> linked_list.insert_at_tail('b') + >>> linked_list.insert_at_tail('c') + >>> str(linked_list) + 'a->b->c' + """ + return "->".join([str(item) for item in self]) + + def __len__(self): + """ + >>> linked_list = DoublyLinkedList() + >>> for i in range(0, 5): + ... linked_list.insert_at_nth(i, i + 1) + >>> len(linked_list) == 5 + True + """ + return len(tuple(iter(self))) - return head_data + def insert_at_head(self, data): + self.insert_at_nth(0, data) def insert_at_tail(self, data): + self.insert_at_nth(len(self), data) + + def insert_at_nth(self, index: int, data): + """ + >>> linked_list = DoublyLinkedList() + >>> linked_list.insert_at_nth(-1, 666) + Traceback (most recent call last): + .... + IndexError: list index out of range + >>> linked_list.insert_at_nth(1, 666) + Traceback (most recent call last): + .... + IndexError: list index out of range + >>> linked_list.insert_at_nth(0, 2) + >>> linked_list.insert_at_nth(0, 1) + >>> linked_list.insert_at_nth(2, 4) + >>> linked_list.insert_at_nth(2, 3) + >>> str(linked_list) + '1->2->3->4' + >>> linked_list.insert_at_nth(5, 5) + Traceback (most recent call last): + .... + IndexError: list index out of range + """ + if not 0 <= index <= len(self): + raise IndexError("list index out of range") new_node = Node(data) - if self.is_empty: - self.tail = new_node + if self.head is None: + self.head = self.tail = new_node + elif index == 0: + self.head.previous = new_node + new_node.next = self.head self.head = new_node - else: + elif index == len(self): self.tail.next = new_node new_node.previous = self.tail self.tail = new_node - - def delete_tail(self) -> str: - if self.is_empty: - return "List is empty" - - tail_data = self.tail.data - if self.tail.previous: + else: + temp = self.head + for i in range(0, index): + temp = temp.next + temp.previous.next = new_node + new_node.previous = temp.previous + new_node.next = temp + temp.previous = new_node + + def delete_head(self): + return self.delete_at_nth(0) + + def delete_tail(self): + return self.delete_at_nth(len(self) - 1) + + def delete_at_nth(self, index: int): + """ + >>> linked_list = DoublyLinkedList() + >>> linked_list.delete_at_nth(0) + Traceback (most recent call last): + .... + IndexError: list index out of range + >>> for i in range(0, 5): + ... linked_list.insert_at_nth(i, i + 1) + >>> linked_list.delete_at_nth(0) == 1 + True + >>> linked_list.delete_at_nth(3) == 5 + True + >>> linked_list.delete_at_nth(1) == 3 + True + >>> str(linked_list) + '2->4' + >>> linked_list.delete_at_nth(2) + Traceback (most recent call last): + .... + IndexError: list index out of range + """ + if not 0 <= index <= len(self) - 1: + raise IndexError("list index out of range") + delete_node = self.head # default first node + if len(self) == 1: + self.head = self.tail = None + elif index == 0: + self.head = self.head.next + self.head.previous = None + elif index == len(self) - 1: + delete_node = self.tail self.tail = self.tail.previous self.tail.next = None - else: # if there is no previous node - self.head = None - self.tail = None - - return tail_data + else: + temp = self.head + for i in range(0, index): + temp = temp.next + delete_node = temp + temp.next.previous = temp.previous + temp.previous.next = temp.next + return delete_node.data def delete(self, data) -> str: current = self.head @@ -105,16 +172,55 @@ def delete(self, data) -> str: current.next.previous = current.previous # 1 <--> 3 return data - @property - def is_empty(self): # return True if the list is empty - return self.head is None + def is_empty(self): + """ + >>> linked_list = DoublyLinkedList() + >>> linked_list.is_empty() + True + >>> linked_list.insert_at_tail(1) + >>> linked_list.is_empty() + False + """ + return len(self) == 0 -class Node: - def __init__(self, data): - self.data = data - self.previous = None - self.next = None - - def __str__(self): - return f"{self.data}" +def test_doubly_linked_list() -> None: + """ + >>> test_doubly_linked_list() + """ + linked_list = DoublyLinkedList() + assert linked_list.is_empty() is True + assert str(linked_list) == "" + + try: + linked_list.delete_head() + assert False # This should not happen. + except IndexError: + assert True # This should happen. + + try: + linked_list.delete_tail() + assert False # This should not happen. + except IndexError: + assert True # This should happen. + + for i in range(10): + assert len(linked_list) == i + linked_list.insert_at_nth(i, i + 1) + assert str(linked_list) == "->".join(str(i) for i in range(1, 11)) + + linked_list.insert_at_head(0) + linked_list.insert_at_tail(11) + assert str(linked_list) == "->".join(str(i) for i in range(0, 12)) + + assert linked_list.delete_head() == 0 + assert linked_list.delete_at_nth(9) == 10 + assert linked_list.delete_tail() == 11 + assert len(linked_list) == 9 + assert str(linked_list) == "->".join(str(i) for i in range(1, 10)) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 40543e66dd8aa632543e574d5d4130ce4b9cdc02 Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Sat, 24 Oct 2020 00:16:23 +0800 Subject: [PATCH 020/195] Update Linked Stack (#3625) * update linked_stack * remove properties * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- data_structures/stacks/linked_stack.py | 134 +++++++++++++++++++++---- 1 file changed, 112 insertions(+), 22 deletions(-) diff --git a/data_structures/stacks/linked_stack.py b/data_structures/stacks/linked_stack.py index 18ba87ddc221..1a2d07f20e7c 100644 --- a/data_structures/stacks/linked_stack.py +++ b/data_structures/stacks/linked_stack.py @@ -1,11 +1,14 @@ -""" A Stack using a Linked List like structure """ -from typing import Any, Optional +""" A Stack using a linked list like structure """ +from typing import Any class Node: - def __init__(self, data: Any, next: Optional["Node"] = None): - self.data: Any = data - self.next: Optional["Node"] = next + def __init__(self, data): + self.data = data + self.next = None + + def __str__(self): + return f"{self.data}" class LinkedStack: @@ -19,7 +22,7 @@ class LinkedStack: >>> stack.push(5) >>> stack.push(9) >>> stack.push('python') - >>> stack.is_empty(); + >>> stack.is_empty() False >>> stack.pop() 'python' @@ -39,29 +42,116 @@ class LinkedStack: """ def __init__(self) -> None: - self.top: Optional[Node] = None + self.top = None + + def __iter__(self): + node = self.top + while node: + yield node.data + node = node.next + + def __str__(self): + """ + >>> stack = LinkedStack() + >>> stack.push("c") + >>> stack.push("b") + >>> stack.push("a") + >>> str(stack) + 'a->b->c' + """ + return "->".join([str(item) for item in self]) + + def __len__(self): + """ + >>> stack = LinkedStack() + >>> len(stack) == 0 + True + >>> stack.push("c") + >>> stack.push("b") + >>> stack.push("a") + >>> len(stack) == 3 + True + """ + return len(tuple(iter(self))) def is_empty(self) -> bool: - """ returns boolean describing if stack is empty """ + """ + >>> stack = LinkedStack() + >>> stack.is_empty() + True + >>> stack.push(1) + >>> stack.is_empty() + False + """ return self.top is None def push(self, item: Any) -> None: - """ append item to top of stack """ - node: Node = Node(item) - if self.is_empty(): - self.top = node - else: - # each node points to the item "lower" in the stack + """ + >>> stack = LinkedStack() + >>> stack.push("Python") + >>> stack.push("Java") + >>> stack.push("C") + >>> str(stack) + 'C->Java->Python' + """ + node = Node(item) + if not self.is_empty(): node.next = self.top - self.top = node + self.top = node def pop(self) -> Any: - """ returns and removes item at top of stack """ + """ + >>> stack = LinkedStack() + >>> stack.pop() + Traceback (most recent call last): + ... + IndexError: pop from empty stack + >>> stack.push("c") + >>> stack.push("b") + >>> stack.push("a") + >>> stack.pop() == 'a' + True + >>> stack.pop() == 'b' + True + >>> stack.pop() == 'c' + True + """ if self.is_empty(): raise IndexError("pop from empty stack") - else: - # "remove" element by having top point to the next one - assert isinstance(self.top, Node) - node: Node = self.top - self.top = node.next - return node.data + assert isinstance(self.top, Node) + pop_node = self.top + self.top = self.top.next + return pop_node.data + + def peek(self) -> Any: + """ + >>> stack = LinkedStack() + >>> stack.push("Java") + >>> stack.push("C") + >>> stack.push("Python") + >>> stack.peek() + 'Python' + """ + if self.is_empty(): + raise IndexError("peek from empty stack") + return self.top.data + + def clear(self) -> None: + """ + >>> stack = LinkedStack() + >>> stack.push("Java") + >>> stack.push("C") + >>> stack.push("Python") + >>> str(stack) + 'Python->C->Java' + >>> stack.clear() + >>> len(stack) == 0 + True + """ + self.top = None + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 444f7926845c9b3142616c62fdf5795da7f40700 Mon Sep 17 00:00:00 2001 From: Rolv Apneseth Date: Fri, 23 Oct 2020 17:17:29 +0100 Subject: [PATCH 021/195] Improved and shortened prime_check.py (#3454) * Made small improvements and shortened prime_check.py * improved descriptions on tests in prime_check.py * Ran black and isort --- maths/prime_check.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/maths/prime_check.py b/maths/prime_check.py index ed8fbbae809a..e2bcb7b8f151 100644 --- a/maths/prime_check.py +++ b/maths/prime_check.py @@ -5,25 +5,20 @@ def prime_check(number: int) -> bool: - """ - Check to See if a Number is Prime. + """Checks to see if a number is a prime. - A number is prime if it has exactly two dividers: 1 and itself. + A number is prime if it has exactly two factors: 1 and itself. """ - if number < 2: - # Negatives, 0 and 1 are not primes - return False - if number < 4: + + if 1 < number < 4: # 2 and 3 are primes return True - if number % 2 == 0: - # Even values are not primes + elif number < 2 or not number % 2: + # Negatives, 0, 1 and all even numbers are not primes return False - # Except 2, all primes are odd. If any odd value divide - # the number, then that number is not prime. - odd_numbers = range(3, int(math.sqrt(number)) + 1, 2) - return not any(number % i == 0 for i in odd_numbers) + odd_numbers = range(3, int(math.sqrt(number) + 1), 2) + return not any(not number % i for i in odd_numbers) class Test(unittest.TestCase): @@ -40,12 +35,17 @@ def test_primes(self): self.assertTrue(prime_check(29)) def test_not_primes(self): - self.assertFalse(prime_check(-19), "Negative numbers are not prime.") self.assertFalse( - prime_check(0), "Zero doesn't have any divider, primes must have two." + prime_check(-19), + "Negative numbers are excluded by definition of prime numbers.", + ) + self.assertFalse( + prime_check(0), + "Zero doesn't have any positive factors, primes must have exactly two.", ) self.assertFalse( - prime_check(1), "One just have 1 divider, primes must have two." + prime_check(1), + "One only has 1 positive factor, primes must have exactly two.", ) self.assertFalse(prime_check(2 * 2)) self.assertFalse(prime_check(2 * 3)) From 7bbd8a3b898b93431524ed1710fe7d6be2c2c30f Mon Sep 17 00:00:00 2001 From: Himadri Ganguly Date: Fri, 23 Oct 2020 22:25:13 +0530 Subject: [PATCH 022/195] Fix coin change (#2571) * Removed unused variable m. * Doctests are modified to match functions. * Added condition for negative values. * Fixed white-space around operator. * Fixed W293 blank line contains white-space error. * Update dynamic_programming/coin_change.py Co-authored-by: Tapajyoti Bose <44058757+ruppysuppy@users.noreply.github.com> * Fixed error in code. * Fixed whited spacing. * Fixed PEP8 error. * Added more test cases for coin change problem. * Removed extra test for negetive value. Co-authored-by: Tapajyoti Bose <44058757+ruppysuppy@users.noreply.github.com> --- dynamic_programming/coin_change.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dynamic_programming/coin_change.py b/dynamic_programming/coin_change.py index 2d7106f0cc6f..2869b5857be1 100644 --- a/dynamic_programming/coin_change.py +++ b/dynamic_programming/coin_change.py @@ -7,20 +7,23 @@ """ -def dp_count(S, m, n): +def dp_count(S, n): """ - >>> dp_count([1, 2, 3], 3, 4) + >>> dp_count([1, 2, 3], 4) 4 - >>> dp_count([1, 2, 3], 3, 7) + >>> dp_count([1, 2, 3], 7) 8 - >>> dp_count([2, 5, 3, 6], 4, 10) + >>> dp_count([2, 5, 3, 6], 10) 5 - >>> dp_count([10], 1, 99) + >>> dp_count([10], 99) 0 - >>> dp_count([4, 5, 6], 3, 0) + >>> dp_count([4, 5, 6], 0) 1 + >>> dp_count([1, 2, 3], -5) + 0 """ - + if n < 0: + return 0 # table[i] represents the number of ways to get to amount i table = [0] * (n + 1) From 07a85533e7a0f250e37d9c29aeda7f86bdeded3d Mon Sep 17 00:00:00 2001 From: Peter Yao Date: Fri, 23 Oct 2020 19:25:15 -0700 Subject: [PATCH 023/195] Add Project Euler 65 Solution (#3035) * Add solution for Project Euler 65, * Add URL to problem 65 and don't pass in parameter to solution() * Remove solution() tests * Add tests for solution(), add fstring and positional arg for solution * Rename directory and problem number to 065 * Remove directory * Move up explanation to module code block * Move solution() below helper function, rename variables --- DIRECTORY.md | 2 + project_euler/problem_065/__init__.py | 0 project_euler/problem_065/sol1.py | 99 +++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 project_euler/problem_065/__init__.py create mode 100644 project_euler/problem_065/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 866e0d658bf6..f0f494e7a3b4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -671,6 +671,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_062/sol1.py) * Problem 063 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_063/sol1.py) + * Problem 065 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_065/sol1.py) * Problem 067 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_067/sol1.py) * Problem 069 diff --git a/project_euler/problem_065/__init__.py b/project_euler/problem_065/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_065/sol1.py b/project_euler/problem_065/sol1.py new file mode 100644 index 000000000000..229769a77d07 --- /dev/null +++ b/project_euler/problem_065/sol1.py @@ -0,0 +1,99 @@ +""" +Project Euler Problem 65: https://projecteuler.net/problem=65 + +The square root of 2 can be written as an infinite continued fraction. + +sqrt(2) = 1 + 1 / (2 + 1 / (2 + 1 / (2 + 1 / (2 + ...)))) + +The infinite continued fraction can be written, sqrt(2) = [1;(2)], (2) +indicates that 2 repeats ad infinitum. In a similar way, sqrt(23) = +[4;(1,3,1,8)]. + +It turns out that the sequence of partial values of continued +fractions for square roots provide the best rational approximations. +Let us consider the convergents for sqrt(2). + +1 + 1 / 2 = 3/2 +1 + 1 / (2 + 1 / 2) = 7/5 +1 + 1 / (2 + 1 / (2 + 1 / 2)) = 17/12 +1 + 1 / (2 + 1 / (2 + 1 / (2 + 1 / 2))) = 41/29 + +Hence the sequence of the first ten convergents for sqrt(2) are: +1, 3/2, 7/5, 17/12, 41/29, 99/70, 239/169, 577/408, 1393/985, 3363/2378, ... + +What is most surprising is that the important mathematical constant, +e = [2;1,2,1,1,4,1,1,6,1,...,1,2k,1,...]. + +The first ten terms in the sequence of convergents for e are: +2, 3, 8/3, 11/4, 19/7, 87/32, 106/39, 193/71, 1264/465, 1457/536, ... + +The sum of digits in the numerator of the 10th convergent is +1 + 4 + 5 + 7 = 17. + +Find the sum of the digits in the numerator of the 100th convergent +of the continued fraction for e. + +----- + +The solution mostly comes down to finding an equation that will generate +the numerator of the continued fraction. For the i-th numerator, the +pattern is: + +n_i = m_i * n_(i-1) + n_(i-2) + +for m_i = the i-th index of the continued fraction representation of e, +n_0 = 1, and n_1 = 2 as the first 2 numbers of the representation. + +For example: +n_9 = 6 * 193 + 106 = 1264 +1 + 2 + 6 + 4 = 13 + +n_10 = 1 * 193 + 1264 = 1457 +1 + 4 + 5 + 7 = 17 +""" + + +def sum_digits(num: int) -> int: + """ + Returns the sum of every digit in num. + + >>> sum_digits(1) + 1 + >>> sum_digits(12345) + 15 + >>> sum_digits(999001) + 28 + """ + digit_sum = 0 + while num > 0: + digit_sum += num % 10 + num //= 10 + return digit_sum + + +def solution(max: int = 100) -> int: + """ + Returns the sum of the digits in the numerator of the max-th convergent of + the continued fraction for e. + + >>> solution(9) + 13 + >>> solution(10) + 17 + >>> solution(50) + 91 + """ + pre_numerator = 1 + cur_numerator = 2 + + for i in range(2, max + 1): + temp = pre_numerator + e_cont = 2 * i // 3 if i % 3 == 0 else 1 + pre_numerator = cur_numerator + cur_numerator = e_cont * pre_numerator + temp + + return sum_digits(cur_numerator) + + +if __name__ == "__main__": + print(f"{solution() = }") From 231dae18a95d94760fa8f96773681bbb8945f6fa Mon Sep 17 00:00:00 2001 From: fpringle Date: Sat, 24 Oct 2020 04:42:15 +0200 Subject: [PATCH 024/195] Add solution for Project Euler problem 38. (#3115) * Added solution for Project Euler problem 38. Fixes: #2695 * Update docstring and 0-padding in directory name. Reference: #3256 * Renamed is_9_palindromic to is_9_pandigital. * Changed just-in-case return value for solution() to None. * Moved exmplanation to module-level docstring and deleted unnecessary import --- project_euler/problem_038/__init__.py | 0 project_euler/problem_038/sol1.py | 77 +++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 project_euler/problem_038/__init__.py create mode 100644 project_euler/problem_038/sol1.py diff --git a/project_euler/problem_038/__init__.py b/project_euler/problem_038/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_038/sol1.py b/project_euler/problem_038/sol1.py new file mode 100644 index 000000000000..6d54f6df7ff8 --- /dev/null +++ b/project_euler/problem_038/sol1.py @@ -0,0 +1,77 @@ +""" +Project Euler Problem 38: https://projecteuler.net/problem=38 + +Take the number 192 and multiply it by each of 1, 2, and 3: + +192 × 1 = 192 +192 × 2 = 384 +192 × 3 = 576 + +By concatenating each product we get the 1 to 9 pandigital, 192384576. We will call +192384576 the concatenated product of 192 and (1,2,3) + +The same can be achieved by starting with 9 and multiplying by 1, 2, 3, 4, and 5, +giving the pandigital, 918273645, which is the concatenated product of 9 and +(1,2,3,4,5). + +What is the largest 1 to 9 pandigital 9-digit number that can be formed as the +concatenated product of an integer with (1,2, ... , n) where n > 1? + +Solution: +Since n>1, the largest candidate for the solution will be a concactenation of +a 4-digit number and its double, a 5-digit number. +Let a be the 4-digit number. +a has 4 digits => 1000 <= a < 10000 +2a has 5 digits => 10000 <= 2a < 100000 +=> 5000 <= a < 10000 + +The concatenation of a with 2a = a * 10^5 + 2a +so our candidate for a given a is 100002 * a. +We iterate through the search space 5000 <= a < 10000 in reverse order, +calculating the candidates for each a and checking if they are 1-9 pandigital. + +In case there are no 4-digit numbers that satisfy this property, we check +the 3-digit numbers with a similar formula (the example a=192 gives a lower +bound on the length of a): +a has 3 digits, etc... +=> 100 <= a < 334, candidate = a * 10^6 + 2a * 10^3 + 3a + = 1002003 * a +""" + +from typing import Union + + +def is_9_pandigital(n: int) -> bool: + """ + Checks whether n is a 9-digit 1 to 9 pandigital number. + >>> is_9_pandigital(12345) + False + >>> is_9_pandigital(156284973) + True + >>> is_9_pandigital(1562849733) + False + """ + s = str(n) + return len(s) == 9 and set(s) == set("123456789") + + +def solution() -> Union[int, None]: + """ + Return the largest 1 to 9 pandigital 9-digital number that can be formed as the + concatenated product of an integer with (1,2,...,n) where n > 1. + """ + for base_num in range(9999, 4999, -1): + candidate = 100002 * base_num + if is_9_pandigital(candidate): + return candidate + + for base_num in range(333, 99, -1): + candidate = 1002003 * base_num + if is_9_pandigital(candidate): + return candidate + + return None + + +if __name__ == "__main__": + print(f"{solution() = }") From 803a85d42b5e47aa74da35cfbf16c9fbf8f0f1aa Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Sat, 24 Oct 2020 18:16:37 +0800 Subject: [PATCH 025/195] Update LinkedQueue (#3683) * update LinkedQueue * add type hint and rename --- data_structures/queue/linked_queue.py | 139 ++++++++++++++++++++------ 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/data_structures/queue/linked_queue.py b/data_structures/queue/linked_queue.py index 614c60cd1ae2..8526ad311ed0 100644 --- a/data_structures/queue/linked_queue.py +++ b/data_structures/queue/linked_queue.py @@ -1,18 +1,18 @@ -""" A Queue using a Linked List like structure """ -from typing import Any, Optional +""" A Queue using a linked list like structure """ +from typing import Any class Node: - def __init__(self, data: Any, next: Optional["Node"] = None): - self.data: Any = data - self.next: Optional["Node"] = next + def __init__(self, data: Any) -> None: + self.data = data + self.next = None + + def __str__(self) -> str: + return f"{self.data}" class LinkedQueue: """ - Linked List Queue implementing put (to end of queue), - get (from front of queue) and is_empty - >>> queue = LinkedQueue() >>> queue.is_empty() True @@ -35,40 +35,117 @@ class LinkedQueue: >>> queue.get() Traceback (most recent call last): ... - IndexError: get from empty queue + IndexError: dequeue from empty queue """ def __init__(self) -> None: - self.front: Optional[Node] = None - self.rear: Optional[Node] = None + self.front = self.rear = None + + def __iter__(self): + node = self.front + while node: + yield node.data + node = node.next + + def __len__(self) -> int: + """ + >>> queue = LinkedQueue() + >>> for i in range(1, 6): + ... queue.put(i) + >>> len(queue) + 5 + >>> for i in range(1, 6): + ... assert len(queue) == 6 - i + ... _ = queue.get() + >>> len(queue) + 0 + """ + return len(tuple(iter(self))) + + def __str__(self) -> str: + """ + >>> queue = LinkedQueue() + >>> for i in range(1, 4): + ... queue.put(i) + >>> queue.put("Python") + >>> queue.put(3.14) + >>> queue.put(True) + >>> str(queue) + '1 <- 2 <- 3 <- Python <- 3.14 <- True' + """ + return " <- ".join(str(item) for item in self) def is_empty(self) -> bool: - """ returns boolean describing if queue is empty """ - return self.front is None + """ + >>> queue = LinkedQueue() + >>> queue.is_empty() + True + >>> for i in range(1, 6): + ... queue.put(i) + >>> queue.is_empty() + False + """ + return len(self) == 0 - def put(self, item: Any) -> None: - """ append item to rear of queue """ - node: Node = Node(item) + def put(self, item) -> None: + """ + >>> queue = LinkedQueue() + >>> queue.get() + Traceback (most recent call last): + ... + IndexError: dequeue from empty queue + >>> for i in range(1, 6): + ... queue.put(i) + >>> str(queue) + '1 <- 2 <- 3 <- 4 <- 5' + """ + node = Node(item) if self.is_empty(): - # the queue contains just the single element - self.front = node - self.rear = node + self.front = self.rear = node else: - # not empty, so we add it to the rear of the queue assert isinstance(self.rear, Node) self.rear.next = node self.rear = node def get(self) -> Any: - """ returns and removes item at front of queue """ + """ + >>> queue = LinkedQueue() + >>> queue.get() + Traceback (most recent call last): + ... + IndexError: dequeue from empty queue + >>> queue = LinkedQueue() + >>> for i in range(1, 6): + ... queue.put(i) + >>> for i in range(1, 6): + ... assert queue.get() == i + >>> len(queue) + 0 + """ if self.is_empty(): - raise IndexError("get from empty queue") - else: - # "remove" element by having front point to the next one - assert isinstance(self.front, Node) - node: Node = self.front - self.front = node.next - if self.front is None: - self.rear = None - - return node.data + raise IndexError("dequeue from empty queue") + assert isinstance(self.front, Node) + node = self.front + self.front = self.front.next + if self.front is None: + self.rear = None + return node.data + + def clear(self) -> None: + """ + >>> queue = LinkedQueue() + >>> for i in range(1, 6): + ... queue.put(i) + >>> queue.clear() + >>> len(queue) + 0 + >>> str(queue) + '' + """ + self.front = self.rear = None + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 3ef1a034099017877d0f2d50ea63ae884292cea1 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 24 Oct 2020 19:07:33 +0530 Subject: [PATCH 026/195] Move validate_solutions and add durations flag to pytest.ini (#3704) * Move PE validate_solutions to scripts/ directory * Update pytest.ini file with durations settings * Remove codespell and autoblack workflow file * Dependent changes to test config files * Update pytest.ini --- .github/workflows/autoblack.yml | 25 ------------------- .github/workflows/codespell.yml | 17 ------------- .github/workflows/project_euler.yml | 7 +++--- .travis.yml | 2 +- pytest.ini | 1 + .../project_euler_answers.json | 0 .../validate_solutions.py | 4 +-- 7 files changed, 8 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/autoblack.yml delete mode 100644 .github/workflows/codespell.yml rename {project_euler => scripts}/project_euler_answers.json (100%) rename {project_euler => scripts}/validate_solutions.py (94%) diff --git a/.github/workflows/autoblack.yml b/.github/workflows/autoblack.yml deleted file mode 100644 index ce34170d44bb..000000000000 --- a/.github/workflows/autoblack.yml +++ /dev/null @@ -1,25 +0,0 @@ -# GitHub Action that uses Black to reformat Python code (if needed) when doing a git push. -# If all Python code in the repo is compliant with Black then this Action does nothing. -# Otherwise, Black is run and its changes are committed to the repo. -# https://github.com/cclauss/autoblack - -name: autoblack_push -on: [push] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 # Use v1, NOT v2 - - uses: actions/setup-python@v2 - - run: pip install black isort - - run: black --check . - - name: If needed, commit black changes to a new pull request - if: failure() - run: | - black . - isort --profile black . - git config --global user.name github-actions - git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY - git commit -am "fixup! Format Python code with psf/black push" - git push --force origin HEAD:$GITHUB_REF diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml deleted file mode 100644 index e336f697708c..000000000000 --- a/.github/workflows/codespell.yml +++ /dev/null @@ -1,17 +0,0 @@ -# GitHub Action to automate the identification of common misspellings in text files -# https://github.com/codespell-project/codespell -name: codespell -on: [push, pull_request] -jobs: - codespell: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - run: pip install codespell - - run: | - SKIP="./.*,./other/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" - codespell --ignore-words-list=ans,fo,followings,hist,iff,secant,som,tim --skip=$SKIP --quiet-level=2 - - name: Codespell comment - if: ${{ failure() }} - uses: plettich/python_codespell_action@master diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index 852b0adbcb56..e8b011af20a6 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -1,9 +1,10 @@ on: pull_request: - # only check if a file is changed within the project_euler directory + # only check if a file is changed within the project_euler directory and related files paths: - 'project_euler/**' - '.github/workflows/project_euler.yml' + - 'scripts/validate_solutions.py' name: 'Project Euler' @@ -17,7 +18,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade pytest pytest-cov - - run: pytest --doctest-modules --durations=10 --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ + - run: pytest --doctest-modules --cov-report=term-missing:skip-covered --cov=project_euler/ project_euler/ validate-solutions: runs-on: ubuntu-latest steps: @@ -27,4 +28,4 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade pytest - - run: pytest --durations=10 project_euler/validate_solutions.py + - run: pytest scripts/validate_solutions.py diff --git a/.travis.yml b/.travis.yml index 2a4a6392d4e7..c74669ebcb51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ jobs: - name: Build install: pip install pytest-cov -r requirements.txt script: - - pytest --doctest-modules --ignore=project_euler/ --durations=10 --cov-report=term-missing:skip-covered --cov=. . + - pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . after_success: - scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md notifications: diff --git a/pytest.ini b/pytest.ini index a26de5e638dc..488379278230 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,3 +2,4 @@ [pytest] markers = mat_ops: mark a test as utilizing matrix operations. +addopts = --durations=10 diff --git a/project_euler/project_euler_answers.json b/scripts/project_euler_answers.json similarity index 100% rename from project_euler/project_euler_answers.json rename to scripts/project_euler_answers.json diff --git a/project_euler/validate_solutions.py b/scripts/validate_solutions.py similarity index 94% rename from project_euler/validate_solutions.py rename to scripts/validate_solutions.py index 6cc1d6498e37..e1f68ff843bb 100755 --- a/project_euler/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -8,8 +8,8 @@ import pytest PROJECT_EULER_DIR_PATH = pathlib.Path.cwd().joinpath("project_euler") -PROJECT_EULER_ANSWERS_PATH = PROJECT_EULER_DIR_PATH.joinpath( - "project_euler_answers.json" +PROJECT_EULER_ANSWERS_PATH = pathlib.Path.cwd().joinpath( + "scripts", "project_euler_answers.json" ) with open(PROJECT_EULER_ANSWERS_PATH) as file_handle: From 7dc6dbf467b9caa9dfeb25a61bfc79b2c95b7e44 Mon Sep 17 00:00:00 2001 From: Nandiya Date: Sat, 24 Oct 2020 21:07:27 +0700 Subject: [PATCH 027/195] Forecast (#3219) * add forecasting code * add statsmodel * sort import * sort import fix * fixing black * sort requirement * optimize code * try with limited data * sort again * sort fix * sort fix * delete warning and black * add code for forecasting * use black * add more hints to describe * add doctest * finding whitespace * fixing doctest * delete * revert back * revert back * revert back again * revert back again * revert back again * try trimming whitespace * try adding doctypeand etc * fixing reviews * deleting all the space * fixing the build * delete x * add description for safety checker * deleting subscription integer * fix docthint * make def to use function parameters and return values * make def to use function parameters and return values * type hints on data safety checker * optimize code * Update run.py Co-authored-by: FVFYK3GEHV22 Co-authored-by: Christian Clauss --- machine_learning/forecasting/__init__.py | 0 machine_learning/forecasting/ex_data.csv | 114 +++++++++++++++++ machine_learning/forecasting/run.py | 156 +++++++++++++++++++++++ requirements.txt | 1 + 4 files changed, 271 insertions(+) create mode 100644 machine_learning/forecasting/__init__.py create mode 100644 machine_learning/forecasting/ex_data.csv create mode 100644 machine_learning/forecasting/run.py diff --git a/machine_learning/forecasting/__init__.py b/machine_learning/forecasting/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/machine_learning/forecasting/ex_data.csv b/machine_learning/forecasting/ex_data.csv new file mode 100644 index 000000000000..1c429e649755 --- /dev/null +++ b/machine_learning/forecasting/ex_data.csv @@ -0,0 +1,114 @@ +total_user,total_events,days +18231,0.0,1 +22621,1.0,2 +15675,0.0,3 +23583,1.0,4 +68351,5.0,5 +34338,3.0,6 +19238,0.0,0 +24192,0.0,1 +70349,0.0,2 +103510,0.0,3 +128355,1.0,4 +148484,6.0,5 +153489,3.0,6 +162667,1.0,0 +311430,3.0,1 +435663,7.0,2 +273526,0.0,3 +628588,2.0,4 +454989,13.0,5 +539040,3.0,6 +52974,1.0,0 +103451,2.0,1 +810020,5.0,2 +580982,3.0,3 +216515,0.0,4 +134694,10.0,5 +93563,1.0,6 +55432,1.0,0 +169634,1.0,1 +254908,4.0,2 +315285,3.0,3 +191764,0.0,4 +514284,7.0,5 +181214,4.0,6 +78459,2.0,0 +161620,3.0,1 +245610,4.0,2 +326722,5.0,3 +214578,0.0,4 +312365,5.0,5 +232454,4.0,6 +178368,1.0,0 +97152,1.0,1 +222813,4.0,2 +285852,4.0,3 +192149,1.0,4 +142241,1.0,5 +173011,2.0,6 +56488,3.0,0 +89572,2.0,1 +356082,2.0,2 +172799,0.0,3 +142300,1.0,4 +78432,2.0,5 +539023,9.0,6 +62389,1.0,0 +70247,1.0,1 +89229,0.0,2 +94583,1.0,3 +102455,0.0,4 +129270,0.0,5 +311409,1.0,6 +1837026,0.0,0 +361824,0.0,1 +111379,2.0,2 +76337,2.0,3 +96747,0.0,4 +92058,0.0,5 +81929,2.0,6 +143423,0.0,0 +82939,0.0,1 +74403,1.0,2 +68234,0.0,3 +94556,1.0,4 +80311,0.0,5 +75283,3.0,6 +77724,0.0,0 +49229,2.0,1 +65708,2.0,2 +273864,1.0,3 +1711281,0.0,4 +1900253,5.0,5 +343071,1.0,6 +1551326,0.0,0 +56636,1.0,1 +272782,2.0,2 +1785678,0.0,3 +241866,0.0,4 +461904,0.0,5 +2191901,2.0,6 +102925,0.0,0 +242778,1.0,1 +298608,0.0,2 +322458,10.0,3 +216027,9.0,4 +916052,12.0,5 +193278,12.0,6 +263207,8.0,0 +672948,10.0,1 +281909,1.0,2 +384562,1.0,3 +1027375,2.0,4 +828905,9.0,5 +624188,22.0,6 +392218,8.0,0 +292581,10.0,1 +299869,12.0,2 +769455,20.0,3 +316443,8.0,4 +1212864,24.0,5 +1397338,28.0,6 +223249,8.0,0 +191264,14.0,1 diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py new file mode 100644 index 000000000000..467371e8d2ff --- /dev/null +++ b/machine_learning/forecasting/run.py @@ -0,0 +1,156 @@ +""" +this is code for forecasting +but i modified it and used it for safety checker of data +for ex: you have a online shop and for some reason some data are +missing (the amount of data that u expected are not supposed to be) + then we can use it +*ps : 1. ofc we can use normal statistic method but in this case + the data is quite absurd and only a little^^ + 2. ofc u can use this and modified it for forecasting purpose + for the next 3 months sales or something, + u can just adjust it for ur own purpose +""" + +import numpy as np +import pandas as pd +from sklearn.preprocessing import Normalizer +from sklearn.svm import SVR +from statsmodels.tsa.statespace.sarimax import SARIMAX + + +def linear_regression_prediction( + train_dt: list, train_usr: list, train_mtch: list, test_dt: list, test_mtch: list +) -> float: + """ + First method: linear regression + input : training data (date, total_user, total_event) in list of float + output : list of total user prediction in float + >>> linear_regression_prediction([2,3,4,5], [5,3,4,6], [3,1,2,4], [2,1], [2,2]) + 5.000000000000003 + """ + x = [[1, item, train_mtch[i]] for i, item in enumerate(train_dt)] + x = np.array(x) + y = np.array(train_usr) + beta = np.dot(np.dot(np.linalg.inv(np.dot(x.transpose(), x)), x.transpose()), y) + return abs(beta[0] + test_dt[0] * beta[1] + test_mtch[0] + beta[2]) + + +def sarimax_predictor(train_user: list, train_match: list, test_match: list) -> float: + """ + second method: Sarimax + sarimax is a statistic method which using previous input + and learn its pattern to predict future data + input : training data (total_user, with exog data = total_event) in list of float + output : list of total user prediction in float + >>> sarimax_predictor([4,2,6,8], [3,1,2,4], [2]) + 6.6666671111109626 + """ + order = (1, 2, 1) + seasonal_order = (1, 1, 0, 7) + model = SARIMAX( + train_user, exog=train_match, order=order, seasonal_order=seasonal_order + ) + model_fit = model.fit(disp=False, maxiter=600, method="nm") + result = model_fit.predict(1, len(test_match), exog=[test_match]) + return result[0] + + +def support_vector_regressor(x_train: list, x_test: list, train_user: list) -> float: + """ + Third method: Support vector regressor + svr is quite the same with svm(support vector machine) + it uses the same principles as the SVM for classification, + with only a few minor differences and the only different is that + it suits better for regression purpose + input : training data (date, total_user, total_event) in list of float + where x = list of set (date and total event) + output : list of total user prediction in float + >>> support_vector_regressor([[5,2],[1,5],[6,2]], [[3,2]], [2,1,4]) + 1.634932078116079 + """ + regressor = SVR(kernel="rbf", C=1, gamma=0.1, epsilon=0.1) + regressor.fit(x_train, train_user) + y_pred = regressor.predict(x_test) + return y_pred[0] + + +def interquartile_range_checker(train_user: list) -> float: + """ + Optional method: interquatile range + input : list of total user in float + output : low limit of input in float + this method can be used to check whether some data is outlier or not + >>> interquartile_range_checker([1,2,3,4,5,6,7,8,9,10]) + 2.8 + """ + train_user.sort() + q1 = np.percentile(train_user, 25) + q3 = np.percentile(train_user, 75) + iqr = q3 - q1 + low_lim = q1 - (iqr * 0.1) + return low_lim + + +def data_safety_checker(list_vote: list, actual_result: float) -> None: + """ + Used to review all the votes (list result prediction) + and compare it to the actual result. + input : list of predictions + output : print whether it's safe or not + >>> data_safety_checker([2,3,4],5.0) + Today's data is not safe. + """ + safe = 0 + not_safe = 0 + for i in list_vote: + if i > actual_result: + safe = not_safe + 1 + else: + if abs(abs(i) - abs(actual_result)) <= 0.1: + safe = safe + 1 + else: + not_safe = not_safe + 1 + print(f"Today's data is {'not ' if safe <= not_safe else ''}safe.") + + +# data_input_df = pd.read_csv("ex_data.csv", header=None) +data_input = [[18231, 0.0, 1], [22621, 1.0, 2], [15675, 0.0, 3], [23583, 1.0, 4]] +data_input_df = pd.DataFrame(data_input, columns=["total_user", "total_even", "days"]) + +""" +data column = total user in a day, how much online event held in one day, +what day is that(sunday-saturday) +""" + +# start normalization +normalize_df = Normalizer().fit_transform(data_input_df.values) +# split data +total_date = normalize_df[:, 2].tolist() +total_user = normalize_df[:, 0].tolist() +total_match = normalize_df[:, 1].tolist() + +# for svr (input variable = total date and total match) +x = normalize_df[:, [1, 2]].tolist() +x_train = x[: len(x) - 1] +x_test = x[len(x) - 1 :] + +# for linear reression & sarimax +trn_date = total_date[: len(total_date) - 1] +trn_user = total_user[: len(total_user) - 1] +trn_match = total_match[: len(total_match) - 1] + +tst_date = total_date[len(total_date) - 1 :] +tst_user = total_user[len(total_user) - 1 :] +tst_match = total_match[len(total_match) - 1 :] + + +# voting system with forecasting +res_vote = [] +res_vote.append( + linear_regression_prediction(trn_date, trn_user, trn_match, tst_date, tst_match) +) +res_vote.append(sarimax_predictor(trn_user, trn_match, tst_match)) +res_vote.append(support_vector_regressor(x_train, x_test, trn_user)) + +# check the safety of todays'data^^ +data_safety_checker(res_vote, tst_user) diff --git a/requirements.txt b/requirements.txt index 67d9bbbd8448..8bbb8d524ed4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ qiskit requests scikit-fuzzy sklearn +statsmodels sympy tensorflow xgboost From 75df93f4415bd7c0c5b7e029d921ac7e9ddec49a Mon Sep 17 00:00:00 2001 From: Sam Holst Date: Sat, 24 Oct 2020 10:19:59 -0700 Subject: [PATCH 028/195] removed extra line to match rest of file (#3528) --- scripts/validate_filenames.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/validate_filenames.py b/scripts/validate_filenames.py index e75bf6c18b07..419295fe679d 100755 --- a/scripts/validate_filenames.py +++ b/scripts/validate_filenames.py @@ -9,7 +9,6 @@ filepaths = list(good_file_paths()) assert filepaths, "good_file_paths() failed!" - upper_files = [file for file in filepaths if file != file.lower()] if upper_files: print(f"{len(upper_files)} files contain uppercase characters:") From c2118013eb80105cb207dbc4e533d846c538b4e0 Mon Sep 17 00:00:00 2001 From: Phil Bazun Date: Sat, 24 Oct 2020 23:07:04 +0200 Subject: [PATCH 029/195] Add 0-1-bfs. (#3285) * Add 0-1-bfs. * fixup! Add 0-1-bfs. * fixup! Add 0-1-bfs. * Check edge weights. * Check edge vertecies. --- graphs/bfs_zero_one_shortest_path.py | 138 +++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 graphs/bfs_zero_one_shortest_path.py diff --git a/graphs/bfs_zero_one_shortest_path.py b/graphs/bfs_zero_one_shortest_path.py new file mode 100644 index 000000000000..a725fae7e48f --- /dev/null +++ b/graphs/bfs_zero_one_shortest_path.py @@ -0,0 +1,138 @@ +from collections import deque +from dataclasses import dataclass +from typing import Iterator, List + +""" +Finding the shortest path in 0-1-graph in O(E + V) which is faster than dijkstra. +0-1-graph is the weighted graph with the weights equal to 0 or 1. +Link: https://codeforces.com/blog/entry/22276 +""" + + +@dataclass +class Edge: + """Weighted directed graph edge.""" + + destination_vertex: int + weight: int + + +class AdjacencyList: + """Graph adjacency list.""" + + def __init__(self, size: int): + self._graph: List[List[Edge]] = [[] for _ in range(size)] + self._size = size + + def __getitem__(self, vertex: int) -> Iterator[Edge]: + """Get all the vertices adjacent to the given one.""" + return iter(self._graph[vertex]) + + @property + def size(self): + return self._size + + def add_edge(self, from_vertex: int, to_vertex: int, weight: int): + """ + >>> g = AdjacencyList(2) + >>> g.add_edge(0, 1, 0) + >>> g.add_edge(1, 0, 1) + >>> list(g[0]) + [Edge(destination_vertex=1, weight=0)] + >>> list(g[1]) + [Edge(destination_vertex=0, weight=1)] + >>> g.add_edge(0, 1, 2) + Traceback (most recent call last): + ... + ValueError: Edge weight must be either 0 or 1. + >>> g.add_edge(0, 2, 1) + Traceback (most recent call last): + ... + ValueError: Vertex indexes must be in [0; size). + """ + if weight not in (0, 1): + raise ValueError("Edge weight must be either 0 or 1.") + + if to_vertex < 0 or to_vertex >= self.size: + raise ValueError("Vertex indexes must be in [0; size).") + + self._graph[from_vertex].append(Edge(to_vertex, weight)) + + def get_shortest_path(self, start_vertex: int, finish_vertex: int) -> int: + """ + Return the shortest distance from start_vertex to finish_vertex in 0-1-graph. + 1 1 1 + 0--------->3 6--------7>------->8 + | ^ ^ ^ |1 + | | | |0 v + 0| |0 1| 9-------->10 + | | | ^ 1 + v | | |0 + 1--------->2<-------4------->5 + 0 1 1 + >>> g = AdjacencyList(11) + >>> g.add_edge(0, 1, 0) + >>> g.add_edge(0, 3, 1) + >>> g.add_edge(1, 2, 0) + >>> g.add_edge(2, 3, 0) + >>> g.add_edge(4, 2, 1) + >>> g.add_edge(4, 5, 1) + >>> g.add_edge(4, 6, 1) + >>> g.add_edge(5, 9, 0) + >>> g.add_edge(6, 7, 1) + >>> g.add_edge(7, 8, 1) + >>> g.add_edge(8, 10, 1) + >>> g.add_edge(9, 7, 0) + >>> g.add_edge(9, 10, 1) + >>> g.add_edge(1, 2, 2) + Traceback (most recent call last): + ... + ValueError: Edge weight must be either 0 or 1. + >>> g.get_shortest_path(0, 3) + 0 + >>> g.get_shortest_path(0, 4) + Traceback (most recent call last): + ... + ValueError: No path from start_vertex to finish_vertex. + >>> g.get_shortest_path(4, 10) + 2 + >>> g.get_shortest_path(4, 8) + 2 + >>> g.get_shortest_path(0, 1) + 0 + >>> g.get_shortest_path(1, 0) + Traceback (most recent call last): + ... + ValueError: No path from start_vertex to finish_vertex. + """ + queue = deque([start_vertex]) + distances = [None for i in range(self.size)] + distances[start_vertex] = 0 + + while queue: + current_vertex = queue.popleft() + current_distance = distances[current_vertex] + + for edge in self[current_vertex]: + new_distance = current_distance + edge.weight + if ( + distances[edge.destination_vertex] is not None + and new_distance >= distances[edge.destination_vertex] + ): + continue + distances[edge.destination_vertex] = new_distance + if edge.weight == 0: + queue.appendleft(edge.destination_vertex) + else: + queue.append(edge.destination_vertex) + + if distances[finish_vertex] is None: + raise ValueError("No path from start_vertex to finish_vertex.") + + return distances[finish_vertex] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4e8aabd51388b1648106b2c8136131a1dca3c0a9 Mon Sep 17 00:00:00 2001 From: Michael D Date: Sun, 25 Oct 2020 04:23:16 +0100 Subject: [PATCH 030/195] Fix style of the first ten solutions for Project Euler (#3242) * Fix style of the first ten solutions for Project Euler - Unify the header docstring, and add reference URLs to wikipedia or similar - Fix docstrings to be properly multilined - Add newlines where appropriate - Add doctests where they were missing - Remove doctests that test for the correct solution - fix obvious spelling or grammar mistakes in comments and exception messages - Fix line endings to be UNIX. This makes two of the files seem to have changed completely - no functional changes in any of the solutions were done (except for the spelling fixes mentioned above) * Fix docstrings and main function as per Style Guide --- project_euler/problem_001/sol1.py | 13 ++-- project_euler/problem_001/sol2.py | 13 ++-- project_euler/problem_001/sol3.py | 10 +++- project_euler/problem_001/sol4.py | 99 ++++++++++++++++--------------- project_euler/problem_001/sol5.py | 15 +++-- project_euler/problem_001/sol6.py | 13 ++-- project_euler/problem_001/sol7.py | 13 ++-- project_euler/problem_002/sol1.py | 25 +++++--- project_euler/problem_002/sol2.py | 85 ++++++++++++++------------ project_euler/problem_002/sol3.py | 21 ++++--- project_euler/problem_002/sol4.py | 37 +++++++----- project_euler/problem_002/sol5.py | 24 +++++--- project_euler/problem_003/sol1.py | 44 ++++++++------ project_euler/problem_003/sol2.py | 32 ++++++---- project_euler/problem_003/sol3.py | 32 ++++++---- project_euler/problem_004/sol1.py | 26 ++++---- project_euler/problem_004/sol2.py | 21 ++++--- project_euler/problem_005/sol1.py | 36 ++++++----- project_euler/problem_005/sol2.py | 56 +++++++++++++---- project_euler/problem_006/sol1.py | 27 ++++----- project_euler/problem_006/sol2.py | 27 ++++----- project_euler/problem_006/sol3.py | 27 ++++----- project_euler/problem_006/sol4.py | 27 ++++----- project_euler/problem_007/sol1.py | 35 ++++++++--- project_euler/problem_007/sol2.py | 46 ++++++++------ project_euler/problem_007/sol3.py | 35 ++++++++--- project_euler/problem_008/sol1.py | 59 ++++++++++-------- project_euler/problem_008/sol2.py | 59 ++++++++++-------- project_euler/problem_008/sol3.py | 57 +++++++++--------- project_euler/problem_009/sol1.py | 36 +++++++---- project_euler/problem_009/sol2.py | 34 +++++++---- project_euler/problem_009/sol3.py | 21 ++++--- project_euler/problem_010/sol1.py | 23 ++++--- project_euler/problem_010/sol2.py | 26 +++++--- project_euler/problem_010/sol3.py | 30 ++++++---- 35 files changed, 716 insertions(+), 468 deletions(-) diff --git a/project_euler/problem_001/sol1.py b/project_euler/problem_001/sol1.py index 385bbbbf43b3..85ad32294c9b 100644 --- a/project_euler/problem_001/sol1.py +++ b/project_euler/problem_001/sol1.py @@ -1,13 +1,18 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. + """ + Returns the sum of all the multiples of 3 or 5 below n. >>> solution(3) 0 @@ -25,4 +30,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol2.py b/project_euler/problem_001/sol2.py index f08f548cb752..7093d3513378 100644 --- a/project_euler/problem_001/sol2.py +++ b/project_euler/problem_001/sol2.py @@ -1,13 +1,18 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. + """ + Returns the sum of all the multiples of 3 or 5 below n. >>> solution(3) 0 @@ -30,4 +35,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol3.py b/project_euler/problem_001/sol3.py index 67cb83faf238..8267fec84155 100644 --- a/project_euler/problem_001/sol3.py +++ b/project_euler/problem_001/sol3.py @@ -1,8 +1,12 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ @@ -57,4 +61,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol4.py b/project_euler/problem_001/sol4.py index 77f323695898..a0643c05b34f 100644 --- a/project_euler/problem_001/sol4.py +++ b/project_euler/problem_001/sol4.py @@ -1,47 +1,52 @@ -""" -Problem Statement: -If we list all the natural numbers below 10 that are multiples of 3 or 5, -we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. -""" - - -def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. - - >>> solution(3) - 0 - >>> solution(4) - 3 - >>> solution(10) - 23 - >>> solution(600) - 83700 - """ - - xmulti = [] - zmulti = [] - z = 3 - x = 5 - temp = 1 - while True: - result = z * temp - if result < n: - zmulti.append(result) - temp += 1 - else: - temp = 1 - break - while True: - result = x * temp - if result < n: - xmulti.append(result) - temp += 1 - else: - break - collection = list(set(xmulti + zmulti)) - return sum(collection) - - -if __name__ == "__main__": - print(solution(int(input().strip()))) +""" +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + +If we list all the natural numbers below 10 that are multiples of 3 or 5, +we get 3, 5, 6 and 9. The sum of these multiples is 23. + +Find the sum of all the multiples of 3 or 5 below 1000. +""" + + +def solution(n: int = 1000) -> int: + """ + Returns the sum of all the multiples of 3 or 5 below n. + + >>> solution(3) + 0 + >>> solution(4) + 3 + >>> solution(10) + 23 + >>> solution(600) + 83700 + """ + + xmulti = [] + zmulti = [] + z = 3 + x = 5 + temp = 1 + while True: + result = z * temp + if result < n: + zmulti.append(result) + temp += 1 + else: + temp = 1 + break + while True: + result = x * temp + if result < n: + xmulti.append(result) + temp += 1 + else: + break + collection = list(set(xmulti + zmulti)) + return sum(collection) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol5.py b/project_euler/problem_001/sol5.py index 256516802ca0..7f0b0bd1bc7c 100644 --- a/project_euler/problem_001/sol5.py +++ b/project_euler/problem_001/sol5.py @@ -1,14 +1,19 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. - A straightforward pythonic solution using list comprehension. + """ + Returns the sum of all the multiples of 3 or 5 below n. + A straightforward pythonic solution using list comprehension. >>> solution(3) 0 @@ -24,4 +29,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol6.py b/project_euler/problem_001/sol6.py index 5f60512a73fb..8ddce18ced04 100644 --- a/project_euler/problem_001/sol6.py +++ b/project_euler/problem_001/sol6.py @@ -1,13 +1,18 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. + """ + Returns the sum of all the multiples of 3 or 5 below n. >>> solution(3) 0 @@ -31,4 +36,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol7.py b/project_euler/problem_001/sol7.py index 5761c00f2996..8f5d1977fdde 100644 --- a/project_euler/problem_001/sol7.py +++ b/project_euler/problem_001/sol7.py @@ -1,13 +1,18 @@ """ -Problem Statement: +Project Euler Problem 1: https://projecteuler.net/problem=1 + +Multiples of 3 and 5 + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -Find the sum of all the multiples of 3 or 5 below N. + +Find the sum of all the multiples of 3 or 5 below 1000. """ def solution(n: int = 1000) -> int: - """Returns the sum of all the multiples of 3 or 5 below n. + """ + Returns the sum of all the multiples of 3 or 5 below n. >>> solution(3) 0 @@ -29,4 +34,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol1.py b/project_euler/problem_002/sol1.py index 2acc93b0affc..539f68fb6bc1 100644 --- a/project_euler/problem_002/sol1.py +++ b/project_euler/problem_002/sol1.py @@ -1,19 +1,25 @@ """ -Problem: -Each new term in the Fibonacci sequence is generated by adding the previous two -terms. By starting with 1 and 2, the first 10 terms will be: +Project Euler Problem 2: https://projecteuler.net/problem=2 - 1,2,3,5,8,13,21,34,55,89,.. +Even Fibonacci Numbers + +Each new term in the Fibonacci sequence is generated by adding the previous +two terms. By starting with 1 and 2, the first 10 terms will be: + +1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... By considering the terms in the Fibonacci sequence whose values do not exceed -n, find the sum of the even-valued terms. e.g. for n=10, we have {2,8}, sum is -10. +four million, find the sum of the even-valued terms. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number """ def solution(n: int = 4000000) -> int: - """Returns the sum of all fibonacci sequence even elements that are lower - or equals to n. + """ + Returns the sum of all even fibonacci sequence elements that are lower + or equal to n. >>> solution(10) 10 @@ -26,6 +32,7 @@ def solution(n: int = 4000000) -> int: >>> solution(34) 44 """ + i = 1 j = 2 total = 0 @@ -38,4 +45,4 @@ def solution(n: int = 4000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol2.py b/project_euler/problem_002/sol2.py index 01fc552b9b21..9033d0a69bcf 100644 --- a/project_euler/problem_002/sol2.py +++ b/project_euler/problem_002/sol2.py @@ -1,39 +1,46 @@ -""" -Problem: -Each new term in the Fibonacci sequence is generated by adding the previous two -terms. By starting with 1 and 2, the first 10 terms will be: - - 1,2,3,5,8,13,21,34,55,89,.. - -By considering the terms in the Fibonacci sequence whose values do not exceed -n, find the sum of the even-valued terms. e.g. for n=10, we have {2,8}, sum is -10. -""" - - -def solution(n: int = 4000000) -> int: - """Returns the sum of all fibonacci sequence even elements that are lower - or equals to n. - - >>> solution(10) - 10 - >>> solution(15) - 10 - >>> solution(2) - 2 - >>> solution(1) - 0 - >>> solution(34) - 44 - """ - even_fibs = [] - a, b = 0, 1 - while b <= n: - if b % 2 == 0: - even_fibs.append(b) - a, b = b, a + b - return sum(even_fibs) - - -if __name__ == "__main__": - print(solution(int(input().strip()))) +""" +Project Euler Problem 2: https://projecteuler.net/problem=2 + +Even Fibonacci Numbers + +Each new term in the Fibonacci sequence is generated by adding the previous +two terms. By starting with 1 and 2, the first 10 terms will be: + +1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... + +By considering the terms in the Fibonacci sequence whose values do not exceed +four million, find the sum of the even-valued terms. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number +""" + + +def solution(n: int = 4000000) -> int: + """ + Returns the sum of all even fibonacci sequence elements that are lower + or equal to n. + + >>> solution(10) + 10 + >>> solution(15) + 10 + >>> solution(2) + 2 + >>> solution(1) + 0 + >>> solution(34) + 44 + """ + + even_fibs = [] + a, b = 0, 1 + while b <= n: + if b % 2 == 0: + even_fibs.append(b) + a, b = b, a + b + return sum(even_fibs) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol3.py b/project_euler/problem_002/sol3.py index 53d8ca6f1b68..3ae175a99815 100644 --- a/project_euler/problem_002/sol3.py +++ b/project_euler/problem_002/sol3.py @@ -1,19 +1,25 @@ """ -Problem: +Project Euler Problem 2: https://projecteuler.net/problem=2 + +Even Fibonacci Numbers + Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be: - 1,2,3,5,8,13,21,34,55,89,.. +1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... By considering the terms in the Fibonacci sequence whose values do not exceed -n, find the sum of the even-valued terms. e.g. for n=10, we have {2,8}, sum is -10. +four million, find the sum of the even-valued terms. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number """ def solution(n: int = 4000000) -> int: - """Returns the sum of all fibonacci sequence even elements that are lower - or equals to n. + """ + Returns the sum of all even fibonacci sequence elements that are lower + or equal to n. >>> solution(10) 10 @@ -26,6 +32,7 @@ def solution(n: int = 4000000) -> int: >>> solution(34) 44 """ + if n <= 1: return 0 a = 0 @@ -38,4 +45,4 @@ def solution(n: int = 4000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py index a87410b7006d..70b7d6a80a1d 100644 --- a/project_euler/problem_002/sol4.py +++ b/project_euler/problem_002/sol4.py @@ -1,21 +1,27 @@ """ -Problem: -Each new term in the Fibonacci sequence is generated by adding the previous two -terms. By starting with 1 and 2, the first 10 terms will be: +Project Euler Problem 2: https://projecteuler.net/problem=2 - 1,2,3,5,8,13,21,34,55,89,.. +Even Fibonacci Numbers + +Each new term in the Fibonacci sequence is generated by adding the previous +two terms. By starting with 1 and 2, the first 10 terms will be: + +1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... By considering the terms in the Fibonacci sequence whose values do not exceed -n, find the sum of the even-valued terms. e.g. for n=10, we have {2,8}, sum is -10. +four million, find the sum of the even-valued terms. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number """ import math from decimal import Decimal, getcontext def solution(n: int = 4000000) -> int: - """Returns the sum of all fibonacci sequence even elements that are lower - or equals to n. + """ + Returns the sum of all even fibonacci sequence elements that are lower + or equal to n. >>> solution(10) 10 @@ -32,26 +38,27 @@ def solution(n: int = 4000000) -> int: >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. """ + try: n = int(n) except (TypeError, ValueError): - raise TypeError("Parameter n must be int or passive of cast to int.") + raise TypeError("Parameter n must be int or castable to int.") if n <= 0: - raise ValueError("Parameter n must be greater or equal to one.") + raise ValueError("Parameter n must be greater than or equal to one.") getcontext().prec = 100 phi = (Decimal(5) ** Decimal(0.5) + 1) / Decimal(2) @@ -62,4 +69,4 @@ def solution(n: int = 4000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol5.py b/project_euler/problem_002/sol5.py index dcf6eae85891..390fd19ef638 100644 --- a/project_euler/problem_002/sol5.py +++ b/project_euler/problem_002/sol5.py @@ -1,19 +1,25 @@ """ -Problem: -Each new term in the Fibonacci sequence is generated by adding the previous two -terms. By starting with 1 and 2, the first 10 terms will be: +Project Euler Problem 2: https://projecteuler.net/problem=2 - 1,2,3,5,8,13,21,34,55,89,.. +Even Fibonacci Numbers + +Each new term in the Fibonacci sequence is generated by adding the previous +two terms. By starting with 1 and 2, the first 10 terms will be: + +1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... By considering the terms in the Fibonacci sequence whose values do not exceed -n, find the sum of the even-valued terms. e.g. for n=10, we have {2,8}, sum is -10. +four million, find the sum of the even-valued terms. + +References: + - https://en.wikipedia.org/wiki/Fibonacci_number """ def solution(n: int = 4000000) -> int: - """Returns the sum of all fibonacci sequence even elements that are lower - or equals to n. + """ + Returns the sum of all even fibonacci sequence elements that are lower + or equal to n. >>> solution(10) 10 @@ -43,4 +49,4 @@ def solution(n: int = 4000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_003/sol1.py b/project_euler/problem_003/sol1.py index 22efeb2c4e90..3441dbf9e0b3 100644 --- a/project_euler/problem_003/sol1.py +++ b/project_euler/problem_003/sol1.py @@ -1,16 +1,22 @@ """ -Problem: -The prime factors of 13195 are 5,7,13 and 29. What is the largest prime factor -of a given number N? +Project Euler Problem 3: https://projecteuler.net/problem=3 -e.g. for 10, largest prime factor = 5. For 17, largest prime factor = 17. -""" +Largest prime factor + +The prime factors of 13195 are 5, 7, 13 and 29. + +What is the largest prime factor of the number 600851475143? +References: + - https://en.wikipedia.org/wiki/Prime_number#Unique_factorization +""" import math def isprime(num: int) -> bool: - """Returns boolean representing primality of given number num. + """ + Returns boolean representing primality of given number num. + >>> isprime(2) True >>> isprime(3) @@ -22,14 +28,15 @@ def isprime(num: int) -> bool: >>> isprime(0) Traceback (most recent call last): ... - ValueError: Parameter num must be greater or equal to two. + ValueError: Parameter num must be greater than or equal to two. >>> isprime(1) Traceback (most recent call last): ... - ValueError: Parameter num must be greater or equal to two. + ValueError: Parameter num must be greater than or equal to two. """ + if num <= 1: - raise ValueError("Parameter num must be greater or equal to two.") + raise ValueError("Parameter num must be greater than or equal to two.") if num == 2: return True elif num % 2 == 0: @@ -41,7 +48,9 @@ def isprime(num: int) -> bool: def solution(n: int = 600851475143) -> int: - """Returns the largest prime factor of a given number n. + """ + Returns the largest prime factor of a given number n. + >>> solution(13195) 29 >>> solution(10) @@ -53,26 +62,27 @@ def solution(n: int = 600851475143) -> int: >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. """ + try: n = int(n) except (TypeError, ValueError): - raise TypeError("Parameter n must be int or passive of cast to int.") + raise TypeError("Parameter n must be int or castable to int.") if n <= 0: - raise ValueError("Parameter n must be greater or equal to one.") + raise ValueError("Parameter n must be greater than or equal to one.") max_number = 0 if isprime(n): return n @@ -91,4 +101,4 @@ def solution(n: int = 600851475143) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_003/sol2.py b/project_euler/problem_003/sol2.py index f28232109a84..0af0daceed06 100644 --- a/project_euler/problem_003/sol2.py +++ b/project_euler/problem_003/sol2.py @@ -1,14 +1,21 @@ """ -Problem: -The prime factors of 13195 are 5,7,13 and 29. What is the largest prime factor -of a given number N? +Project Euler Problem 3: https://projecteuler.net/problem=3 -e.g. for 10, largest prime factor = 5. For 17, largest prime factor = 17. +Largest prime factor + +The prime factors of 13195 are 5, 7, 13 and 29. + +What is the largest prime factor of the number 600851475143? + +References: + - https://en.wikipedia.org/wiki/Prime_number#Unique_factorization """ def solution(n: int = 600851475143) -> int: - """Returns the largest prime factor of a given number n. + """ + Returns the largest prime factor of a given number n. + >>> solution(13195) 29 >>> solution(10) @@ -20,26 +27,27 @@ def solution(n: int = 600851475143) -> int: >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. """ + try: n = int(n) except (TypeError, ValueError): - raise TypeError("Parameter n must be int or passive of cast to int.") + raise TypeError("Parameter n must be int or castable to int.") if n <= 0: - raise ValueError("Parameter n must be greater or equal to one.") + raise ValueError("Parameter n must be greater than or equal to one.") prime = 1 i = 2 while i * i <= n: @@ -53,4 +61,4 @@ def solution(n: int = 600851475143) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_003/sol3.py b/project_euler/problem_003/sol3.py index 676717cceca8..bc6f1d2f61ca 100644 --- a/project_euler/problem_003/sol3.py +++ b/project_euler/problem_003/sol3.py @@ -1,14 +1,21 @@ """ -Problem: -The prime factors of 13195 are 5,7,13 and 29. What is the largest prime factor -of a given number N? +Project Euler Problem 3: https://projecteuler.net/problem=3 -e.g. for 10, largest prime factor = 5. For 17, largest prime factor = 17. +Largest prime factor + +The prime factors of 13195 are 5, 7, 13 and 29. + +What is the largest prime factor of the number 600851475143? + +References: + - https://en.wikipedia.org/wiki/Prime_number#Unique_factorization """ def solution(n: int = 600851475143) -> int: - """Returns the largest prime factor of a given number n. + """ + Returns the largest prime factor of a given number n. + >>> solution(13195) 29 >>> solution(10) @@ -20,26 +27,27 @@ def solution(n: int = 600851475143) -> int: >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. """ + try: n = int(n) except (TypeError, ValueError): - raise TypeError("Parameter n must be int or passive of cast to int.") + raise TypeError("Parameter n must be int or castable to int.") if n <= 0: - raise ValueError("Parameter n must be greater or equal to one.") + raise ValueError("Parameter n must be greater than or equal to one.") i = 2 ans = 0 if n == 2: @@ -55,4 +63,4 @@ def solution(n: int = 600851475143) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_004/sol1.py b/project_euler/problem_004/sol1.py index 42f56f3ef389..db6133a1a1d2 100644 --- a/project_euler/problem_004/sol1.py +++ b/project_euler/problem_004/sol1.py @@ -1,15 +1,21 @@ """ -Problem: -A palindromic number reads the same both ways. The largest palindrome made from -the product of two 2-digit numbers is 9009 = 91 x 99. +Project Euler Problem 4: https://projecteuler.net/problem=4 -Find the largest palindrome made from the product of two 3-digit numbers which -is less than N. +Largest palindrome product + +A palindromic number reads the same both ways. The largest palindrome made +from the product of two 2-digit numbers is 9009 = 91 × 99. + +Find the largest palindrome made from the product of two 3-digit numbers. + +References: + - https://en.wikipedia.org/wiki/Palindromic_number """ def solution(n: int = 998001) -> int: - """Returns the largest palindrome made from the product of two 3-digit + """ + Returns the largest palindrome made from the product of two 3-digit numbers which is less than n. >>> solution(20000) @@ -23,10 +29,10 @@ def solution(n: int = 998001) -> int: ... ValueError: That number is larger than our acceptable range. """ + # fetches the next number for number in range(n - 1, 9999, -1): - # converts number into string. str_number = str(number) # checks whether 'str_number' is a palindrome. @@ -44,8 +50,4 @@ def solution(n: int = 998001) -> int: if __name__ == "__main__": - import doctest - - doctest.testmod() - - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_004/sol2.py b/project_euler/problem_004/sol2.py index 8ee082ad2f6a..abc880966d58 100644 --- a/project_euler/problem_004/sol2.py +++ b/project_euler/problem_004/sol2.py @@ -1,15 +1,21 @@ """ -Problem: -A palindromic number reads the same both ways. The largest palindrome made from -the product of two 2-digit numbers is 9009 = 91 x 99. +Project Euler Problem 4: https://projecteuler.net/problem=4 -Find the largest palindrome made from the product of two 3-digit numbers which -is less than N. +Largest palindrome product + +A palindromic number reads the same both ways. The largest palindrome made +from the product of two 2-digit numbers is 9009 = 91 × 99. + +Find the largest palindrome made from the product of two 3-digit numbers. + +References: + - https://en.wikipedia.org/wiki/Palindromic_number """ def solution(n: int = 998001) -> int: - """Returns the largest palindrome made from the product of two 3-digit + """ + Returns the largest palindrome made from the product of two 3-digit numbers which is less than n. >>> solution(20000) @@ -19,6 +25,7 @@ def solution(n: int = 998001) -> int: >>> solution(40000) 39893 """ + answer = 0 for i in range(999, 99, -1): # 3 digit numbers range from 999 down to 100 for j in range(999, 99, -1): @@ -29,4 +36,4 @@ def solution(n: int = 998001) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_005/sol1.py b/project_euler/problem_005/sol1.py index a347d6564fa7..f272c102d2bb 100644 --- a/project_euler/problem_005/sol1.py +++ b/project_euler/problem_005/sol1.py @@ -1,23 +1,28 @@ """ -Problem: -2520 is the smallest number that can be divided by each of the numbers from 1 -to 10 without any remainder. +Project Euler Problem 5: https://projecteuler.net/problem=5 -What is the smallest positive number that is evenly divisible(divisible with no -remainder) by all of the numbers from 1 to N? +Smallest multiple + +2520 is the smallest number that can be divided by each of the numbers +from 1 to 10 without any remainder. + +What is the smallest positive number that is _evenly divisible_ by all +of the numbers from 1 to 20? + +References: + - https://en.wiktionary.org/wiki/evenly_divisible """ def solution(n: int = 20) -> int: - """Returns the smallest positive number that is evenly divisible(divisible + """ + Returns the smallest positive number that is evenly divisible (divisible with no remainder) by all of the numbers from 1 to n. >>> solution(10) 2520 >>> solution(15) 360360 - >>> solution(20) - 232792560 >>> solution(22) 232792560 >>> solution(3.4) @@ -25,26 +30,27 @@ def solution(n: int = 20) -> int: >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter n must be greater or equal to one. + ValueError: Parameter n must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter n must be int or passive of cast to int. + TypeError: Parameter n must be int or castable to int. """ + try: n = int(n) except (TypeError, ValueError): - raise TypeError("Parameter n must be int or passive of cast to int.") + raise TypeError("Parameter n must be int or castable to int.") if n <= 0: - raise ValueError("Parameter n must be greater or equal to one.") + raise ValueError("Parameter n must be greater than or equal to one.") i = 0 while 1: i += n * (n - 1) @@ -60,4 +66,4 @@ def solution(n: int = 20) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_005/sol2.py b/project_euler/problem_005/sol2.py index 57b4cc823d82..c88044487d20 100644 --- a/project_euler/problem_005/sol2.py +++ b/project_euler/problem_005/sol2.py @@ -1,38 +1,70 @@ """ -Problem: -2520 is the smallest number that can be divided by each of the numbers from 1 -to 10 without any remainder. +Project Euler Problem 5: https://projecteuler.net/problem=5 -What is the smallest positive number that is evenly divisible(divisible with no -remainder) by all of the numbers from 1 to N? +Smallest multiple + +2520 is the smallest number that can be divided by each of the numbers +from 1 to 10 without any remainder. + +What is the smallest positive number that is _evenly divisible_ by all +of the numbers from 1 to 20? + +References: + - https://en.wiktionary.org/wiki/evenly_divisible + - https://en.wikipedia.org/wiki/Euclidean_algorithm + - https://en.wikipedia.org/wiki/Least_common_multiple """ -""" Euclidean GCD Algorithm """ def gcd(x: int, y: int) -> int: - return x if y == 0 else gcd(y, x % y) + """ + Euclidean GCD algorithm (Greatest Common Divisor) + >>> gcd(0, 0) + 0 + >>> gcd(23, 42) + 1 + >>> gcd(15, 33) + 3 + >>> gcd(12345, 67890) + 15 + """ -""" Using the property lcm*gcd of two numbers = product of them """ + return x if y == 0 else gcd(y, x % y) def lcm(x: int, y: int) -> int: + """ + Least Common Multiple. + + Using the property that lcm(a, b) * gcd(a, b) = a*b + + >>> lcm(3, 15) + 15 + >>> lcm(1, 27) + 27 + >>> lcm(13, 27) + 351 + >>> lcm(64, 48) + 192 + """ + return (x * y) // gcd(x, y) def solution(n: int = 20) -> int: - """Returns the smallest positive number that is evenly divisible(divisible + """ + Returns the smallest positive number that is evenly divisible (divisible with no remainder) by all of the numbers from 1 to n. >>> solution(10) 2520 >>> solution(15) 360360 - >>> solution(20) - 232792560 >>> solution(22) 232792560 """ + g = 1 for i in range(1, n + 1): g = lcm(g, i) @@ -40,4 +72,4 @@ def solution(n: int = 20) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol1.py b/project_euler/problem_006/sol1.py index 38f995bbf822..61dd7a321011 100644 --- a/project_euler/problem_006/sol1.py +++ b/project_euler/problem_006/sol1.py @@ -1,22 +1,25 @@ """ -Problem 6: https://projecteuler.net/problem=6 +Project Euler Problem 6: https://projecteuler.net/problem=6 + +Sum square difference The sum of the squares of the first ten natural numbers is, - 1^2 + 2^2 + ... + 10^2 = 385 + 1^2 + 2^2 + ... + 10^2 = 385 The square of the sum of the first ten natural numbers is, - (1 + 2 + ... + 10)^2 = 552 = 3025 + (1 + 2 + ... + 10)^2 = 55^2 = 3025 -Hence the difference between the sum of the squares of the first ten natural -numbers and the square of the sum is 3025 − 385 = 2640. +Hence the difference between the sum of the squares of the first ten +natural numbers and the square of the sum is 3025 - 385 = 2640. -Find the difference between the sum of the squares of the first N natural -numbers and the square of the sum. +Find the difference between the sum of the squares of the first one +hundred natural numbers and the square of the sum. """ def solution(n: int = 100) -> int: - """Returns the difference between the sum of the squares of the first n + """ + Returns the difference between the sum of the squares of the first n natural numbers and the square of the sum. >>> solution(10) @@ -27,9 +30,8 @@ def solution(n: int = 100) -> int: 41230 >>> solution(50) 1582700 - >>> solution() - 25164150 """ + sum_of_squares = 0 sum_of_ints = 0 for i in range(1, n + 1): @@ -39,7 +41,4 @@ def solution(n: int = 100) -> int: if __name__ == "__main__": - import doctest - - doctest.testmod() - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol2.py b/project_euler/problem_006/sol2.py index f4d74c993f0d..cd1bc5071e0e 100644 --- a/project_euler/problem_006/sol2.py +++ b/project_euler/problem_006/sol2.py @@ -1,22 +1,25 @@ """ -Problem 6: https://projecteuler.net/problem=6 +Project Euler Problem 6: https://projecteuler.net/problem=6 + +Sum square difference The sum of the squares of the first ten natural numbers is, - 1^2 + 2^2 + ... + 10^2 = 385 + 1^2 + 2^2 + ... + 10^2 = 385 The square of the sum of the first ten natural numbers is, - (1 + 2 + ... + 10)^2 = 552 = 3025 + (1 + 2 + ... + 10)^2 = 55^2 = 3025 -Hence the difference between the sum of the squares of the first ten natural -numbers and the square of the sum is 3025 − 385 = 2640. +Hence the difference between the sum of the squares of the first ten +natural numbers and the square of the sum is 3025 - 385 = 2640. -Find the difference between the sum of the squares of the first N natural -numbers and the square of the sum. +Find the difference between the sum of the squares of the first one +hundred natural numbers and the square of the sum. """ def solution(n: int = 100) -> int: - """Returns the difference between the sum of the squares of the first n + """ + Returns the difference between the sum of the squares of the first n natural numbers and the square of the sum. >>> solution(10) @@ -27,16 +30,12 @@ def solution(n: int = 100) -> int: 41230 >>> solution(50) 1582700 - >>> solution() - 25164150 """ + sum_cubes = (n * (n + 1) // 2) ** 2 sum_squares = n * (n + 1) * (2 * n + 1) // 6 return sum_cubes - sum_squares if __name__ == "__main__": - import doctest - - doctest.testmod() - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol3.py b/project_euler/problem_006/sol3.py index 8b5c5d3ba4aa..c87931309574 100644 --- a/project_euler/problem_006/sol3.py +++ b/project_euler/problem_006/sol3.py @@ -1,23 +1,26 @@ """ -Problem 6: https://projecteuler.net/problem=6 +Project Euler Problem 6: https://projecteuler.net/problem=6 + +Sum square difference The sum of the squares of the first ten natural numbers is, - 1^2 + 2^2 + ... + 10^2 = 385 + 1^2 + 2^2 + ... + 10^2 = 385 The square of the sum of the first ten natural numbers is, - (1 + 2 + ... + 10)^2 = 552 = 3025 + (1 + 2 + ... + 10)^2 = 55^2 = 3025 -Hence the difference between the sum of the squares of the first ten natural -numbers and the square of the sum is 3025 − 385 = 2640. +Hence the difference between the sum of the squares of the first ten +natural numbers and the square of the sum is 3025 - 385 = 2640. -Find the difference between the sum of the squares of the first N natural -numbers and the square of the sum. +Find the difference between the sum of the squares of the first one +hundred natural numbers and the square of the sum. """ import math def solution(n: int = 100) -> int: - """Returns the difference between the sum of the squares of the first n + """ + Returns the difference between the sum of the squares of the first n natural numbers and the square of the sum. >>> solution(10) @@ -28,16 +31,12 @@ def solution(n: int = 100) -> int: 41230 >>> solution(50) 1582700 - >>> solution() - 25164150 """ + sum_of_squares = sum([i * i for i in range(1, n + 1)]) square_of_sum = int(math.pow(sum(range(1, n + 1)), 2)) return square_of_sum - sum_of_squares if __name__ == "__main__": - import doctest - - doctest.testmod() - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol4.py b/project_euler/problem_006/sol4.py index 5fae84008448..748b141490a0 100644 --- a/project_euler/problem_006/sol4.py +++ b/project_euler/problem_006/sol4.py @@ -1,22 +1,25 @@ """ -Problem 6: https://projecteuler.net/problem=6 +Project Euler Problem 6: https://projecteuler.net/problem=6 + +Sum square difference The sum of the squares of the first ten natural numbers is, - 1^2 + 2^2 + ... + 10^2 = 385 + 1^2 + 2^2 + ... + 10^2 = 385 The square of the sum of the first ten natural numbers is, - (1 + 2 + ... + 10)^2 = 552 = 3025 + (1 + 2 + ... + 10)^2 = 55^2 = 3025 -Hence the difference between the sum of the squares of the first ten natural -numbers and the square of the sum is 3025 − 385 = 2640. +Hence the difference between the sum of the squares of the first ten +natural numbers and the square of the sum is 3025 - 385 = 2640. -Find the difference between the sum of the squares of the first N natural -numbers and the square of the sum. +Find the difference between the sum of the squares of the first one +hundred natural numbers and the square of the sum. """ def solution(n: int = 100) -> int: - """Returns the difference between the sum of the squares of the first n + """ + Returns the difference between the sum of the squares of the first n natural numbers and the square of the sum. >>> solution(10) @@ -27,16 +30,12 @@ def solution(n: int = 100) -> int: 41230 >>> solution(50) 1582700 - >>> solution() - 25164150 """ + sum_of_squares = n * (n + 1) * (2 * n + 1) / 6 square_of_sum = (n * (n + 1) / 2) ** 2 return int(square_of_sum - sum_of_squares) if __name__ == "__main__": - import doctest - - doctest.testmod() - print(solution(int(input("Enter a number: ").strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_007/sol1.py b/project_euler/problem_007/sol1.py index 727d7fb7fac6..78fbcb511611 100644 --- a/project_euler/problem_007/sol1.py +++ b/project_euler/problem_007/sol1.py @@ -1,17 +1,34 @@ """ -Problem 7: https://projecteuler.net/problem=7 +Project Euler Problem 7: https://projecteuler.net/problem=7 -By listing the first six prime numbers: +10001st prime - 2, 3, 5, 7, 11, and 13 +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. -We can see that the 6th prime is 13. What is the Nth prime number? +What is the 10001st prime number? + +References: + - https://en.wikipedia.org/wiki/Prime_number """ + from math import sqrt def is_prime(num: int) -> bool: - """Determines whether the given number is prime or not""" + """ + Determines whether the given number is prime or not + + >>> is_prime(2) + True + >>> is_prime(15) + False + >>> is_prime(29) + True + >>> is_prime(0) + False + """ + if num == 2: return True elif num % 2 == 0: @@ -25,7 +42,8 @@ def is_prime(num: int) -> bool: def solution(nth: int = 10001) -> int: - """Returns the n-th prime number. + """ + Returns the n-th prime number. >>> solution(6) 13 @@ -39,9 +57,8 @@ def solution(nth: int = 10001) -> int: 229 >>> solution(100) 541 - >>> solution() - 104743 """ + count = 0 number = 1 while count != nth and number < 3: @@ -56,4 +73,4 @@ def solution(nth: int = 10001) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_007/sol2.py b/project_euler/problem_007/sol2.py index 62806e1e2e5d..b395c631b766 100644 --- a/project_euler/problem_007/sol2.py +++ b/project_euler/problem_007/sol2.py @@ -1,16 +1,30 @@ """ -Problem 7: https://projecteuler.net/problem=7 +Project Euler Problem 7: https://projecteuler.net/problem=7 -By listing the first six prime numbers: +10001st prime - 2, 3, 5, 7, 11, and 13 +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. -We can see that the 6th prime is 13. What is the Nth prime number? +What is the 10001st prime number? + +References: + - https://en.wikipedia.org/wiki/Prime_number """ def isprime(number: int) -> bool: - """Determines whether the given number is prime or not""" + """ + Determines whether the given number is prime or not + + >>> isprime(2) + True + >>> isprime(15) + False + >>> isprime(29) + True + """ + for i in range(2, int(number ** 0.5) + 1): if number % i == 0: return False @@ -18,7 +32,8 @@ def isprime(number: int) -> bool: def solution(nth: int = 10001) -> int: - """Returns the n-th prime number. + """ + Returns the n-th prime number. >>> solution(6) 13 @@ -32,35 +47,32 @@ def solution(nth: int = 10001) -> int: 229 >>> solution(100) 541 - >>> solution() - 104743 >>> solution(3.4) 5 >>> solution(0) Traceback (most recent call last): ... - ValueError: Parameter nth must be greater or equal to one. + ValueError: Parameter nth must be greater than or equal to one. >>> solution(-17) Traceback (most recent call last): ... - ValueError: Parameter nth must be greater or equal to one. + ValueError: Parameter nth must be greater than or equal to one. >>> solution([]) Traceback (most recent call last): ... - TypeError: Parameter nth must be int or passive of cast to int. + TypeError: Parameter nth must be int or castable to int. >>> solution("asd") Traceback (most recent call last): ... - TypeError: Parameter nth must be int or passive of cast to int. + TypeError: Parameter nth must be int or castable to int. """ + try: nth = int(nth) except (TypeError, ValueError): - raise TypeError( - "Parameter nth must be int or passive of cast to int." - ) from None + raise TypeError("Parameter nth must be int or castable to int.") from None if nth <= 0: - raise ValueError("Parameter nth must be greater or equal to one.") + raise ValueError("Parameter nth must be greater than or equal to one.") primes = [] num = 2 while len(primes) < nth: @@ -73,4 +85,4 @@ def solution(nth: int = 10001) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_007/sol3.py b/project_euler/problem_007/sol3.py index 1182875c05c9..7911fa3e9d6f 100644 --- a/project_euler/problem_007/sol3.py +++ b/project_euler/problem_007/sol3.py @@ -1,24 +1,42 @@ """ -Project 7: https://projecteuler.net/problem=7 +Project Euler Problem 7: https://projecteuler.net/problem=7 -By listing the first six prime numbers: +10001st prime - 2, 3, 5, 7, 11, and 13 +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. -We can see that the 6th prime is 13. What is the Nth prime number? +What is the 10001st prime number? + +References: + - https://en.wikipedia.org/wiki/Prime_number """ import itertools import math def prime_check(number: int) -> bool: - """Determines whether a given number is prime or not""" + """ + Determines whether a given number is prime or not + + >>> prime_check(2) + True + >>> prime_check(15) + False + >>> prime_check(29) + True + """ + if number % 2 == 0 and number > 2: return False return all(number % i for i in range(3, int(math.sqrt(number)) + 1, 2)) def prime_generator(): + """ + Generate a sequence of prime numbers + """ + num = 2 while True: if prime_check(num): @@ -27,7 +45,8 @@ def prime_generator(): def solution(nth: int = 10001) -> int: - """Returns the n-th prime number. + """ + Returns the n-th prime number. >>> solution(6) 13 @@ -41,11 +60,9 @@ def solution(nth: int = 10001) -> int: 229 >>> solution(100) 541 - >>> solution() - 104743 """ return next(itertools.islice(prime_generator(), nth - 1, nth)) if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_008/sol1.py b/project_euler/problem_008/sol1.py index db15907b3fbd..796080127778 100644 --- a/project_euler/problem_008/sol1.py +++ b/project_euler/problem_008/sol1.py @@ -1,33 +1,36 @@ """ -Problem 8: https://projecteuler.net/problem=8 +Project Euler Problem 8: https://projecteuler.net/problem=8 + +Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest product are 9 × 9 × 8 × 9 = 5832. -73167176531330624919225119674426574742355349194934 -96983520312774506326239578318016984801869478851843 -85861560789112949495459501737958331952853208805511 -12540698747158523863050715693290963295227443043557 -66896648950445244523161731856403098711121722383113 -62229893423380308135336276614282806444486645238749 -30358907296290491560440772390713810515859307960866 -70172427121883998797908792274921901699720888093776 -65727333001053367881220235421809751254540594752243 -52584907711670556013604839586446706324415722155397 -53697817977846174064955149290862569321978468622482 -83972241375657056057490261407972968652414535100474 -82166370484403199890008895243450658541227588666881 -16427171479924442928230863465674813919123162824586 -17866458359124566529476545682848912883142607690042 -24219022671055626321111109370544217506941658960408 -07198403850962455444362981230987879927244284909188 -84580156166097919133875499200524063689912560717606 -05886116467109405077541002256983155200055935729725 -71636269561882670428252483600823257530420752963450 + 73167176531330624919225119674426574742355349194934 + 96983520312774506326239578318016984801869478851843 + 85861560789112949495459501737958331952853208805511 + 12540698747158523863050715693290963295227443043557 + 66896648950445244523161731856403098711121722383113 + 62229893423380308135336276614282806444486645238749 + 30358907296290491560440772390713810515859307960866 + 70172427121883998797908792274921901699720888093776 + 65727333001053367881220235421809751254540594752243 + 52584907711670556013604839586446706324415722155397 + 53697817977846174064955149290862569321978468622482 + 83972241375657056057490261407972968652414535100474 + 82166370484403199890008895243450658541227588666881 + 16427171479924442928230863465674813919123162824586 + 17866458359124566529476545682848912883142607690042 + 24219022671055626321111109370544217506941658960408 + 07198403850962455444362981230987879927244284909188 + 84580156166097919133875499200524063689912560717606 + 05886116467109405077541002256983155200055935729725 + 71636269561882670428252483600823257530420752963450 Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product? """ + import sys N = """73167176531330624919225119674426574742355349194934\ @@ -53,12 +56,18 @@ def solution(n: str = N) -> int: - """Find the thirteen adjacent digits in the 1000-digit number n that have + """ + Find the thirteen adjacent digits in the 1000-digit number n that have the greatest product and returns it. - >>> solution(N) - 23514624000 + >>> solution("13978431290823798458352374") + 609638400 + >>> solution("13978431295823798458352374") + 2612736000 + >>> solution("1397843129582379841238352374") + 209018880 """ + largest_product = -sys.maxsize - 1 for i in range(len(n) - 12): product = 1 @@ -70,4 +79,4 @@ def solution(n: str = N) -> int: if __name__ == "__main__": - print(solution(N)) + print(f"{solution() = }") diff --git a/project_euler/problem_008/sol2.py b/project_euler/problem_008/sol2.py index 1b338a9553d7..d2c1b4f7ca48 100644 --- a/project_euler/problem_008/sol2.py +++ b/project_euler/problem_008/sol2.py @@ -1,34 +1,35 @@ """ -Problem 8: https://projecteuler.net/problem=8 +Project Euler Problem 8: https://projecteuler.net/problem=8 + +Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest product are 9 × 9 × 8 × 9 = 5832. -73167176531330624919225119674426574742355349194934 -96983520312774506326239578318016984801869478851843 -85861560789112949495459501737958331952853208805511 -12540698747158523863050715693290963295227443043557 -66896648950445244523161731856403098711121722383113 -62229893423380308135336276614282806444486645238749 -30358907296290491560440772390713810515859307960866 -70172427121883998797908792274921901699720888093776 -65727333001053367881220235421809751254540594752243 -52584907711670556013604839586446706324415722155397 -53697817977846174064955149290862569321978468622482 -83972241375657056057490261407972968652414535100474 -82166370484403199890008895243450658541227588666881 -16427171479924442928230863465674813919123162824586 -17866458359124566529476545682848912883142607690042 -24219022671055626321111109370544217506941658960408 -07198403850962455444362981230987879927244284909188 -84580156166097919133875499200524063689912560717606 -05886116467109405077541002256983155200055935729725 -71636269561882670428252483600823257530420752963450 + 73167176531330624919225119674426574742355349194934 + 96983520312774506326239578318016984801869478851843 + 85861560789112949495459501737958331952853208805511 + 12540698747158523863050715693290963295227443043557 + 66896648950445244523161731856403098711121722383113 + 62229893423380308135336276614282806444486645238749 + 30358907296290491560440772390713810515859307960866 + 70172427121883998797908792274921901699720888093776 + 65727333001053367881220235421809751254540594752243 + 52584907711670556013604839586446706324415722155397 + 53697817977846174064955149290862569321978468622482 + 83972241375657056057490261407972968652414535100474 + 82166370484403199890008895243450658541227588666881 + 16427171479924442928230863465674813919123162824586 + 17866458359124566529476545682848912883142607690042 + 24219022671055626321111109370544217506941658960408 + 07198403850962455444362981230987879927244284909188 + 84580156166097919133875499200524063689912560717606 + 05886116467109405077541002256983155200055935729725 + 71636269561882670428252483600823257530420752963450 Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product? """ - from functools import reduce N = ( @@ -56,12 +57,18 @@ def solution(n: str = N) -> int: - """Find the thirteen adjacent digits in the 1000-digit number n that have + """ + Find the thirteen adjacent digits in the 1000-digit number n that have the greatest product and returns it. - >>> solution(N) - 23514624000 + >>> solution("13978431290823798458352374") + 609638400 + >>> solution("13978431295823798458352374") + 2612736000 + >>> solution("1397843129582379841238352374") + 209018880 """ + return max( [ reduce(lambda x, y: int(x) * int(y), n[i : i + 13]) @@ -71,4 +78,4 @@ def solution(n: str = N) -> int: if __name__ == "__main__": - print(solution(str(N))) + print(f"{solution() = }") diff --git a/project_euler/problem_008/sol3.py b/project_euler/problem_008/sol3.py index 17f68cba57d3..4b99d0ea6e76 100644 --- a/project_euler/problem_008/sol3.py +++ b/project_euler/problem_008/sol3.py @@ -1,29 +1,31 @@ """ -Problem 8: https://projecteuler.net/problem=8 +Project Euler Problem 8: https://projecteuler.net/problem=8 + +Largest product in a series The four adjacent digits in the 1000-digit number that have the greatest product are 9 × 9 × 8 × 9 = 5832. -73167176531330624919225119674426574742355349194934 -96983520312774506326239578318016984801869478851843 -85861560789112949495459501737958331952853208805511 -12540698747158523863050715693290963295227443043557 -66896648950445244523161731856403098711121722383113 -62229893423380308135336276614282806444486645238749 -30358907296290491560440772390713810515859307960866 -70172427121883998797908792274921901699720888093776 -65727333001053367881220235421809751254540594752243 -52584907711670556013604839586446706324415722155397 -53697817977846174064955149290862569321978468622482 -83972241375657056057490261407972968652414535100474 -82166370484403199890008895243450658541227588666881 -16427171479924442928230863465674813919123162824586 -17866458359124566529476545682848912883142607690042 -24219022671055626321111109370544217506941658960408 -07198403850962455444362981230987879927244284909188 -84580156166097919133875499200524063689912560717606 -05886116467109405077541002256983155200055935729725 -71636269561882670428252483600823257530420752963450 + 73167176531330624919225119674426574742355349194934 + 96983520312774506326239578318016984801869478851843 + 85861560789112949495459501737958331952853208805511 + 12540698747158523863050715693290963295227443043557 + 66896648950445244523161731856403098711121722383113 + 62229893423380308135336276614282806444486645238749 + 30358907296290491560440772390713810515859307960866 + 70172427121883998797908792274921901699720888093776 + 65727333001053367881220235421809751254540594752243 + 52584907711670556013604839586446706324415722155397 + 53697817977846174064955149290862569321978468622482 + 83972241375657056057490261407972968652414535100474 + 82166370484403199890008895243450658541227588666881 + 16427171479924442928230863465674813919123162824586 + 17866458359124566529476545682848912883142607690042 + 24219022671055626321111109370544217506941658960408 + 07198403850962455444362981230987879927244284909188 + 84580156166097919133875499200524063689912560717606 + 05886116467109405077541002256983155200055935729725 + 71636269561882670428252483600823257530420752963450 Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product? @@ -53,13 +55,15 @@ def str_eval(s: str) -> int: - """Returns product of digits in given string n + """ + Returns product of digits in given string n >>> str_eval("987654321") 362880 >>> str_eval("22222222") 256 """ + product = 1 for digit in s: product *= int(digit) @@ -67,12 +71,11 @@ def str_eval(s: str) -> int: def solution(n: str = N) -> int: - """Find the thirteen adjacent digits in the 1000-digit number n that have + """ + Find the thirteen adjacent digits in the 1000-digit number n that have the greatest product and returns it. - - >>> solution(N) - 23514624000 """ + largest_product = -sys.maxsize - 1 substr = n[:13] cur_index = 13 @@ -88,4 +91,4 @@ def solution(n: str = N) -> int: if __name__ == "__main__": - print(solution(N)) + print(f"{solution() = }") diff --git a/project_euler/problem_009/sol1.py b/project_euler/problem_009/sol1.py index 1ab3376cae33..a58ea943e48b 100644 --- a/project_euler/problem_009/sol1.py +++ b/project_euler/problem_009/sol1.py @@ -1,26 +1,35 @@ """ -Problem 9: https://projecteuler.net/problem=9 +Project Euler Problem 9: https://projecteuler.net/problem=9 + +Special Pythagorean triplet A Pythagorean triplet is a set of three natural numbers, a < b < c, for which, + a^2 + b^2 = c^2 + For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2. There exists exactly one Pythagorean triplet for which a + b + c = 1000. -Find the product abc. +Find the product a*b*c. + +References: + - https://en.wikipedia.org/wiki/Pythagorean_triple """ def solution() -> int: """ - Returns the product of a,b,c which are Pythagorean Triplet that satisfies - the following: - 1. a < b < c - 2. a**2 + b**2 = c**2 - 3. a + b + c = 1000 + Returns the product of a,b,c which are Pythagorean Triplet that satisfies + the following: + 1. a < b < c + 2. a**2 + b**2 = c**2 + 3. a + b + c = 1000 + # The code below has been commented due to slow execution affecting Travis. # >>> solution() # 31875000 """ + for a in range(300): for b in range(400): for c in range(500): @@ -32,16 +41,17 @@ def solution() -> int: def solution_fast() -> int: """ - Returns the product of a,b,c which are Pythagorean Triplet that satisfies - the following: - 1. a < b < c - 2. a**2 + b**2 = c**2 - 3. a + b + c = 1000 + Returns the product of a,b,c which are Pythagorean Triplet that satisfies + the following: + 1. a < b < c + 2. a**2 + b**2 = c**2 + 3. a + b + c = 1000 # The code below has been commented due to slow execution affecting Travis. # >>> solution_fast() # 31875000 """ + for a in range(300): for b in range(400): c = 1000 - a - b @@ -66,4 +76,4 @@ def benchmark() -> None: if __name__ == "__main__": - benchmark() + print(f"{solution() = }") diff --git a/project_euler/problem_009/sol2.py b/project_euler/problem_009/sol2.py index e22ed45e8644..722ad522ee45 100644 --- a/project_euler/problem_009/sol2.py +++ b/project_euler/problem_009/sol2.py @@ -1,30 +1,40 @@ """ -Problem 9: https://projecteuler.net/problem=9 +Project Euler Problem 9: https://projecteuler.net/problem=9 + +Special Pythagorean triplet A Pythagorean triplet is a set of three natural numbers, a < b < c, for which, + a^2 + b^2 = c^2 + For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2. There exists exactly one Pythagorean triplet for which a + b + c = 1000. -Find the product abc. +Find the product a*b*c. + +References: + - https://en.wikipedia.org/wiki/Pythagorean_triple """ def solution(n: int = 1000) -> int: """ - Return the product of a,b,c which are Pythagorean Triplet that satisfies - the following: - 1. a < b < c - 2. a**2 + b**2 = c**2 - 3. a + b + c = n - - >>> solution(1000) - 31875000 + Return the product of a,b,c which are Pythagorean Triplet that satisfies + the following: + 1. a < b < c + 2. a**2 + b**2 = c**2 + 3. a + b + c = n + + >>> solution(36) + 1620 + >>> solution(126) + 66780 """ + product = -1 candidate = 0 for a in range(1, n // 3): - """Solving the two equations a**2+b**2=c**2 and a+b+c=N eliminating c""" + # Solving the two equations a**2+b**2=c**2 and a+b+c=N eliminating c b = (n * n - 2 * a * n) // (2 * n - 2 * a) c = n - a - b if c * c == (a * a + b * b): @@ -35,4 +45,4 @@ def solution(n: int = 1000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_009/sol3.py b/project_euler/problem_009/sol3.py index 0900a76e6c56..03aed4b70761 100644 --- a/project_euler/problem_009/sol3.py +++ b/project_euler/problem_009/sol3.py @@ -1,5 +1,7 @@ """ -Problem 9: https://projecteuler.net/problem=9 +Project Euler Problem 9: https://projecteuler.net/problem=9 + +Special Pythagorean triplet A Pythagorean triplet is a set of three natural numbers, a < b < c, for which, @@ -8,22 +10,25 @@ For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2. There exists exactly one Pythagorean triplet for which a + b + c = 1000. -Find the product abc. +Find the product a*b*c. + +References: + - https://en.wikipedia.org/wiki/Pythagorean_triple """ def solution() -> int: """ - Returns the product of a,b,c which are Pythagorean Triplet that satisfies - the following: - - 1. a**2 + b**2 = c**2 - 2. a + b + c = 1000 + Returns the product of a,b,c which are Pythagorean Triplet that satisfies + the following: + 1. a**2 + b**2 = c**2 + 2. a + b + c = 1000 # The code below has been commented due to slow execution affecting Travis. # >>> solution() # 31875000 """ + return [ a * b * (1000 - a - b) for a in range(1, 999) @@ -33,4 +38,4 @@ def solution() -> int: if __name__ == "__main__": - print(solution()) + print(f"{solution() = }") diff --git a/project_euler/problem_010/sol1.py b/project_euler/problem_010/sol1.py index 4f3b3a4a42f5..bd49b3523c97 100644 --- a/project_euler/problem_010/sol1.py +++ b/project_euler/problem_010/sol1.py @@ -1,16 +1,23 @@ """ -https://projecteuler.net/problem=10 +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes -Problem Statement: The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. + +References: + - https://en.wikipedia.org/wiki/Prime_number """ + from math import sqrt def is_prime(n: int) -> bool: - """Returns boolean representing primality of given number num. + """ + Returns boolean representing primality of given number num. + >>> is_prime(2) True >>> is_prime(3) @@ -20,6 +27,7 @@ def is_prime(n: int) -> bool: >>> is_prime(2999) True """ + for i in range(2, int(sqrt(n)) + 1): if n % i == 0: return False @@ -28,11 +36,9 @@ def is_prime(n: int) -> bool: def solution(n: int = 2000000) -> int: - """Returns the sum of all the primes below n. + """ + Returns the sum of all the primes below n. - # The code below has been commented due to slow execution affecting Travis. - # >>> solution(2000000) - # 142913828922 >>> solution(1000) 76127 >>> solution(5000) @@ -42,6 +48,7 @@ def solution(n: int = 2000000) -> int: >>> solution(7) 10 """ + if n > 2: sum_of_primes = 2 else: @@ -55,4 +62,4 @@ def solution(n: int = 2000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_010/sol2.py b/project_euler/problem_010/sol2.py index 39f5f5604053..3a2f485dde50 100644 --- a/project_euler/problem_010/sol2.py +++ b/project_euler/problem_010/sol2.py @@ -1,10 +1,14 @@ """ -https://projecteuler.net/problem=10 +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes -Problem Statement: The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. + +References: + - https://en.wikipedia.org/wiki/Prime_number """ import math from itertools import takewhile @@ -12,7 +16,9 @@ def is_prime(number: int) -> bool: - """Returns boolean representing primality of given number num. + """ + Returns boolean representing primality of given number num. + >>> is_prime(2) True >>> is_prime(3) @@ -22,12 +28,17 @@ def is_prime(number: int) -> bool: >>> is_prime(2999) True """ + if number % 2 == 0 and number > 2: return False return all(number % i for i in range(3, int(math.sqrt(number)) + 1, 2)) def prime_generator() -> Iterator[int]: + """ + Generate a list sequence of prime numbers + """ + num = 2 while True: if is_prime(num): @@ -36,11 +47,9 @@ def prime_generator() -> Iterator[int]: def solution(n: int = 2000000) -> int: - """Returns the sum of all the primes below n. + """ + Returns the sum of all the primes below n. - # The code below has been commented due to slow execution affecting Travis. - # >>> solution(2000000) - # 142913828922 >>> solution(1000) 76127 >>> solution(5000) @@ -50,8 +59,9 @@ def solution(n: int = 2000000) -> int: >>> solution(7) 10 """ + return sum(takewhile(lambda x: x < n, prime_generator())) if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") diff --git a/project_euler/problem_010/sol3.py b/project_euler/problem_010/sol3.py index ef895f546fa5..f49d9393c7af 100644 --- a/project_euler/problem_010/sol3.py +++ b/project_euler/problem_010/sol3.py @@ -1,43 +1,47 @@ """ -https://projecteuler.net/problem=10 +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes -Problem Statement: The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. + +References: + - https://en.wikipedia.org/wiki/Prime_number + - https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ def solution(n: int = 2000000) -> int: - """Returns the sum of all the primes below n using Sieve of Eratosthenes: + """ + Returns the sum of all the primes below n using Sieve of Eratosthenes: - https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes The sieve of Eratosthenes is one of the most efficient ways to find all primes smaller than n when n is smaller than 10 million. Only for positive numbers. - >>> solution(2_000_000) - 142913828922 - >>> solution(1_000) + >>> solution(1000) 76127 - >>> solution(5_000) + >>> solution(5000) 1548136 - >>> solution(10_000) + >>> solution(10000) 5736396 >>> solution(7) 10 - >>> solution(7.1) # doctest: +ELLIPSIS + >>> solution(7.1) # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: 'float' object cannot be interpreted as an integer - >>> solution(-7) # doctest: +ELLIPSIS + >>> solution(-7) # doctest: +ELLIPSIS Traceback (most recent call last): ... IndexError: list assignment index out of range - >>> solution("seven") # doctest: +ELLIPSIS + >>> solution("seven") # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: can only concatenate str (not "int") to str """ + primality_list = [0 for i in range(n + 1)] primality_list[0] = 1 primality_list[1] = 1 @@ -54,4 +58,4 @@ def solution(n: int = 2000000) -> int: if __name__ == "__main__": - print(solution(int(input().strip()))) + print(f"{solution() = }") From 3a1c2f6d8ef27b89f29c8a439fa586fa9e60a667 Mon Sep 17 00:00:00 2001 From: Ayoub Chegraoui Date: Sun, 25 Oct 2020 05:06:31 +0100 Subject: [PATCH 031/195] Add solution to Project Euler problem 81 (#3408) * Add solution to problem 81 - project euler * Update project_euler/problem_081/sol1.py Co-authored-by: Christian Clauss * Update project_euler/problem_081/sol1.py Co-authored-by: Christian Clauss Co-authored-by: Christian Clauss --- project_euler/problem_081/__init__.py | 0 project_euler/problem_081/matrix.txt | 80 +++++++++++++++++++++++++++ project_euler/problem_081/sol1.py | 47 ++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 project_euler/problem_081/__init__.py create mode 100644 project_euler/problem_081/matrix.txt create mode 100644 project_euler/problem_081/sol1.py diff --git a/project_euler/problem_081/__init__.py b/project_euler/problem_081/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_081/matrix.txt b/project_euler/problem_081/matrix.txt new file mode 100644 index 000000000000..f65322a7e541 --- /dev/null +++ b/project_euler/problem_081/matrix.txt @@ -0,0 +1,80 @@ +4445,2697,5115,718,2209,2212,654,4348,3079,6821,7668,3276,8874,4190,3785,2752,9473,7817,9137,496,7338,3434,7152,4355,4552,7917,7827,2460,2350,691,3514,5880,3145,7633,7199,3783,5066,7487,3285,1084,8985,760,872,8609,8051,1134,9536,5750,9716,9371,7619,5617,275,9721,2997,2698,1887,8825,6372,3014,2113,7122,7050,6775,5948,2758,1219,3539,348,7989,2735,9862,1263,8089,6401,9462,3168,2758,3748,5870 +1096,20,1318,7586,5167,2642,1443,5741,7621,7030,5526,4244,2348,4641,9827,2448,6918,5883,3737,300,7116,6531,567,5997,3971,6623,820,6148,3287,1874,7981,8424,7672,7575,6797,6717,1078,5008,4051,8795,5820,346,1851,6463,2117,6058,3407,8211,117,4822,1317,4377,4434,5925,8341,4800,1175,4173,690,8978,7470,1295,3799,8724,3509,9849,618,3320,7068,9633,2384,7175,544,6583,1908,9983,481,4187,9353,9377 +9607,7385,521,6084,1364,8983,7623,1585,6935,8551,2574,8267,4781,3834,2764,2084,2669,4656,9343,7709,2203,9328,8004,6192,5856,3555,2260,5118,6504,1839,9227,1259,9451,1388,7909,5733,6968,8519,9973,1663,5315,7571,3035,4325,4283,2304,6438,3815,9213,9806,9536,196,5542,6907,2475,1159,5820,9075,9470,2179,9248,1828,4592,9167,3713,4640,47,3637,309,7344,6955,346,378,9044,8635,7466,5036,9515,6385,9230 +7206,3114,7760,1094,6150,5182,7358,7387,4497,955,101,1478,7777,6966,7010,8417,6453,4955,3496,107,449,8271,131,2948,6185,784,5937,8001,6104,8282,4165,3642,710,2390,575,715,3089,6964,4217,192,5949,7006,715,3328,1152,66,8044,4319,1735,146,4818,5456,6451,4113,1063,4781,6799,602,1504,6245,6550,1417,1343,2363,3785,5448,4545,9371,5420,5068,4613,4882,4241,5043,7873,8042,8434,3939,9256,2187 +3620,8024,577,9997,7377,7682,1314,1158,6282,6310,1896,2509,5436,1732,9480,706,496,101,6232,7375,2207,2306,110,6772,3433,2878,8140,5933,8688,1399,2210,7332,6172,6403,7333,4044,2291,1790,2446,7390,8698,5723,3678,7104,1825,2040,140,3982,4905,4160,2200,5041,2512,1488,2268,1175,7588,8321,8078,7312,977,5257,8465,5068,3453,3096,1651,7906,253,9250,6021,8791,8109,6651,3412,345,4778,5152,4883,7505 +1074,5438,9008,2679,5397,5429,2652,3403,770,9188,4248,2493,4361,8327,9587,707,9525,5913,93,1899,328,2876,3604,673,8576,6908,7659,2544,3359,3883,5273,6587,3065,1749,3223,604,9925,6941,2823,8767,7039,3290,3214,1787,7904,3421,7137,9560,8451,2669,9219,6332,1576,5477,6755,8348,4164,4307,2984,4012,6629,1044,2874,6541,4942,903,1404,9125,5160,8836,4345,2581,460,8438,1538,5507,668,3352,2678,6942 +4295,1176,5596,1521,3061,9868,7037,7129,8933,6659,5947,5063,3653,9447,9245,2679,767,714,116,8558,163,3927,8779,158,5093,2447,5782,3967,1716,931,7772,8164,1117,9244,5783,7776,3846,8862,6014,2330,6947,1777,3112,6008,3491,1906,5952,314,4602,8994,5919,9214,3995,5026,7688,6809,5003,3128,2509,7477,110,8971,3982,8539,2980,4689,6343,5411,2992,5270,5247,9260,2269,7474,1042,7162,5206,1232,4556,4757 +510,3556,5377,1406,5721,4946,2635,7847,4251,8293,8281,6351,4912,287,2870,3380,3948,5322,3840,4738,9563,1906,6298,3234,8959,1562,6297,8835,7861,239,6618,1322,2553,2213,5053,5446,4402,6500,5182,8585,6900,5756,9661,903,5186,7687,5998,7997,8081,8955,4835,6069,2621,1581,732,9564,1082,1853,5442,1342,520,1737,3703,5321,4793,2776,1508,1647,9101,2499,6891,4336,7012,3329,3212,1442,9993,3988,4930,7706 +9444,3401,5891,9716,1228,7107,109,3563,2700,6161,5039,4992,2242,8541,7372,2067,1294,3058,1306,320,8881,5756,9326,411,8650,8824,5495,8282,8397,2000,1228,7817,2099,6473,3571,5994,4447,1299,5991,543,7874,2297,1651,101,2093,3463,9189,6872,6118,872,1008,1779,2805,9084,4048,2123,5877,55,3075,1737,9459,4535,6453,3644,108,5982,4437,5213,1340,6967,9943,5815,669,8074,1838,6979,9132,9315,715,5048 +3327,4030,7177,6336,9933,5296,2621,4785,2755,4832,2512,2118,2244,4407,2170,499,7532,9742,5051,7687,970,6924,3527,4694,5145,1306,2165,5940,2425,8910,3513,1909,6983,346,6377,4304,9330,7203,6605,3709,3346,970,369,9737,5811,4427,9939,3693,8436,5566,1977,3728,2399,3985,8303,2492,5366,9802,9193,7296,1033,5060,9144,2766,1151,7629,5169,5995,58,7619,7565,4208,1713,6279,3209,4908,9224,7409,1325,8540 +6882,1265,1775,3648,4690,959,5837,4520,5394,1378,9485,1360,4018,578,9174,2932,9890,3696,116,1723,1178,9355,7063,1594,1918,8574,7594,7942,1547,6166,7888,354,6932,4651,1010,7759,6905,661,7689,6092,9292,3845,9605,8443,443,8275,5163,7720,7265,6356,7779,1798,1754,5225,6661,1180,8024,5666,88,9153,1840,3508,1193,4445,2648,3538,6243,6375,8107,5902,5423,2520,1122,5015,6113,8859,9370,966,8673,2442 +7338,3423,4723,6533,848,8041,7921,8277,4094,5368,7252,8852,9166,2250,2801,6125,8093,5738,4038,9808,7359,9494,601,9116,4946,2702,5573,2921,9862,1462,1269,2410,4171,2709,7508,6241,7522,615,2407,8200,4189,5492,5649,7353,2590,5203,4274,710,7329,9063,956,8371,3722,4253,4785,1194,4828,4717,4548,940,983,2575,4511,2938,1827,2027,2700,1236,841,5760,1680,6260,2373,3851,1841,4968,1172,5179,7175,3509 +4420,1327,3560,2376,6260,2988,9537,4064,4829,8872,9598,3228,1792,7118,9962,9336,4368,9189,6857,1829,9863,6287,7303,7769,2707,8257,2391,2009,3975,4993,3068,9835,3427,341,8412,2134,4034,8511,6421,3041,9012,2983,7289,100,1355,7904,9186,6920,5856,2008,6545,8331,3655,5011,839,8041,9255,6524,3862,8788,62,7455,3513,5003,8413,3918,2076,7960,6108,3638,6999,3436,1441,4858,4181,1866,8731,7745,3744,1000 +356,8296,8325,1058,1277,4743,3850,2388,6079,6462,2815,5620,8495,5378,75,4324,3441,9870,1113,165,1544,1179,2834,562,6176,2313,6836,8839,2986,9454,5199,6888,1927,5866,8760,320,1792,8296,7898,6121,7241,5886,5814,2815,8336,1576,4314,3109,2572,6011,2086,9061,9403,3947,5487,9731,7281,3159,1819,1334,3181,5844,5114,9898,4634,2531,4412,6430,4262,8482,4546,4555,6804,2607,9421,686,8649,8860,7794,6672 +9870,152,1558,4963,8750,4754,6521,6256,8818,5208,5691,9659,8377,9725,5050,5343,2539,6101,1844,9700,7750,8114,5357,3001,8830,4438,199,9545,8496,43,2078,327,9397,106,6090,8181,8646,6414,7499,5450,4850,6273,5014,4131,7639,3913,6571,8534,9703,4391,7618,445,1320,5,1894,6771,7383,9191,4708,9706,6939,7937,8726,9382,5216,3685,2247,9029,8154,1738,9984,2626,9438,4167,6351,5060,29,1218,1239,4785 +192,5213,8297,8974,4032,6966,5717,1179,6523,4679,9513,1481,3041,5355,9303,9154,1389,8702,6589,7818,6336,3539,5538,3094,6646,6702,6266,2759,4608,4452,617,9406,8064,6379,444,5602,4950,1810,8391,1536,316,8714,1178,5182,5863,5110,5372,4954,1978,2971,5680,4863,2255,4630,5723,2168,538,1692,1319,7540,440,6430,6266,7712,7385,5702,620,641,3136,7350,1478,3155,2820,9109,6261,1122,4470,14,8493,2095 +1046,4301,6082,474,4974,7822,2102,5161,5172,6946,8074,9716,6586,9962,9749,5015,2217,995,5388,4402,7652,6399,6539,1349,8101,3677,1328,9612,7922,2879,231,5887,2655,508,4357,4964,3554,5930,6236,7384,4614,280,3093,9600,2110,7863,2631,6626,6620,68,1311,7198,7561,1768,5139,1431,221,230,2940,968,5283,6517,2146,1646,869,9402,7068,8645,7058,1765,9690,4152,2926,9504,2939,7504,6074,2944,6470,7859 +4659,736,4951,9344,1927,6271,8837,8711,3241,6579,7660,5499,5616,3743,5801,4682,9748,8796,779,1833,4549,8138,4026,775,4170,2432,4174,3741,7540,8017,2833,4027,396,811,2871,1150,9809,2719,9199,8504,1224,540,2051,3519,7982,7367,2761,308,3358,6505,2050,4836,5090,7864,805,2566,2409,6876,3361,8622,5572,5895,3280,441,7893,8105,1634,2929,274,3926,7786,6123,8233,9921,2674,5340,1445,203,4585,3837 +5759,338,7444,7968,7742,3755,1591,4839,1705,650,7061,2461,9230,9391,9373,2413,1213,431,7801,4994,2380,2703,6161,6878,8331,2538,6093,1275,5065,5062,2839,582,1014,8109,3525,1544,1569,8622,7944,2905,6120,1564,1839,5570,7579,1318,2677,5257,4418,5601,7935,7656,5192,1864,5886,6083,5580,6202,8869,1636,7907,4759,9082,5854,3185,7631,6854,5872,5632,5280,1431,2077,9717,7431,4256,8261,9680,4487,4752,4286 +1571,1428,8599,1230,7772,4221,8523,9049,4042,8726,7567,6736,9033,2104,4879,4967,6334,6716,3994,1269,8995,6539,3610,7667,6560,6065,874,848,4597,1711,7161,4811,6734,5723,6356,6026,9183,2586,5636,1092,7779,7923,8747,6887,7505,9909,1792,3233,4526,3176,1508,8043,720,5212,6046,4988,709,5277,8256,3642,1391,5803,1468,2145,3970,6301,7767,2359,8487,9771,8785,7520,856,1605,8972,2402,2386,991,1383,5963 +1822,4824,5957,6511,9868,4113,301,9353,6228,2881,2966,6956,9124,9574,9233,1601,7340,973,9396,540,4747,8590,9535,3650,7333,7583,4806,3593,2738,8157,5215,8472,2284,9473,3906,6982,5505,6053,7936,6074,7179,6688,1564,1103,6860,5839,2022,8490,910,7551,7805,881,7024,1855,9448,4790,1274,3672,2810,774,7623,4223,4850,6071,9975,4935,1915,9771,6690,3846,517,463,7624,4511,614,6394,3661,7409,1395,8127 +8738,3850,9555,3695,4383,2378,87,6256,6740,7682,9546,4255,6105,2000,1851,4073,8957,9022,6547,5189,2487,303,9602,7833,1628,4163,6678,3144,8589,7096,8913,5823,4890,7679,1212,9294,5884,2972,3012,3359,7794,7428,1579,4350,7246,4301,7779,7790,3294,9547,4367,3549,1958,8237,6758,3497,3250,3456,6318,1663,708,7714,6143,6890,3428,6853,9334,7992,591,6449,9786,1412,8500,722,5468,1371,108,3939,4199,2535 +7047,4323,1934,5163,4166,461,3544,2767,6554,203,6098,2265,9078,2075,4644,6641,8412,9183,487,101,7566,5622,1975,5726,2920,5374,7779,5631,3753,3725,2672,3621,4280,1162,5812,345,8173,9785,1525,955,5603,2215,2580,5261,2765,2990,5979,389,3907,2484,1232,5933,5871,3304,1138,1616,5114,9199,5072,7442,7245,6472,4760,6359,9053,7876,2564,9404,3043,9026,2261,3374,4460,7306,2326,966,828,3274,1712,3446 +3975,4565,8131,5800,4570,2306,8838,4392,9147,11,3911,7118,9645,4994,2028,6062,5431,2279,8752,2658,7836,994,7316,5336,7185,3289,1898,9689,2331,5737,3403,1124,2679,3241,7748,16,2724,5441,6640,9368,9081,5618,858,4969,17,2103,6035,8043,7475,2181,939,415,1617,8500,8253,2155,7843,7974,7859,1746,6336,3193,2617,8736,4079,6324,6645,8891,9396,5522,6103,1857,8979,3835,2475,1310,7422,610,8345,7615 +9248,5397,5686,2988,3446,4359,6634,9141,497,9176,6773,7448,1907,8454,916,1596,2241,1626,1384,2741,3649,5362,8791,7170,2903,2475,5325,6451,924,3328,522,90,4813,9737,9557,691,2388,1383,4021,1609,9206,4707,5200,7107,8104,4333,9860,5013,1224,6959,8527,1877,4545,7772,6268,621,4915,9349,5970,706,9583,3071,4127,780,8231,3017,9114,3836,7503,2383,1977,4870,8035,2379,9704,1037,3992,3642,1016,4303 +5093,138,4639,6609,1146,5565,95,7521,9077,2272,974,4388,2465,2650,722,4998,3567,3047,921,2736,7855,173,2065,4238,1048,5,6847,9548,8632,9194,5942,4777,7910,8971,6279,7253,2516,1555,1833,3184,9453,9053,6897,7808,8629,4877,1871,8055,4881,7639,1537,7701,2508,7564,5845,5023,2304,5396,3193,2955,1088,3801,6203,1748,3737,1276,13,4120,7715,8552,3047,2921,106,7508,304,1280,7140,2567,9135,5266 +6237,4607,7527,9047,522,7371,4883,2540,5867,6366,5301,1570,421,276,3361,527,6637,4861,2401,7522,5808,9371,5298,2045,5096,5447,7755,5115,7060,8529,4078,1943,1697,1764,5453,7085,960,2405,739,2100,5800,728,9737,5704,5693,1431,8979,6428,673,7540,6,7773,5857,6823,150,5869,8486,684,5816,9626,7451,5579,8260,3397,5322,6920,1879,2127,2884,5478,4977,9016,6165,6292,3062,5671,5968,78,4619,4763 +9905,7127,9390,5185,6923,3721,9164,9705,4341,1031,1046,5127,7376,6528,3248,4941,1178,7889,3364,4486,5358,9402,9158,8600,1025,874,1839,1783,309,9030,1843,845,8398,1433,7118,70,8071,2877,3904,8866,6722,4299,10,1929,5897,4188,600,1889,3325,2485,6473,4474,7444,6992,4846,6166,4441,2283,2629,4352,7775,1101,2214,9985,215,8270,9750,2740,8361,7103,5930,8664,9690,8302,9267,344,2077,1372,1880,9550 +5825,8517,7769,2405,8204,1060,3603,7025,478,8334,1997,3692,7433,9101,7294,7498,9415,5452,3850,3508,6857,9213,6807,4412,7310,854,5384,686,4978,892,8651,3241,2743,3801,3813,8588,6701,4416,6990,6490,3197,6838,6503,114,8343,5844,8646,8694,65,791,5979,2687,2621,2019,8097,1423,3644,9764,4921,3266,3662,5561,2476,8271,8138,6147,1168,3340,1998,9874,6572,9873,6659,5609,2711,3931,9567,4143,7833,8887 +6223,2099,2700,589,4716,8333,1362,5007,2753,2848,4441,8397,7192,8191,4916,9955,6076,3370,6396,6971,3156,248,3911,2488,4930,2458,7183,5455,170,6809,6417,3390,1956,7188,577,7526,2203,968,8164,479,8699,7915,507,6393,4632,1597,7534,3604,618,3280,6061,9793,9238,8347,568,9645,2070,5198,6482,5000,9212,6655,5961,7513,1323,3872,6170,3812,4146,2736,67,3151,5548,2781,9679,7564,5043,8587,1893,4531 +5826,3690,6724,2121,9308,6986,8106,6659,2142,1642,7170,2877,5757,6494,8026,6571,8387,9961,6043,9758,9607,6450,8631,8334,7359,5256,8523,2225,7487,1977,9555,8048,5763,2414,4948,4265,2427,8978,8088,8841,9208,9601,5810,9398,8866,9138,4176,5875,7212,3272,6759,5678,7649,4922,5422,1343,8197,3154,3600,687,1028,4579,2084,9467,4492,7262,7296,6538,7657,7134,2077,1505,7332,6890,8964,4879,7603,7400,5973,739 +1861,1613,4879,1884,7334,966,2000,7489,2123,4287,1472,3263,4726,9203,1040,4103,6075,6049,330,9253,4062,4268,1635,9960,577,1320,3195,9628,1030,4092,4979,6474,6393,2799,6967,8687,7724,7392,9927,2085,3200,6466,8702,265,7646,8665,7986,7266,4574,6587,612,2724,704,3191,8323,9523,3002,704,5064,3960,8209,2027,2758,8393,4875,4641,9584,6401,7883,7014,768,443,5490,7506,1852,2005,8850,5776,4487,4269 +4052,6687,4705,7260,6645,6715,3706,5504,8672,2853,1136,8187,8203,4016,871,1809,1366,4952,9294,5339,6872,2645,6083,7874,3056,5218,7485,8796,7401,3348,2103,426,8572,4163,9171,3176,948,7654,9344,3217,1650,5580,7971,2622,76,2874,880,2034,9929,1546,2659,5811,3754,7096,7436,9694,9960,7415,2164,953,2360,4194,2397,1047,2196,6827,575,784,2675,8821,6802,7972,5996,6699,2134,7577,2887,1412,4349,4380 +4629,2234,6240,8132,7592,3181,6389,1214,266,1910,2451,8784,2790,1127,6932,1447,8986,2492,5476,397,889,3027,7641,5083,5776,4022,185,3364,5701,2442,2840,4160,9525,4828,6602,2614,7447,3711,4505,7745,8034,6514,4907,2605,7753,6958,7270,6936,3006,8968,439,2326,4652,3085,3425,9863,5049,5361,8688,297,7580,8777,7916,6687,8683,7141,306,9569,2384,1500,3346,4601,7329,9040,6097,2727,6314,4501,4974,2829 +8316,4072,2025,6884,3027,1808,5714,7624,7880,8528,4205,8686,7587,3230,1139,7273,6163,6986,3914,9309,1464,9359,4474,7095,2212,7302,2583,9462,7532,6567,1606,4436,8981,5612,6796,4385,5076,2007,6072,3678,8331,1338,3299,8845,4783,8613,4071,1232,6028,2176,3990,2148,3748,103,9453,538,6745,9110,926,3125,473,5970,8728,7072,9062,1404,1317,5139,9862,6496,6062,3338,464,1600,2532,1088,8232,7739,8274,3873 +2341,523,7096,8397,8301,6541,9844,244,4993,2280,7689,4025,4196,5522,7904,6048,2623,9258,2149,9461,6448,8087,7245,1917,8340,7127,8466,5725,6996,3421,5313,512,9164,9837,9794,8369,4185,1488,7210,1524,1016,4620,9435,2478,7765,8035,697,6677,3724,6988,5853,7662,3895,9593,1185,4727,6025,5734,7665,3070,138,8469,6748,6459,561,7935,8646,2378,462,7755,3115,9690,8877,3946,2728,8793,244,6323,8666,4271 +6430,2406,8994,56,1267,3826,9443,7079,7579,5232,6691,3435,6718,5698,4144,7028,592,2627,217,734,6194,8156,9118,58,2640,8069,4127,3285,694,3197,3377,4143,4802,3324,8134,6953,7625,3598,3584,4289,7065,3434,2106,7132,5802,7920,9060,7531,3321,1725,1067,3751,444,5503,6785,7937,6365,4803,198,6266,8177,1470,6390,1606,2904,7555,9834,8667,2033,1723,5167,1666,8546,8152,473,4475,6451,7947,3062,3281 +2810,3042,7759,1741,2275,2609,7676,8640,4117,1958,7500,8048,1757,3954,9270,1971,4796,2912,660,5511,3553,1012,5757,4525,6084,7198,8352,5775,7726,8591,7710,9589,3122,4392,6856,5016,749,2285,3356,7482,9956,7348,2599,8944,495,3462,3578,551,4543,7207,7169,7796,1247,4278,6916,8176,3742,8385,2310,1345,8692,2667,4568,1770,8319,3585,4920,3890,4928,7343,5385,9772,7947,8786,2056,9266,3454,2807,877,2660 +6206,8252,5928,5837,4177,4333,207,7934,5581,9526,8906,1498,8411,2984,5198,5134,2464,8435,8514,8674,3876,599,5327,826,2152,4084,2433,9327,9697,4800,2728,3608,3849,3861,3498,9943,1407,3991,7191,9110,5666,8434,4704,6545,5944,2357,1163,4995,9619,6754,4200,9682,6654,4862,4744,5953,6632,1054,293,9439,8286,2255,696,8709,1533,1844,6441,430,1999,6063,9431,7018,8057,2920,6266,6799,356,3597,4024,6665 +3847,6356,8541,7225,2325,2946,5199,469,5450,7508,2197,9915,8284,7983,6341,3276,3321,16,1321,7608,5015,3362,8491,6968,6818,797,156,2575,706,9516,5344,5457,9210,5051,8099,1617,9951,7663,8253,9683,2670,1261,4710,1068,8753,4799,1228,2621,3275,6188,4699,1791,9518,8701,5932,4275,6011,9877,2933,4182,6059,2930,6687,6682,9771,654,9437,3169,8596,1827,5471,8909,2352,123,4394,3208,8756,5513,6917,2056 +5458,8173,3138,3290,4570,4892,3317,4251,9699,7973,1163,1935,5477,6648,9614,5655,9592,975,9118,2194,7322,8248,8413,3462,8560,1907,7810,6650,7355,2939,4973,6894,3933,3784,3200,2419,9234,4747,2208,2207,1945,2899,1407,6145,8023,3484,5688,7686,2737,3828,3704,9004,5190,9740,8643,8650,5358,4426,1522,1707,3613,9887,6956,2447,2762,833,1449,9489,2573,1080,4167,3456,6809,2466,227,7125,2759,6250,6472,8089 +3266,7025,9756,3914,1265,9116,7723,9788,6805,5493,2092,8688,6592,9173,4431,4028,6007,7131,4446,4815,3648,6701,759,3312,8355,4485,4187,5188,8746,7759,3528,2177,5243,8379,3838,7233,4607,9187,7216,2190,6967,2920,6082,7910,5354,3609,8958,6949,7731,494,8753,8707,1523,4426,3543,7085,647,6771,9847,646,5049,824,8417,5260,2730,5702,2513,9275,4279,2767,8684,1165,9903,4518,55,9682,8963,6005,2102,6523 +1998,8731,936,1479,5259,7064,4085,91,7745,7136,3773,3810,730,8255,2705,2653,9790,6807,2342,355,9344,2668,3690,2028,9679,8102,574,4318,6481,9175,5423,8062,2867,9657,7553,3442,3920,7430,3945,7639,3714,3392,2525,4995,4850,2867,7951,9667,486,9506,9888,781,8866,1702,3795,90,356,1483,4200,2131,6969,5931,486,6880,4404,1084,5169,4910,6567,8335,4686,5043,2614,3352,2667,4513,6472,7471,5720,1616 +8878,1613,1716,868,1906,2681,564,665,5995,2474,7496,3432,9491,9087,8850,8287,669,823,347,6194,2264,2592,7871,7616,8508,4827,760,2676,4660,4881,7572,3811,9032,939,4384,929,7525,8419,5556,9063,662,8887,7026,8534,3111,1454,2082,7598,5726,6687,9647,7608,73,3014,5063,670,5461,5631,3367,9796,8475,7908,5073,1565,5008,5295,4457,1274,4788,1728,338,600,8415,8535,9351,7750,6887,5845,1741,125 +3637,6489,9634,9464,9055,2413,7824,9517,7532,3577,7050,6186,6980,9365,9782,191,870,2497,8498,2218,2757,5420,6468,586,3320,9230,1034,1393,9886,5072,9391,1178,8464,8042,6869,2075,8275,3601,7715,9470,8786,6475,8373,2159,9237,2066,3264,5000,679,355,3069,4073,494,2308,5512,4334,9438,8786,8637,9774,1169,1949,6594,6072,4270,9158,7916,5752,6794,9391,6301,5842,3285,2141,3898,8027,4310,8821,7079,1307 +8497,6681,4732,7151,7060,5204,9030,7157,833,5014,8723,3207,9796,9286,4913,119,5118,7650,9335,809,3675,2597,5144,3945,5090,8384,187,4102,1260,2445,2792,4422,8389,9290,50,1765,1521,6921,8586,4368,1565,5727,7855,2003,4834,9897,5911,8630,5070,1330,7692,7557,7980,6028,5805,9090,8265,3019,3802,698,9149,5748,1965,9658,4417,5994,5584,8226,2937,272,5743,1278,5698,8736,2595,6475,5342,6596,1149,6920 +8188,8009,9546,6310,8772,2500,9846,6592,6872,3857,1307,8125,7042,1544,6159,2330,643,4604,7899,6848,371,8067,2062,3200,7295,1857,9505,6936,384,2193,2190,301,8535,5503,1462,7380,5114,4824,8833,1763,4974,8711,9262,6698,3999,2645,6937,7747,1128,2933,3556,7943,2885,3122,9105,5447,418,2899,5148,3699,9021,9501,597,4084,175,1621,1,1079,6067,5812,4326,9914,6633,5394,4233,6728,9084,1864,5863,1225 +9935,8793,9117,1825,9542,8246,8437,3331,9128,9675,6086,7075,319,1334,7932,3583,7167,4178,1726,7720,695,8277,7887,6359,5912,1719,2780,8529,1359,2013,4498,8072,1129,9998,1147,8804,9405,6255,1619,2165,7491,1,8882,7378,3337,503,5758,4109,3577,985,3200,7615,8058,5032,1080,6410,6873,5496,1466,2412,9885,5904,4406,3605,8770,4361,6205,9193,1537,9959,214,7260,9566,1685,100,4920,7138,9819,5637,976 +3466,9854,985,1078,7222,8888,5466,5379,3578,4540,6853,8690,3728,6351,7147,3134,6921,9692,857,3307,4998,2172,5783,3931,9417,2541,6299,13,787,2099,9131,9494,896,8600,1643,8419,7248,2660,2609,8579,91,6663,5506,7675,1947,6165,4286,1972,9645,3805,1663,1456,8853,5705,9889,7489,1107,383,4044,2969,3343,152,7805,4980,9929,5033,1737,9953,7197,9158,4071,1324,473,9676,3984,9680,3606,8160,7384,5432 +1005,4512,5186,3953,2164,3372,4097,3247,8697,3022,9896,4101,3871,6791,3219,2742,4630,6967,7829,5991,6134,1197,1414,8923,8787,1394,8852,5019,7768,5147,8004,8825,5062,9625,7988,1110,3992,7984,9966,6516,6251,8270,421,3723,1432,4830,6935,8095,9059,2214,6483,6846,3120,1587,6201,6691,9096,9627,6671,4002,3495,9939,7708,7465,5879,6959,6634,3241,3401,2355,9061,2611,7830,3941,2177,2146,5089,7079,519,6351 +7280,8586,4261,2831,7217,3141,9994,9940,5462,2189,4005,6942,9848,5350,8060,6665,7519,4324,7684,657,9453,9296,2944,6843,7499,7847,1728,9681,3906,6353,5529,2822,3355,3897,7724,4257,7489,8672,4356,3983,1948,6892,7415,4153,5893,4190,621,1736,4045,9532,7701,3671,1211,1622,3176,4524,9317,7800,5638,6644,6943,5463,3531,2821,1347,5958,3436,1438,2999,994,850,4131,2616,1549,3465,5946,690,9273,6954,7991 +9517,399,3249,2596,7736,2142,1322,968,7350,1614,468,3346,3265,7222,6086,1661,5317,2582,7959,4685,2807,2917,1037,5698,1529,3972,8716,2634,3301,3412,8621,743,8001,4734,888,7744,8092,3671,8941,1487,5658,7099,2781,99,1932,4443,4756,4652,9328,1581,7855,4312,5976,7255,6480,3996,2748,1973,9731,4530,2790,9417,7186,5303,3557,351,7182,9428,1342,9020,7599,1392,8304,2070,9138,7215,2008,9937,1106,7110 +7444,769,9688,632,1571,6820,8743,4338,337,3366,3073,1946,8219,104,4210,6986,249,5061,8693,7960,6546,1004,8857,5997,9352,4338,6105,5008,2556,6518,6694,4345,3727,7956,20,3954,8652,4424,9387,2035,8358,5962,5304,5194,8650,8282,1256,1103,2138,6679,1985,3653,2770,2433,4278,615,2863,1715,242,3790,2636,6998,3088,1671,2239,957,5411,4595,6282,2881,9974,2401,875,7574,2987,4587,3147,6766,9885,2965 +3287,3016,3619,6818,9073,6120,5423,557,2900,2015,8111,3873,1314,4189,1846,4399,7041,7583,2427,2864,3525,5002,2069,748,1948,6015,2684,438,770,8367,1663,7887,7759,1885,157,7770,4520,4878,3857,1137,3525,3050,6276,5569,7649,904,4533,7843,2199,5648,7628,9075,9441,3600,7231,2388,5640,9096,958,3058,584,5899,8150,1181,9616,1098,8162,6819,8171,1519,1140,7665,8801,2632,1299,9192,707,9955,2710,7314 +1772,2963,7578,3541,3095,1488,7026,2634,6015,4633,4370,2762,1650,2174,909,8158,2922,8467,4198,4280,9092,8856,8835,5457,2790,8574,9742,5054,9547,4156,7940,8126,9824,7340,8840,6574,3547,1477,3014,6798,7134,435,9484,9859,3031,4,1502,4133,1738,1807,4825,463,6343,9701,8506,9822,9555,8688,8168,3467,3234,6318,1787,5591,419,6593,7974,8486,9861,6381,6758,194,3061,4315,2863,4665,3789,2201,1492,4416 +126,8927,6608,5682,8986,6867,1715,6076,3159,788,3140,4744,830,9253,5812,5021,7616,8534,1546,9590,1101,9012,9821,8132,7857,4086,1069,7491,2988,1579,2442,4321,2149,7642,6108,250,6086,3167,24,9528,7663,2685,1220,9196,1397,5776,1577,1730,5481,977,6115,199,6326,2183,3767,5928,5586,7561,663,8649,9688,949,5913,9160,1870,5764,9887,4477,6703,1413,4995,5494,7131,2192,8969,7138,3997,8697,646,1028 +8074,1731,8245,624,4601,8706,155,8891,309,2552,8208,8452,2954,3124,3469,4246,3352,1105,4509,8677,9901,4416,8191,9283,5625,7120,2952,8881,7693,830,4580,8228,9459,8611,4499,1179,4988,1394,550,2336,6089,6872,269,7213,1848,917,6672,4890,656,1478,6536,3165,4743,4990,1176,6211,7207,5284,9730,4738,1549,4986,4942,8645,3698,9429,1439,2175,6549,3058,6513,1574,6988,8333,3406,5245,5431,7140,7085,6407 +7845,4694,2530,8249,290,5948,5509,1588,5940,4495,5866,5021,4626,3979,3296,7589,4854,1998,5627,3926,8346,6512,9608,1918,7070,4747,4182,2858,2766,4606,6269,4107,8982,8568,9053,4244,5604,102,2756,727,5887,2566,7922,44,5986,621,1202,374,6988,4130,3627,6744,9443,4568,1398,8679,397,3928,9159,367,2917,6127,5788,3304,8129,911,2669,1463,9749,264,4478,8940,1109,7309,2462,117,4692,7724,225,2312 +4164,3637,2000,941,8903,39,3443,7172,1031,3687,4901,8082,4945,4515,7204,9310,9349,9535,9940,218,1788,9245,2237,1541,5670,6538,6047,5553,9807,8101,1925,8714,445,8332,7309,6830,5786,5736,7306,2710,3034,1838,7969,6318,7912,2584,2080,7437,6705,2254,7428,820,782,9861,7596,3842,3631,8063,5240,6666,394,4565,7865,4895,9890,6028,6117,4724,9156,4473,4552,602,470,6191,4927,5387,884,3146,1978,3000 +4258,6880,1696,3582,5793,4923,2119,1155,9056,9698,6603,3768,5514,9927,9609,6166,6566,4536,4985,4934,8076,9062,6741,6163,7399,4562,2337,5600,2919,9012,8459,1308,6072,1225,9306,8818,5886,7243,7365,8792,6007,9256,6699,7171,4230,7002,8720,7839,4533,1671,478,7774,1607,2317,5437,4705,7886,4760,6760,7271,3081,2997,3088,7675,6208,3101,6821,6840,122,9633,4900,2067,8546,4549,2091,7188,5605,8599,6758,5229 +7854,5243,9155,3556,8812,7047,2202,1541,5993,4600,4760,713,434,7911,7426,7414,8729,322,803,7960,7563,4908,6285,6291,736,3389,9339,4132,8701,7534,5287,3646,592,3065,7582,2592,8755,6068,8597,1982,5782,1894,2900,6236,4039,6569,3037,5837,7698,700,7815,2491,7272,5878,3083,6778,6639,3589,5010,8313,2581,6617,5869,8402,6808,2951,2321,5195,497,2190,6187,1342,1316,4453,7740,4154,2959,1781,1482,8256 +7178,2046,4419,744,8312,5356,6855,8839,319,2962,5662,47,6307,8662,68,4813,567,2712,9931,1678,3101,8227,6533,4933,6656,92,5846,4780,6256,6361,4323,9985,1231,2175,7178,3034,9744,6155,9165,7787,5836,9318,7860,9644,8941,6480,9443,8188,5928,161,6979,2352,5628,6991,1198,8067,5867,6620,3778,8426,2994,3122,3124,6335,3918,8897,2655,9670,634,1088,1576,8935,7255,474,8166,7417,9547,2886,5560,3842 +6957,3111,26,7530,7143,1295,1744,6057,3009,1854,8098,5405,2234,4874,9447,2620,9303,27,7410,969,40,2966,5648,7596,8637,4238,3143,3679,7187,690,9980,7085,7714,9373,5632,7526,6707,3951,9734,4216,2146,3602,5371,6029,3039,4433,4855,4151,1449,3376,8009,7240,7027,4602,2947,9081,4045,8424,9352,8742,923,2705,4266,3232,2264,6761,363,2651,3383,7770,6730,7856,7340,9679,2158,610,4471,4608,910,6241 +4417,6756,1013,8797,658,8809,5032,8703,7541,846,3357,2920,9817,1745,9980,7593,4667,3087,779,3218,6233,5568,4296,2289,2654,7898,5021,9461,5593,8214,9173,4203,2271,7980,2983,5952,9992,8399,3468,1776,3188,9314,1720,6523,2933,621,8685,5483,8986,6163,3444,9539,4320,155,3992,2828,2150,6071,524,2895,5468,8063,1210,3348,9071,4862,483,9017,4097,6186,9815,3610,5048,1644,1003,9865,9332,2145,1944,2213 +9284,3803,4920,1927,6706,4344,7383,4786,9890,2010,5228,1224,3158,6967,8580,8990,8883,5213,76,8306,2031,4980,5639,9519,7184,5645,7769,3259,8077,9130,1317,3096,9624,3818,1770,695,2454,947,6029,3474,9938,3527,5696,4760,7724,7738,2848,6442,5767,6845,8323,4131,2859,7595,2500,4815,3660,9130,8580,7016,8231,4391,8369,3444,4069,4021,556,6154,627,2778,1496,4206,6356,8434,8491,3816,8231,3190,5575,1015 +3787,7572,1788,6803,5641,6844,1961,4811,8535,9914,9999,1450,8857,738,4662,8569,6679,2225,7839,8618,286,2648,5342,2294,3205,4546,176,8705,3741,6134,8324,8021,7004,5205,7032,6637,9442,5539,5584,4819,5874,5807,8589,6871,9016,983,1758,3786,1519,6241,185,8398,495,3370,9133,3051,4549,9674,7311,9738,3316,9383,2658,2776,9481,7558,619,3943,3324,6491,4933,153,9738,4623,912,3595,7771,7939,1219,4405 +2650,3883,4154,5809,315,7756,4430,1788,4451,1631,6461,7230,6017,5751,138,588,5282,2442,9110,9035,6349,2515,1570,6122,4192,4174,3530,1933,4186,4420,4609,5739,4135,2963,6308,1161,8809,8619,2796,3819,6971,8228,4188,1492,909,8048,2328,6772,8467,7671,9068,2226,7579,6422,7056,8042,3296,2272,3006,2196,7320,3238,3490,3102,37,1293,3212,4767,5041,8773,5794,4456,6174,7279,7054,2835,7053,9088,790,6640 +3101,1057,7057,3826,6077,1025,2955,1224,1114,6729,5902,4698,6239,7203,9423,1804,4417,6686,1426,6941,8071,1029,4985,9010,6122,6597,1622,1574,3513,1684,7086,5505,3244,411,9638,4150,907,9135,829,981,1707,5359,8781,9751,5,9131,3973,7159,1340,6955,7514,7993,6964,8198,1933,2797,877,3993,4453,8020,9349,8646,2779,8679,2961,3547,3374,3510,1129,3568,2241,2625,9138,5974,8206,7669,7678,1833,8700,4480 +4865,9912,8038,8238,782,3095,8199,1127,4501,7280,2112,2487,3626,2790,9432,1475,6312,8277,4827,2218,5806,7132,8752,1468,7471,6386,739,8762,8323,8120,5169,9078,9058,3370,9560,7987,8585,8531,5347,9312,1058,4271,1159,5286,5404,6925,8606,9204,7361,2415,560,586,4002,2644,1927,2824,768,4409,2942,3345,1002,808,4941,6267,7979,5140,8643,7553,9438,7320,4938,2666,4609,2778,8158,6730,3748,3867,1866,7181 +171,3771,7134,8927,4778,2913,3326,2004,3089,7853,1378,1729,4777,2706,9578,1360,5693,3036,1851,7248,2403,2273,8536,6501,9216,613,9671,7131,7719,6425,773,717,8803,160,1114,7554,7197,753,4513,4322,8499,4533,2609,4226,8710,6627,644,9666,6260,4870,5744,7385,6542,6203,7703,6130,8944,5589,2262,6803,6381,7414,6888,5123,7320,9392,9061,6780,322,8975,7050,5089,1061,2260,3199,1150,1865,5386,9699,6501 +3744,8454,6885,8277,919,1923,4001,6864,7854,5519,2491,6057,8794,9645,1776,5714,9786,9281,7538,6916,3215,395,2501,9618,4835,8846,9708,2813,3303,1794,8309,7176,2206,1602,1838,236,4593,2245,8993,4017,10,8215,6921,5206,4023,5932,6997,7801,262,7640,3107,8275,4938,7822,2425,3223,3886,2105,8700,9526,2088,8662,8034,7004,5710,2124,7164,3574,6630,9980,4242,2901,9471,1491,2117,4562,1130,9086,4117,6698 +2810,2280,2331,1170,4554,4071,8387,1215,2274,9848,6738,1604,7281,8805,439,1298,8318,7834,9426,8603,6092,7944,1309,8828,303,3157,4638,4439,9175,1921,4695,7716,1494,1015,1772,5913,1127,1952,1950,8905,4064,9890,385,9357,7945,5035,7082,5369,4093,6546,5187,5637,2041,8946,1758,7111,6566,1027,1049,5148,7224,7248,296,6169,375,1656,7993,2816,3717,4279,4675,1609,3317,42,6201,3100,3144,163,9530,4531 +7096,6070,1009,4988,3538,5801,7149,3063,2324,2912,7911,7002,4338,7880,2481,7368,3516,2016,7556,2193,1388,3865,8125,4637,4096,8114,750,3144,1938,7002,9343,4095,1392,4220,3455,6969,9647,1321,9048,1996,1640,6626,1788,314,9578,6630,2813,6626,4981,9908,7024,4355,3201,3521,3864,3303,464,1923,595,9801,3391,8366,8084,9374,1041,8807,9085,1892,9431,8317,9016,9221,8574,9981,9240,5395,2009,6310,2854,9255 +8830,3145,2960,9615,8220,6061,3452,2918,6481,9278,2297,3385,6565,7066,7316,5682,107,7646,4466,68,1952,9603,8615,54,7191,791,6833,2560,693,9733,4168,570,9127,9537,1925,8287,5508,4297,8452,8795,6213,7994,2420,4208,524,5915,8602,8330,2651,8547,6156,1812,6271,7991,9407,9804,1553,6866,1128,2119,4691,9711,8315,5879,9935,6900,482,682,4126,1041,428,6247,3720,5882,7526,2582,4327,7725,3503,2631 +2738,9323,721,7434,1453,6294,2957,3786,5722,6019,8685,4386,3066,9057,6860,499,5315,3045,5194,7111,3137,9104,941,586,3066,755,4177,8819,7040,5309,3583,3897,4428,7788,4721,7249,6559,7324,825,7311,3760,6064,6070,9672,4882,584,1365,9739,9331,5783,2624,7889,1604,1303,1555,7125,8312,425,8936,3233,7724,1480,403,7440,1784,1754,4721,1569,652,3893,4574,5692,9730,4813,9844,8291,9199,7101,3391,8914 +6044,2928,9332,3328,8588,447,3830,1176,3523,2705,8365,6136,5442,9049,5526,8575,8869,9031,7280,706,2794,8814,5767,4241,7696,78,6570,556,5083,1426,4502,3336,9518,2292,1885,3740,3153,9348,9331,8051,2759,5407,9028,7840,9255,831,515,2612,9747,7435,8964,4971,2048,4900,5967,8271,1719,9670,2810,6777,1594,6367,6259,8316,3815,1689,6840,9437,4361,822,9619,3065,83,6344,7486,8657,8228,9635,6932,4864 +8478,4777,6334,4678,7476,4963,6735,3096,5860,1405,5127,7269,7793,4738,227,9168,2996,8928,765,733,1276,7677,6258,1528,9558,3329,302,8901,1422,8277,6340,645,9125,8869,5952,141,8141,1816,9635,4025,4184,3093,83,2344,2747,9352,7966,1206,1126,1826,218,7939,2957,2729,810,8752,5247,4174,4038,8884,7899,9567,301,5265,5752,7524,4381,1669,3106,8270,6228,6373,754,2547,4240,2313,5514,3022,1040,9738 +2265,8192,1763,1369,8469,8789,4836,52,1212,6690,5257,8918,6723,6319,378,4039,2421,8555,8184,9577,1432,7139,8078,5452,9628,7579,4161,7490,5159,8559,1011,81,478,5840,1964,1334,6875,8670,9900,739,1514,8692,522,9316,6955,1345,8132,2277,3193,9773,3923,4177,2183,1236,6747,6575,4874,6003,6409,8187,745,8776,9440,7543,9825,2582,7381,8147,7236,5185,7564,6125,218,7991,6394,391,7659,7456,5128,5294 +2132,8992,8160,5782,4420,3371,3798,5054,552,5631,7546,4716,1332,6486,7892,7441,4370,6231,4579,2121,8615,1145,9391,1524,1385,2400,9437,2454,7896,7467,2928,8400,3299,4025,7458,4703,7206,6358,792,6200,725,4275,4136,7390,5984,4502,7929,5085,8176,4600,119,3568,76,9363,6943,2248,9077,9731,6213,5817,6729,4190,3092,6910,759,2682,8380,1254,9604,3011,9291,5329,9453,9746,2739,6522,3765,5634,1113,5789 +5304,5499,564,2801,679,2653,1783,3608,7359,7797,3284,796,3222,437,7185,6135,8571,2778,7488,5746,678,6140,861,7750,803,9859,9918,2425,3734,2698,9005,4864,9818,6743,2475,132,9486,3825,5472,919,292,4411,7213,7699,6435,9019,6769,1388,802,2124,1345,8493,9487,8558,7061,8777,8833,2427,2238,5409,4957,8503,3171,7622,5779,6145,2417,5873,5563,5693,9574,9491,1937,7384,4563,6842,5432,2751,3406,7981 diff --git a/project_euler/problem_081/sol1.py b/project_euler/problem_081/sol1.py new file mode 100644 index 000000000000..afa143f23b33 --- /dev/null +++ b/project_euler/problem_081/sol1.py @@ -0,0 +1,47 @@ +""" +Problem 81: https://projecteuler.net/problem=81 +In the 5 by 5 matrix below, the minimal path sum from the top left to the bottom right, +by only moving to the right and down, is indicated in bold red and is equal to 2427. + + [131] 673 234 103 18 + [201] [96] [342] 965 150 + 630 803 [746] [422] 111 + 537 699 497 [121] 956 + 805 732 524 [37] [331] + +Find the minimal path sum from the top left to the bottom right by only moving right +and down in matrix.txt (https://projecteuler.net/project/resources/p081_matrix.txt), +a 31K text file containing an 80 by 80 matrix. +""" +import os + + +def solution(filename: str = "matrix.txt") -> int: + """ + Returns the minimal path sum from the top left to the bottom right of the matrix. + >>> solution() + 427337 + """ + with open(os.path.join(os.path.dirname(__file__), filename), "r") as in_file: + data = in_file.read() + + grid = [[int(cell) for cell in row.split(",")] for row in data.strip().splitlines()] + dp = [[0 for cell in row] for row in grid] + n = len(grid[0]) + + dp = [[0 for i in range(n)] for j in range(n)] + dp[0][0] = grid[0][0] + for i in range(1, n): + dp[0][i] = grid[0][i] + dp[0][i - 1] + for i in range(1, n): + dp[i][0] = grid[i][0] + dp[i - 1][0] + + for i in range(1, n): + for j in range(1, n): + dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1]) + + return dp[-1][-1] + + +if __name__ == "__main__": + print(f"{solution() = }") From c6a9da60863179e6a5436b0c4c6478c9ddcacef1 Mon Sep 17 00:00:00 2001 From: Phil Bazun Date: Sun, 25 Oct 2020 10:24:35 +0100 Subject: [PATCH 032/195] Add single bit manipulation operations. (#3284) * Add single bit manipuation operations. * fixup! Add single bit manipuation operations. * Change wording. --- .../single_bit_manipulation_operations.py | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 bit_manipulation/single_bit_manipulation_operations.py diff --git a/bit_manipulation/single_bit_manipulation_operations.py b/bit_manipulation/single_bit_manipulation_operations.py new file mode 100644 index 000000000000..114eafe3235b --- /dev/null +++ b/bit_manipulation/single_bit_manipulation_operations.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +"""Provide the functionality to manipulate a single bit.""" + + +def set_bit(number: int, position: int): + """ + Set the bit at position to 1. + + Details: perform bitwise or for given number and X. + Where X is a number with all the bits – zeroes and bit on given + position – one. + + >>> set_bit(0b1101, 1) # 0b1111 + 15 + >>> set_bit(0b0, 5) # 0b100000 + 32 + >>> set_bit(0b1111, 1) # 0b1111 + 15 + """ + return number | (1 << position) + + +def clear_bit(number: int, position: int): + """ + Set the bit at position to 0. + + Details: perform bitwise and for given number and X. + Where X is a number with all the bits – ones and bit on given + position – zero. + + >>> clear_bit(0b10010, 1) # 0b10000 + 16 + >>> clear_bit(0b0, 5) # 0b0 + 0 + """ + return number & ~(1 << position) + + +def flip_bit(number: int, position: int): + """ + Flip the bit at position. + + Details: perform bitwise xor for given number and X. + Where X is a number with all the bits – zeroes and bit on given + position – one. + + >>> flip_bit(0b101, 1) # 0b111 + 7 + >>> flip_bit(0b101, 0) # 0b100 + 4 + """ + return number ^ (1 << position) + + +def is_bit_set(number: int, position: int) -> bool: + """ + Is the bit at position set? + + Details: Shift the bit at position to be the first (smallest) bit. + Then check if the first bit is set by anding the shifted number with 1. + + >>> is_bit_set(0b1010, 0) + False + >>> is_bit_set(0b1010, 1) + True + >>> is_bit_set(0b1010, 2) + False + >>> is_bit_set(0b1010, 3) + True + >>> is_bit_set(0b0, 17) + False + """ + return ((number >> position) & 1) == 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 9dda6cc062a282710f1b9e3560506ad2251fce4f Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Mon, 26 Oct 2020 00:02:24 +0800 Subject: [PATCH 033/195] Update ceil and floor function (#3710) * Update ceil and floor function * add end line * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 ++++- maths/ceil.py | 9 ++++++--- maths/floor.py | 11 ++++++----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f0f494e7a3b4..6f14f74fd0f7 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -340,6 +340,8 @@ * [Astar](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/astar.py) * [Data Transformations](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/data_transformations.py) * [Decision Tree](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/decision_tree.py) + * Forecasting + * [Run](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/forecasting/run.py) * [Gaussian Naive Bayes](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/gaussian_naive_bayes.py) * [Gradient Boosting Regressor](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/gradient_boosting_regressor.py) * [Gradient Descent](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/gradient_descent.py) @@ -630,6 +632,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_036/sol1.py) * Problem 037 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_037/sol1.py) + * Problem 038 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_038/sol1.py) * Problem 039 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_039/sol1.py) * Problem 040 @@ -716,7 +720,6 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_234/sol1.py) * Problem 551 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_551/sol1.py) - * [Validate Solutions](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py) ## Quantum * [Deutsch Jozsa](https://github.com/TheAlgorithms/Python/blob/master/quantum/deutsch_jozsa.py) diff --git a/maths/ceil.py b/maths/ceil.py index ac86798a357f..97578265c1a9 100644 --- a/maths/ceil.py +++ b/maths/ceil.py @@ -1,3 +1,8 @@ +""" +https://en.wikipedia.org/wiki/Floor_and_ceiling_functions +""" + + def ceil(x) -> int: """ Return the ceiling of x as an Integral. @@ -10,9 +15,7 @@ def ceil(x) -> int: ... in (1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0, 1_000_000_000)) True """ - return ( - x if isinstance(x, int) or x - int(x) == 0 else int(x + 1) if x > 0 else int(x) - ) + return int(x) if x - int(x) <= 0 else int(x) + 1 if __name__ == "__main__": diff --git a/maths/floor.py b/maths/floor.py index 41bd5ecb3cd7..482250f5e59e 100644 --- a/maths/floor.py +++ b/maths/floor.py @@ -1,18 +1,19 @@ +""" +https://en.wikipedia.org/wiki/Floor_and_ceiling_functions +""" + + def floor(x) -> int: """ Return the floor of x as an Integral. - :param x: the number :return: the largest integer <= x. - >>> import math >>> all(floor(n) == math.floor(n) for n ... in (1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0, 1_000_000_000)) True """ - return ( - x if isinstance(x, int) or x - int(x) == 0 else int(x) if x > 0 else int(x - 1) - ) + return int(x) if x - int(x) >= 0 else int(x) - 1 if __name__ == "__main__": From 20b7f3d6a175c6984168810e6e1818939dd48024 Mon Sep 17 00:00:00 2001 From: Kushagra Bansal Date: Mon, 26 Oct 2020 09:37:11 +0530 Subject: [PATCH 034/195] Update lucas_series.py to include another method (#3620) * Update lucas_series.py Added another method to calculate lucas_numbers * Fix pre-commit error * Update lucas_series.py * Update lucas_series.py * Update lucas_series.py * Update lucas_series.py --- maths/lucas_series.py | 65 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/maths/lucas_series.py b/maths/lucas_series.py index 22ad893a6567..02eae8d8c658 100644 --- a/maths/lucas_series.py +++ b/maths/lucas_series.py @@ -1,22 +1,69 @@ -# Lucas Sequence Using Recursion +""" +https://en.wikipedia.org/wiki/Lucas_number +""" -def recur_luc(n): +def recursive_lucas_number(n): """ - >>> recur_luc(1) + Returns the nth lucas number + >>> recursive_lucas_number(1) 1 - >>> recur_luc(0) + >>> recursive_lucas_number(20) + 15127 + >>> recursive_lucas_number(0) 2 + >>> recursive_lucas_number(25) + 167761 + >>> recursive_lucas_number(-1.5) + Traceback (most recent call last): + ... + TypeError: recursive_lucas_number accepts only integer arguments. """ if n == 1: return n if n == 0: return 2 - return recur_luc(n - 1) + recur_luc(n - 2) + if not isinstance(n, int): + raise TypeError("recursive_lucas_number accepts only integer arguments.") + + return recursive_lucas_number(n - 1) + recursive_lucas_number(n - 2) + + +def dynamic_lucas_number(n: int) -> int: + """ + Returns the nth lucas number + >>> dynamic_lucas_number(1) + 1 + >>> dynamic_lucas_number(20) + 15127 + >>> dynamic_lucas_number(0) + 2 + >>> dynamic_lucas_number(25) + 167761 + >>> dynamic_lucas_number(-1.5) + Traceback (most recent call last): + ... + TypeError: dynamic_lucas_number accepts only integer arguments. + """ + if not isinstance(n, int): + raise TypeError("dynamic_lucas_number accepts only integer arguments.") + if n == 0: + return 2 + if n == 1: + return 1 + a, b = 2, 1 + for i in range(n): + a, b = b, a + b + return a if __name__ == "__main__": - limit = int(input("How many terms to include in Lucas series:")) - print("Lucas series:") - for i in range(limit): - print(recur_luc(i)) + from doctest import testmod + + testmod() + n = int(input("Enter the number of terms in lucas series:\n").strip()) + n = int(input("Enter the number of terms in lucas series:\n").strip()) + print("Using recursive function to calculate lucas series:") + print(" ".join(str(recursive_lucas_number(i)) for i in range(n))) + print("\nUsing dynamic function to calculate lucas series:") + print(" ".join(str(dynamic_lucas_number(i)) for i in range(n))) From 2ac63910ad0f456997474f10181130b7048186f7 Mon Sep 17 00:00:00 2001 From: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:05:49 +0530 Subject: [PATCH 035/195] Update CONTRIBUTING.md (#3698) * Update CONTRIBUTING.md Needed to tell people so we do not receive any duplicate solution. Do not count this as hactoberfest-accepted * Update CONTRIBUTING.md * Update CONTRIBUTING.md typo fix Co-authored-by: Du Yuanchao * Update CONTRIBUTING.md Co-authored-by: Du Yuanchao Co-authored-by: John Law --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e248d09f11c3..bfad76a65b61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ We are very happy that you consider implementing algorithms and data structure f - Your work will be distributed under [MIT License](LICENSE.md) once your pull request is merged - You submitted work fulfils or mostly fulfils our styles and standards -**New implementation** is welcome! For example, new solutions for a problem, different representations for a graph data structure or algorithm designs with different complexity. +**New implementation** is welcome! For example, new solutions for a problem, different representations for a graph data structure or algorithm designs with different complexity but **identical implementation** of an existing implementation is not allowed. Please check whether the solution is already implemented or not before submitting your pull request. **Improving comments** and **writing proper tests** are also highly welcome. From b7a0d342d8ea86b3dbefee597e90e98b3ed81c70 Mon Sep 17 00:00:00 2001 From: Rolv Apneseth Date: Mon, 26 Oct 2020 06:48:06 +0000 Subject: [PATCH 036/195] Made improvements to combinations.py (#3681) * Made improvements to combinations.py * Update maths/combinations.py Co-authored-by: Du Yuanchao * Function now raises an error when given invalid input * Update maths/combinations.py Co-authored-by: Du Yuanchao --- maths/combinations.py | 45 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/maths/combinations.py b/maths/combinations.py index fd98992e6c16..40f4f7a9f850 100644 --- a/maths/combinations.py +++ b/maths/combinations.py @@ -1,19 +1,58 @@ +""" +https://en.wikipedia.org/wiki/Combination +""" from math import factorial -def combinations(n, k): +def combinations(n: int, k: int) -> int: """ + Returns the number of different combinations of k length which can + be made from n values, where n >= k. + + Examples: >>> combinations(10,5) 252 + >>> combinations(6,3) 20 + >>> combinations(20,5) 15504 + + >>> combinations(52, 5) + 2598960 + + >>> combinations(0, 0) + 1 + + >>> combinations(-4, -5) + ... + Traceback (most recent call last): + ValueError: Please enter positive integers for n and k where n >= k """ + + # If either of the conditions are true, the function is being asked + # to calculate a factorial of a negative number, which is not possible + if n < k or k < 0: + raise ValueError("Please enter positive integers for n and k where n >= k") return int(factorial(n) / ((factorial(k)) * (factorial(n - k)))) if __name__ == "__main__": - from doctest import testmod - testmod() + print( + "\nThe number of five-card hands possible from a standard", + f"fifty-two card deck is: {combinations(52, 5)}", + ) + + print( + "\nIf a class of 40 students must be arranged into groups of", + f"4 for group projects, there are {combinations(40, 4)} ways", + "to arrange them.\n", + ) + + print( + "If 10 teams are competing in a Formula One race, there", + f"are {combinations(10, 3)} ways that first, second and", + "third place can be awarded.\n", + ) From 0d033839f9a8f38589975b5658270f558b98bf8a Mon Sep 17 00:00:00 2001 From: Gaurav Chaudhari Date: Mon, 26 Oct 2020 12:39:33 +0530 Subject: [PATCH 037/195] fixes: #2969 (#3756) Signed-off-by: Gaurav Chaudhari --- .github/stale.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 22aae982abdc..000000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 30 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - bug - - help wanted - - OK to merge -# Label to use when marking an issue as stale -staleLabel: wontfix -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: > - Please reopen this issue once you commit the changes requested or - make improvements on the code. Thank you for your contributions. From b20415bac5a91047b0f0d89b405518c82694f21d Mon Sep 17 00:00:00 2001 From: Shabab Karim Date: Mon, 26 Oct 2020 16:08:53 +0600 Subject: [PATCH 038/195] Added two pointer solution for two sum problem (#3468) --- other/two_pointer.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 other/two_pointer.py diff --git a/other/two_pointer.py b/other/two_pointer.py new file mode 100644 index 000000000000..ff234cddc9e4 --- /dev/null +++ b/other/two_pointer.py @@ -0,0 +1,61 @@ +""" +Given a sorted array of integers, return indices of the two numbers such +that they add up to a specific target using the two pointers technique. + +You may assume that each input would have exactly one solution, and you +may not use the same element twice. + +This is an alternative solution of the two-sum problem, which uses a +map to solve the problem. Hence can not solve the issue if there is a +constraint not use the same index twice. [1] + +Example: +Given nums = [2, 7, 11, 15], target = 9, + +Because nums[0] + nums[1] = 2 + 7 = 9, +return [0, 1]. + +[1]: https://github.com/TheAlgorithms/Python/blob/master/other/two_sum.py +""" +from __future__ import annotations + + +def two_pointer(nums: list[int], target: int) -> list[int]: + """ + >>> two_pointer([2, 7, 11, 15], 9) + [0, 1] + >>> two_pointer([2, 7, 11, 15], 17) + [0, 3] + >>> two_pointer([2, 7, 11, 15], 18) + [1, 2] + >>> two_pointer([2, 7, 11, 15], 26) + [2, 3] + >>> two_pointer([1, 3, 3], 6) + [1, 2] + >>> two_pointer([2, 7, 11, 15], 8) + [] + >>> two_pointer([3 * i for i in range(10)], 19) + [] + >>> two_pointer([1, 2, 3], 6) + [] + """ + i = 0 + j = len(nums) - 1 + + while i < j: + + if nums[i] + nums[j] == target: + return [i, j] + elif nums[i] + nums[j] < target: + i = i + 1 + else: + j = j - 1 + + return [] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{two_pointer([2, 7, 11, 15], 9) = }") From 11273934a5aca2bf94d554341debb93663fc7944 Mon Sep 17 00:00:00 2001 From: John Law Date: Mon, 26 Oct 2020 23:48:57 +0800 Subject: [PATCH 039/195] Fix Project Euler Readme (#3754) * Fix Project Euler Readme * updating DIRECTORY.md * Update CONTRIBUTING.md * spacing Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- DIRECTORY.md | 4 ++++ project_euler/README.md | 14 +++++++------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bfad76a65b61..eedcb0250169 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,7 +148,7 @@ We want your work to be readable by others; therefore, we encourage you to note - If you need a third party module that is not in the file __requirements.txt__, please add it to that file as part of your submission. #### Other Requirements for Submissions - +- If you are submitting code in the `project_euler/` directory, please also read [the dedicated Guideline](https://github.com/TheAlgorithms/Python/blob/master/project_euler/README.md) before contributing to our Project Euler library. - The file extension for code files should be `.py`. Jupyter Notebooks should be submitted to [TheAlgorithms/Jupyter](https://github.com/TheAlgorithms/Jupyter). - Strictly use snake_case (underscore_separated) in your file_name, as it will be easy to parse in future using scripts. - Please avoid creating new directories if at all possible. Try to fit your work into the existing directory structure. diff --git a/DIRECTORY.md b/DIRECTORY.md index 6f14f74fd0f7..c3b32b1ab754 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -28,6 +28,7 @@ * [Binary And Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_and_operator.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) + * [Single Bit Manipulation Operations](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/single_bit_manipulation_operations.py) ## Blockchain * [Chinese Remainder Theorem](https://github.com/TheAlgorithms/Python/blob/master/blockchain/chinese_remainder_theorem.py) @@ -265,6 +266,7 @@ * [Basic Graphs](https://github.com/TheAlgorithms/Python/blob/master/graphs/basic_graphs.py) * [Bellman Ford](https://github.com/TheAlgorithms/Python/blob/master/graphs/bellman_ford.py) * [Bfs Shortest Path](https://github.com/TheAlgorithms/Python/blob/master/graphs/bfs_shortest_path.py) + * [Bfs Zero One Shortest Path](https://github.com/TheAlgorithms/Python/blob/master/graphs/bfs_zero_one_shortest_path.py) * [Bidirectional A Star](https://github.com/TheAlgorithms/Python/blob/master/graphs/bidirectional_a_star.py) * [Bidirectional Breadth First Search](https://github.com/TheAlgorithms/Python/blob/master/graphs/bidirectional_breadth_first_search.py) * [Breadth First Search](https://github.com/TheAlgorithms/Python/blob/master/graphs/breadth_first_search.py) @@ -694,6 +696,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_076/sol1.py) * Problem 080 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) + * Problem 081 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) * Problem 091 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_091/sol1.py) * Problem 097 diff --git a/project_euler/README.md b/project_euler/README.md index 934e541cc067..1cc6f8150e38 100644 --- a/project_euler/README.md +++ b/project_euler/README.md @@ -1,11 +1,11 @@ # Project Euler -Problems are taken from https://projecteuler.net/. +Problems are taken from https://projecteuler.net/, the Project Euler. [Problems are licensed under CC BY-NC-SA 4.0](https://projecteuler.net/copyright). -Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical +Project Euler is a series of challenging mathematical/computer programming problems that require more than just mathematical insights to solve. Project Euler is ideal for mathematicians who are learning to code. -The solutions will be checked by our [automated testing on Travis CI](https://travis-ci.com/github/TheAlgorithms/Python/pull_requests) with the help of [this script](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py). The efficiency of your code is also checked. You can view the top 10 slowest solutions on Travis CI logs and open a pull request to improve those solutions. +The solutions will be checked by our [automated testing on Travis CI](https://travis-ci.com/github/TheAlgorithms/Python/pull_requests) with the help of [this script](https://github.com/TheAlgorithms/Python/blob/master/scripts/validate_solutions.py). The efficiency of your code is also checked. You can view the top 10 slowest solutions on Travis CI logs (under `slowest 10 durations`) and open a pull request to improve those solutions. ## Solution Guidelines @@ -17,18 +17,18 @@ Welcome to [TheAlgorithms/Python](https://github.com/TheAlgorithms/Python)! Befo * Please maintain consistency in project directory and solution file names. Keep the following points in mind: * Create a new directory only for the problems which do not exist yet. * If you create a new directory, please create an empty `__init__.py` file inside it as well. - * Please name the project directory as `problem_` where `problem_number` should be filled with 0s so as to occupy 3 digits. Example: `problem_001`, `problem_002`, `problem_067`, `problem_145`, and so on. + * Please name the project **directory** as `problem_` where `problem_number` should be filled with 0s so as to occupy 3 digits. Example: `problem_001`, `problem_002`, `problem_067`, `problem_145`, and so on. -* Please provide a link to the problem and other references, if used, in the module-level docstring. +* Please provide a link to the problem and other references, if used, in the **module-level docstring**. * All imports should come ***after*** the module-level docstring. * You can have as many helper functions as you want but there should be one main function called `solution` which should satisfy the conditions as stated below: - * It should contain positional argument(s) whose default value is the question input. Example: Please take a look at [problem 1](https://projecteuler.net/problem=1) where the question is to *Find the sum of all the multiples of 3 or 5 below 1000.* In this case the main solution function will be `solution(limit: int = 1000)`. + * It should contain positional argument(s) whose default value is the question input. Example: Please take a look at [Problem 1](https://projecteuler.net/problem=1) where the question is to *Find the sum of all the multiples of 3 or 5 below 1000.* In this case the main solution function will be `solution(limit: int = 1000)`. * When the `solution` function is called without any arguments like so: `solution()`, it should return the answer to the problem. * Every function, which includes all the helper functions, if any, and the main solution function, should have `doctest` in the function docstring along with a brief statement mentioning what the function is about. - * There should not be a `doctest` for testing the answer as that is done by our Travis CI build using this [script](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py). Keeping in mind the above example of [problem 1](https://projecteuler.net/problem=1): + * There should not be a `doctest` for testing the answer as that is done by our Travis CI build using this [script](https://github.com/TheAlgorithms/Python/blob/master/project_euler/validate_solutions.py). Keeping in mind the above example of [Problem 1](https://projecteuler.net/problem=1): ```python def solution(limit: int = 1000): From bca3c72c4b9048aa023ba544319375d62822e03f Mon Sep 17 00:00:00 2001 From: Snimerjot Singh Date: Tue, 27 Oct 2020 09:35:37 +0530 Subject: [PATCH 040/195] Added reverse_letters.py (#3730) * Added reverse_letters.py * Update strings/reverse_letters.py Co-authored-by: Du Yuanchao Co-authored-by: Du Yuanchao --- strings/reverse_letters.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 strings/reverse_letters.py diff --git a/strings/reverse_letters.py b/strings/reverse_letters.py new file mode 100644 index 000000000000..10b8a6d72a0f --- /dev/null +++ b/strings/reverse_letters.py @@ -0,0 +1,19 @@ +def reverse_letters(input_str: str) -> str: + """ + Reverses letters in a given string without adjusting the position of the words + >>> reverse_letters('The cat in the hat') + 'ehT tac ni eht tah' + >>> reverse_letters('The quick brown fox jumped over the lazy dog.') + 'ehT kciuq nworb xof depmuj revo eht yzal .god' + >>> reverse_letters('Is this true?') + 'sI siht ?eurt' + >>> reverse_letters("I love Python") + 'I evol nohtyP' + """ + return " ".join([word[::-1] for word in input_str.split()]) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 584fe56267c4cc66307de74d02eeb12b7f5c9179 Mon Sep 17 00:00:00 2001 From: Lewis Tian Date: Thu, 29 Oct 2020 08:35:31 +0800 Subject: [PATCH 041/195] Update graphs/depth_first_search_2.py (#3799) - update naming style to snake_case - add type hints --- graphs/depth_first_search_2.py | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/graphs/depth_first_search_2.py b/graphs/depth_first_search_2.py index c932e76293ed..3072d527c1c7 100644 --- a/graphs/depth_first_search_2.py +++ b/graphs/depth_first_search_2.py @@ -8,58 +8,58 @@ def __init__(self): self.vertex = {} # for printing the Graph vertices - def printGraph(self): + def print_graph(self) -> None: print(self.vertex) - for i in self.vertex.keys(): + for i in self.vertex: print(i, " -> ", " -> ".join([str(j) for j in self.vertex[i]])) # for adding the edge between two vertices - def addEdge(self, fromVertex, toVertex): + def add_edge(self, from_vertex: int, to_vertex: int) -> None: # check if vertex is already present, - if fromVertex in self.vertex.keys(): - self.vertex[fromVertex].append(toVertex) + if from_vertex in self.vertex: + self.vertex[from_vertex].append(to_vertex) else: # else make a new vertex - self.vertex[fromVertex] = [toVertex] + self.vertex[from_vertex] = [to_vertex] - def DFS(self): + def dfs(self) -> None: # visited array for storing already visited nodes visited = [False] * len(self.vertex) # call the recursive helper function for i in range(len(self.vertex)): - if visited[i] is False: - self.DFSRec(i, visited) + if not visited[i]: + self.dfs_recursive(i, visited) - def DFSRec(self, startVertex, visited): + def dfs_recursive(self, start_vertex: int, visited: list) -> None: # mark start vertex as visited - visited[startVertex] = True + visited[start_vertex] = True - print(startVertex, end=" ") + print(start_vertex, end=" ") # Recur for all the vertices that are adjacent to this node - for i in self.vertex.keys(): - if visited[i] is False: - self.DFSRec(i, visited) + for i in self.vertex: + if not visited[i]: + self.dfs_recursive(i, visited) if __name__ == "__main__": g = Graph() - g.addEdge(0, 1) - g.addEdge(0, 2) - g.addEdge(1, 2) - g.addEdge(2, 0) - g.addEdge(2, 3) - g.addEdge(3, 3) + g.add_edge(0, 1) + g.add_edge(0, 2) + g.add_edge(1, 2) + g.add_edge(2, 0) + g.add_edge(2, 3) + g.add_edge(3, 3) - g.printGraph() + g.print_graph() print("DFS:") - g.DFS() + g.dfs() # OUTPUT: - # 0  ->  1 -> 2 - # 1  ->  2 - # 2  ->  0 -> 3 - # 3  ->  3 + # 0 -> 1 -> 2 + # 1 -> 2 + # 2 -> 0 -> 3 + # 3 -> 3 # DFS: - #  0 1 2 3 + # 0 1 2 3 From dc1a7c362ffcebeea8975303e3b8254d1c4440ac Mon Sep 17 00:00:00 2001 From: Abhinand C <44578852+abhinand-c@users.noreply.github.com> Date: Thu, 29 Oct 2020 06:13:34 +0530 Subject: [PATCH 042/195] Add IBM Qiskit References (#2561) * Added IBM Qiskit References * space Co-authored-by: John Law --- quantum/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/quantum/README.md b/quantum/README.md index be5bd0843f4f..423d34fa3364 100644 --- a/quantum/README.md +++ b/quantum/README.md @@ -6,3 +6,10 @@ Started at https://github.com/TheAlgorithms/Python/issues/1831 * Google: https://research.google/teams/applied-science/quantum * IBM: https://qiskit.org and https://github.com/Qiskit * Rigetti: https://rigetti.com and https://github.com/rigetti + +## IBM Qiskit +- Start using by installing `pip install qiskit`, refer the [docs](https://qiskit.org/documentation/install.html) for more info. +- Tutorials & References + - https://github.com/Qiskit/qiskit-tutorials + - https://quantum-computing.ibm.com/docs/iql/first-circuit + - https://medium.com/qiskit/how-to-program-a-quantum-computer-982a9329ed02 From 6a7201c03eef36e2f44037c477d6044550b4a525 Mon Sep 17 00:00:00 2001 From: Simon Lammer Date: Thu, 29 Oct 2020 01:46:16 +0100 Subject: [PATCH 043/195] Implement the melkman anlgorithm for computing convex hulls (#2916) * Implement the melkman anlgorithm for computing convex hulls * Link melkman algorithm description * Format melkman algorithm code * Add type hints to functions * Fix build errors --- divide_and_conquer/convex_hull.py | 141 ++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 36 deletions(-) diff --git a/divide_and_conquer/convex_hull.py b/divide_and_conquer/convex_hull.py index cf2c7f835798..9c096f671385 100644 --- a/divide_and_conquer/convex_hull.py +++ b/divide_and_conquer/convex_hull.py @@ -13,6 +13,8 @@ """ +from typing import Iterable, List, Set, Union + class Point: """ @@ -81,7 +83,9 @@ def __hash__(self): return hash(self.x) -def _construct_points(list_of_tuples): +def _construct_points( + list_of_tuples: Union[List[Point], List[List[float]], Iterable[List[float]]] +) -> List[Point]: """ constructs a list of points from an array-like object of numbers @@ -110,20 +114,23 @@ def _construct_points(list_of_tuples): [] """ - points = [] + points: List[Point] = [] if list_of_tuples: for p in list_of_tuples: - try: - points.append(Point(p[0], p[1])) - except (IndexError, TypeError): - print( - f"Ignoring deformed point {p}. All points" - " must have at least 2 coordinates." - ) + if isinstance(p, Point): + points.append(p) + else: + try: + points.append(Point(p[0], p[1])) + except (IndexError, TypeError): + print( + f"Ignoring deformed point {p}. All points" + " must have at least 2 coordinates." + ) return points -def _validate_input(points): +def _validate_input(points: Union[List[Point], List[List[float]]]) -> List[Point]: """ validates an input instance before a convex-hull algorithms uses it @@ -165,33 +172,18 @@ def _validate_input(points): ValueError: Expecting an iterable object but got an non-iterable type 1 """ + if not hasattr(points, "__iter__"): + raise ValueError( + f"Expecting an iterable object but got an non-iterable type {points}" + ) + if not points: raise ValueError(f"Expecting a list of points but got {points}") - if isinstance(points, set): - points = list(points) - - try: - if hasattr(points, "__iter__") and not isinstance(points[0], Point): - if isinstance(points[0], (list, tuple)): - points = _construct_points(points) - else: - raise ValueError( - "Expecting an iterable of type Point, list or tuple. " - f"Found objects of type {type(points[0])} instead" - ) - elif not hasattr(points, "__iter__"): - raise ValueError( - f"Expecting an iterable object but got an non-iterable type {points}" - ) - except TypeError: - print("Expecting an iterable of type Point, list or tuple.") - raise - - return points + return _construct_points(points) -def _det(a, b, c): +def _det(a: Point, b: Point, c: Point) -> float: """ Computes the sign perpendicular distance of a 2d point c from a line segment ab. The sign indicates the direction of c relative to ab. @@ -226,7 +218,7 @@ def _det(a, b, c): return det -def convex_hull_bf(points): +def convex_hull_bf(points: List[Point]) -> List[Point]: """ Constructs the convex hull of a set of 2D points using a brute force algorithm. The algorithm basically considers all combinations of points (i, j) and uses the @@ -299,7 +291,7 @@ def convex_hull_bf(points): return sorted(convex_set) -def convex_hull_recursive(points): +def convex_hull_recursive(points: List[Point]) -> List[Point]: """ Constructs the convex hull of a set of 2D points using a divide-and-conquer strategy The algorithm exploits the geometric properties of the problem by repeatedly @@ -369,7 +361,9 @@ def convex_hull_recursive(points): return sorted(convex_set) -def _construct_hull(points, left, right, convex_set): +def _construct_hull( + points: List[Point], left: Point, right: Point, convex_set: Set[Point] +) -> None: """ Parameters @@ -411,6 +405,77 @@ def _construct_hull(points, left, right, convex_set): _construct_hull(candidate_points, extreme_point, right, convex_set) +def convex_hull_melkman(points: List[Point]) -> List[Point]: + """ + Constructs the convex hull of a set of 2D points using the melkman algorithm. + The algorithm works by iteratively inserting points of a simple polygonal chain + (meaning that no line segments between two consecutive points cross each other). + Sorting the points yields such a polygonal chain. + + For a detailed description, see http://cgm.cs.mcgill.ca/~athens/cs601/Melkman.html + + Runtime: O(n log n) - O(n) if points are already sorted in the input + + Parameters + --------- + points: array-like of object of Points, lists or tuples. + The set of 2d points for which the convex-hull is needed + + Returns + ------ + convex_set: list, the convex-hull of points sorted in non-decreasing order. + + See Also + -------- + + Examples + --------- + >>> convex_hull_melkman([[0, 0], [1, 0], [10, 1]]) + [(0.0, 0.0), (1.0, 0.0), (10.0, 1.0)] + >>> convex_hull_melkman([[0, 0], [1, 0], [10, 0]]) + [(0.0, 0.0), (10.0, 0.0)] + >>> convex_hull_melkman([[-1, 1],[-1, -1], [0, 0], [0.5, 0.5], [1, -1], [1, 1], + ... [-0.75, 1]]) + [(-1.0, -1.0), (-1.0, 1.0), (1.0, -1.0), (1.0, 1.0)] + >>> convex_hull_melkman([(0, 3), (2, 2), (1, 1), (2, 1), (3, 0), (0, 0), (3, 3), + ... (2, -1), (2, -4), (1, -3)]) + [(0.0, 0.0), (0.0, 3.0), (1.0, -3.0), (2.0, -4.0), (3.0, 0.0), (3.0, 3.0)] + """ + points = sorted(_validate_input(points)) + n = len(points) + + convex_hull = points[:2] + for i in range(2, n): + det = _det(convex_hull[1], convex_hull[0], points[i]) + if det > 0: + convex_hull.insert(0, points[i]) + break + elif det < 0: + convex_hull.append(points[i]) + break + else: + convex_hull[1] = points[i] + i += 1 + + for i in range(i, n): + if ( + _det(convex_hull[0], convex_hull[-1], points[i]) > 0 + and _det(convex_hull[-1], convex_hull[0], points[1]) < 0 + ): + # The point lies within the convex hull + continue + + convex_hull.insert(0, points[i]) + convex_hull.append(points[i]) + while _det(convex_hull[0], convex_hull[1], convex_hull[2]) >= 0: + del convex_hull[1] + while _det(convex_hull[-1], convex_hull[-2], convex_hull[-3]) <= 0: + del convex_hull[-2] + + # `convex_hull` is contains the convex hull in circular order + return sorted(convex_hull[1:] if len(convex_hull) > 3 else convex_hull) + + def main(): points = [ (0, 3), @@ -426,10 +491,14 @@ def main(): ] # the convex set of points is # [(0, 0), (0, 3), (1, -3), (2, -4), (3, 0), (3, 3)] - results_recursive = convex_hull_recursive(points) results_bf = convex_hull_bf(points) + + results_recursive = convex_hull_recursive(points) assert results_bf == results_recursive + results_melkman = convex_hull_melkman(points) + assert results_bf == results_melkman + print(results_bf) From 5b5c51854f28d24bb87f5e83d9a69c6aa5682a46 Mon Sep 17 00:00:00 2001 From: sharmapulkit04 <39304055+sharmapulkit04@users.noreply.github.com> Date: Thu, 29 Oct 2020 07:33:55 +0530 Subject: [PATCH 044/195] Hacktoberfest: Added first solution to Project Euler problem 58 (#3599) * Added solution to problem 58 * Update sol1.py Co-authored-by: John Law --- project_euler/problem_058/__init__.py | 1 + project_euler/problem_058/sol1.py | 86 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 project_euler/problem_058/__init__.py create mode 100644 project_euler/problem_058/sol1.py diff --git a/project_euler/problem_058/__init__.py b/project_euler/problem_058/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_058/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_058/sol1.py b/project_euler/problem_058/sol1.py new file mode 100644 index 000000000000..d3b15157fbbd --- /dev/null +++ b/project_euler/problem_058/sol1.py @@ -0,0 +1,86 @@ +""" +Project Euler Problem 58:https://projecteuler.net/problem=58 + + +Starting with 1 and spiralling anticlockwise in the following way, +a square spiral with side length 7 is formed. + +37 36 35 34 33 32 31 +38 17 16 15 14 13 30 +39 18 5 4 3 12 29 +40 19 6 1 2 11 28 +41 20 7 8 9 10 27 +42 21 22 23 24 25 26 +43 44 45 46 47 48 49 + +It is interesting to note that the odd squares lie along the bottom right +diagonal ,but what is more interesting is that 8 out of the 13 numbers +lying along both diagonals are prime; that is, a ratio of 8/13 ≈ 62%. + +If one complete new layer is wrapped around the spiral above, +a square spiral with side length 9 will be formed. +If this process is continued, +what is the side length of the square spiral for which +the ratio of primes along both diagonals first falls below 10%? + +Solution: We have to find an odd length side for which square falls below +10%. With every layer we add 4 elements are being added to the diagonals +,lets say we have a square spiral of odd length with side length j, +then if we move from j to j+2, we are adding j*j+j+1,j*j+2*(j+1),j*j+3*(j+1) +j*j+4*(j+1). Out of these 4 only the first three can become prime +because last one reduces to (j+2)*(j+2). +So we check individually each one of these before incrementing our +count of current primes. + +""" + + +def isprime(d: int) -> int: + """ + returns whether the given digit is prime or not + >>> isprime(1) + 0 + >>> isprime(17) + 1 + >>> isprime(10000) + 0 + """ + if d == 1: + return 0 + + i = 2 + while i * i <= d: + if d % i == 0: + return 0 + i = i + 1 + return 1 + + +def solution(ratio: float = 0.1) -> int: + """ + returns the side length of the square spiral of odd length greater + than 1 for which the ratio of primes along both diagonals + first falls below the given ratio. + >>> solution(.5) + 11 + >>> solution(.2) + 309 + >>> solution(.111) + 11317 + """ + + j = 3 + primes = 3 + + while primes / (2 * j - 1) >= ratio: + for i in range(j * j + j + 1, (j + 2) * (j + 2), j + 1): + primes = primes + isprime(i) + + j = j + 2 + return j + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0ff3f3240d200f5df5fd6b02e9be1c5e56eb53ce Mon Sep 17 00:00:00 2001 From: Marcos Vinicius Date: Thu, 29 Oct 2020 00:09:39 -0300 Subject: [PATCH 045/195] Hacktoberfest: adding doctest to radix_sort.py file (#2779) * adding doctest to radix_sort.py file * fixup! Format Python code with psf/black push * Update radix_sort.py * Update radix_sort.py * fixup! Format Python code with psf/black push * Update radix_sort.py * line * fix tests Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: John Law --- sorts/radix_sort.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/sorts/radix_sort.py b/sorts/radix_sort.py index 7942462ea10d..57dbbaa79076 100644 --- a/sorts/radix_sort.py +++ b/sorts/radix_sort.py @@ -1,13 +1,28 @@ +""" +This is a pure Python implementation of the quick sort algorithm +For doctests run following command: +python -m doctest -v radix_sort.py +or +python3 -m doctest -v radix_sort.py +For manual testing run: +python radix_sort.py +""" from __future__ import annotations +from typing import List -def radix_sort(list_of_ints: list[int]) -> list[int]: + +def radix_sort(list_of_ints: List[int]) -> List[int]: """ - radix_sort(range(15)) == sorted(range(15)) + Examples: + >>> radix_sort([0, 5, 3, 2, 2]) + [0, 2, 2, 3, 5] + + >>> radix_sort(list(range(15))) == sorted(range(15)) True - radix_sort(reversed(range(15))) == sorted(range(15)) + >>> radix_sort(list(range(14,-1,-1))) == sorted(range(15)) True - radix_sort([1,100,10,1000]) == sorted([1,100,10,1000]) + >>> radix_sort([1,100,10,1000]) == sorted([1,100,10,1000]) True """ RADIX = 10 @@ -29,3 +44,9 @@ def radix_sort(list_of_ints: list[int]) -> list[int]: # move to next placement *= RADIX return list_of_ints + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 6908503945059859d8dee6940e7bc63b942b2fb8 Mon Sep 17 00:00:00 2001 From: fpringle Date: Thu, 29 Oct 2020 07:49:33 +0100 Subject: [PATCH 046/195] Added solution for Project Euler problem 87. (#3141) * Added solution for Project Euler problem 87. Fixes: #2695 * Update docstring and 0-padding in directory name. Reference: #3256 --- project_euler/problem_087/__init__.py | 0 project_euler/problem_087/sol1.py | 52 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 project_euler/problem_087/__init__.py create mode 100644 project_euler/problem_087/sol1.py diff --git a/project_euler/problem_087/__init__.py b/project_euler/problem_087/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_087/sol1.py b/project_euler/problem_087/sol1.py new file mode 100644 index 000000000000..f444481c17ac --- /dev/null +++ b/project_euler/problem_087/sol1.py @@ -0,0 +1,52 @@ +""" +Project Euler Problem 87: https://projecteuler.net/problem=87 + +The smallest number expressible as the sum of a prime square, prime cube, and prime +fourth power is 28. In fact, there are exactly four numbers below fifty that can be +expressed in such a way: + +28 = 22 + 23 + 24 +33 = 32 + 23 + 24 +49 = 52 + 23 + 24 +47 = 22 + 33 + 24 + +How many numbers below fifty million can be expressed as the sum of a prime square, +prime cube, and prime fourth power? +""" + + +def solution(limit: int = 50000000) -> int: + """ + Return the number of integers less than limit which can be expressed as the sum + of a prime square, prime cube, and prime fourth power. + >>> solution(50) + 4 + """ + ret = set() + prime_square_limit = int((limit - 24) ** (1 / 2)) + + primes = set(range(3, prime_square_limit + 1, 2)) + primes.add(2) + for p in range(3, prime_square_limit + 1, 2): + if p not in primes: + continue + primes.difference_update(set(range(p * p, prime_square_limit + 1, p))) + + for prime1 in primes: + square = prime1 * prime1 + for prime2 in primes: + cube = prime2 * prime2 * prime2 + if square + cube >= limit - 16: + break + for prime3 in primes: + tetr = prime3 * prime3 * prime3 * prime3 + total = square + cube + tetr + if total >= limit: + break + ret.add(total) + + return len(ret) + + +if __name__ == "__main__": + print(f"{solution() = }") From c80067041b24086ce216a200d3b5c354662c56ea Mon Sep 17 00:00:00 2001 From: PetitNigaud <44503597+PetitNigaud@users.noreply.github.com> Date: Thu, 29 Oct 2020 08:04:42 +0100 Subject: [PATCH 047/195] Add first solution for Project Euler Problem 207 (#3522) * add solution to Project Euler problem 206 * Add solution to Project Euler problem 205 * updating DIRECTORY.md * updating DIRECTORY.md * Revert "Add solution to Project Euler problem 205" This reverts commit 64e3d36cab2b68630b73a217c9ba455202d85cbb. * Revert "add solution to Project Euler problem 206" This reverts commit 53568cf4efd84f4b1c039eade335655842fa29e3. * add solution for project euler problem 207 * updating DIRECTORY.md * add type hint for output of helper function * Correct default parameter value in solution * use descriptive variable names and remove problem solution from doctest Fixes: #2695 Co-authored-by: nico Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_207/__init__.py | 0 project_euler/problem_207/sol1.py | 98 +++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 project_euler/problem_207/__init__.py create mode 100644 project_euler/problem_207/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index c3b32b1ab754..9e8981f5f61d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -720,6 +720,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) * Problem 191 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_191/sol1.py) + * Problem 207 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_207/sol1.py) * Problem 234 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_234/sol1.py) * Problem 551 diff --git a/project_euler/problem_207/__init__.py b/project_euler/problem_207/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_207/sol1.py b/project_euler/problem_207/sol1.py new file mode 100644 index 000000000000..fb901fde1624 --- /dev/null +++ b/project_euler/problem_207/sol1.py @@ -0,0 +1,98 @@ +""" + +Project Euler Problem 207: https://projecteuler.net/problem=207 + +Problem Statement: +For some positive integers k, there exists an integer partition of the form +4**t = 2**t + k, where 4**t, 2**t, and k are all positive integers and t is a real +number. The first two such partitions are 4**1 = 2**1 + 2 and +4**1.5849625... = 2**1.5849625... + 6. +Partitions where t is also an integer are called perfect. +For any m ≥ 1 let P(m) be the proportion of such partitions that are perfect with +k ≤ m. +Thus P(6) = 1/2. +In the following table are listed some values of P(m) + + P(5) = 1/1 + P(10) = 1/2 + P(15) = 2/3 + P(20) = 1/2 + P(25) = 1/2 + P(30) = 2/5 + ... + P(180) = 1/4 + P(185) = 3/13 + +Find the smallest m for which P(m) < 1/12345 + +Solution: +Equation 4**t = 2**t + k solved for t gives: + t = log2(sqrt(4*k+1)/2 + 1/2) +For t to be real valued, sqrt(4*k+1) must be an integer which is implemented in +function check_t_real(k). For a perfect partition t must be an integer. +To speed up significantly the search for partitions, instead of incrementing k by one +per iteration, the next valid k is found by k = (i**2 - 1) / 4 with an integer i and +k has to be a positive integer. If this is the case a partition is found. The partition +is perfect if t os an integer. The integer i is increased with increment 1 until the +proportion perfect partitions / total partitions drops under the given value. + +""" + +import math + + +def check_partition_perfect(positive_integer: int) -> bool: + """ + + Check if t = f(positive_integer) = log2(sqrt(4*positive_integer+1)/2 + 1/2) is a + real number. + + >>> check_partition_perfect(2) + True + + >>> check_partition_perfect(6) + False + + """ + + exponent = math.log2(math.sqrt(4 * positive_integer + 1) / 2 + 1 / 2) + + return exponent == int(exponent) + + +def solution(max_proportion: float = 1 / 12345) -> int: + """ + Find m for which the proportion of perfect partitions to total partitions is lower + than max_proportion + + >>> solution(1) > 5 + True + + >>> solution(1/2) > 10 + True + + >>> solution(3 / 13) > 185 + True + + """ + + total_partitions = 0 + perfect_partitions = 0 + + integer = 3 + while True: + partition_candidate = (integer ** 2 - 1) / 4 + # if candidate is an integer, then there is a partition for k + if partition_candidate == int(partition_candidate): + partition_candidate = int(partition_candidate) + total_partitions += 1 + if check_partition_perfect(partition_candidate): + perfect_partitions += 1 + if perfect_partitions > 0: + if perfect_partitions / total_partitions < max_proportion: + return partition_candidate + integer += 1 + + +if __name__ == "__main__": + print(f"{solution() = }") From da99dd04c148eb64fed2124cba55ce90811648bc Mon Sep 17 00:00:00 2001 From: Joyce Date: Thu, 29 Oct 2020 00:17:26 -0700 Subject: [PATCH 048/195] math/greatest_common_divisor: add support for negative numbers (#2628) * add type hints to math/gcd * add doctest * math/gcd - run black formatter * math/gcd: remove manual doctest * add correction to gcd of negative numbers * add more doctest in iterative gcd --- maths/greatest_common_divisor.py | 39 ++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/maths/greatest_common_divisor.py b/maths/greatest_common_divisor.py index 0926ade5dec2..a2174a8eb74a 100644 --- a/maths/greatest_common_divisor.py +++ b/maths/greatest_common_divisor.py @@ -2,10 +2,12 @@ Greatest Common Divisor. Wikipedia reference: https://en.wikipedia.org/wiki/Greatest_common_divisor + +gcd(a, b) = gcd(a, -b) = gcd(-a, b) = gcd(-a, -b) by definition of divisibility """ -def greatest_common_divisor(a, b): +def greatest_common_divisor(a: int, b: int) -> int: """ Calculate Greatest Common Divisor (GCD). >>> greatest_common_divisor(24, 40) @@ -20,31 +22,44 @@ def greatest_common_divisor(a, b): 1 >>> greatest_common_divisor(16, 4) 4 + >>> greatest_common_divisor(-3, 9) + 3 + >>> greatest_common_divisor(9, -3) + 3 + >>> greatest_common_divisor(3, -9) + 3 + >>> greatest_common_divisor(-3, -9) + 3 """ - return b if a == 0 else greatest_common_divisor(b % a, a) - - -""" -Below method is more memory efficient because it does not use the stack (chunk of -memory). While above method is good, uses more memory for huge numbers because of the -recursive calls required to calculate the greatest common divisor. -""" + return abs(b) if a == 0 else greatest_common_divisor(b % a, a) -def gcd_by_iterative(x, y): +def gcd_by_iterative(x: int, y: int) -> int: """ + Below method is more memory efficient because it does not create additional + stack frames for recursive functions calls (as done in the above method). >>> gcd_by_iterative(24, 40) 8 >>> greatest_common_divisor(24, 40) == gcd_by_iterative(24, 40) True + >>> gcd_by_iterative(-3, -9) + 3 + >>> gcd_by_iterative(3, -9) + 3 + >>> gcd_by_iterative(1, -800) + 1 + >>> gcd_by_iterative(11, 37) + 1 """ while y: # --> when y=0 then loop will terminate and return x as final GCD. x, y = y, x % y - return x + return abs(x) def main(): - """Call Greatest Common Divisor function.""" + """ + Call Greatest Common Divisor function. + """ try: nums = input("Enter two integers separated by comma (,): ").split(",") num_1 = int(nums[0]) From 9588df7dc50e5a52acf67572375bab8a2dc0b559 Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Thu, 29 Oct 2020 17:39:19 +0800 Subject: [PATCH 049/195] Balanced parentheses (#3768) * Fixed balanced_parentheses.py * fixed pre-commit * eliminate is_paired * remove unused line * updating DIRECTORY.md * Update data_structures/stacks/balanced_parentheses.py Co-authored-by: Christian Clauss * Add more test cases * Update data_structures/stacks/balanced_parentheses.py Co-authored-by: Christian Clauss Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + .../stacks/balanced_parentheses.py | 38 +++++++++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 9e8981f5f61d..ea5e01addeb0 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -520,6 +520,7 @@ * [Sierpinski Triangle](https://github.com/TheAlgorithms/Python/blob/master/other/sierpinski_triangle.py) * [Tower Of Hanoi](https://github.com/TheAlgorithms/Python/blob/master/other/tower_of_hanoi.py) * [Triplet Sum](https://github.com/TheAlgorithms/Python/blob/master/other/triplet_sum.py) + * [Two Pointer](https://github.com/TheAlgorithms/Python/blob/master/other/two_pointer.py) * [Two Sum](https://github.com/TheAlgorithms/Python/blob/master/other/two_sum.py) * [Word Patterns](https://github.com/TheAlgorithms/Python/blob/master/other/word_patterns.py) diff --git a/data_structures/stacks/balanced_parentheses.py b/data_structures/stacks/balanced_parentheses.py index 7aacd5969277..674f7ea436ed 100644 --- a/data_structures/stacks/balanced_parentheses.py +++ b/data_structures/stacks/balanced_parentheses.py @@ -1,23 +1,37 @@ from .stack import Stack -__author__ = "Omkar Pathak" - -def balanced_parentheses(parentheses): - """ Use a stack to check if a string of parentheses is balanced.""" - stack = Stack(len(parentheses)) - for parenthesis in parentheses: - if parenthesis == "(": - stack.push(parenthesis) - elif parenthesis == ")": - if stack.is_empty(): +def balanced_parentheses(parentheses: str) -> bool: + """Use a stack to check if a string of parentheses is balanced. + >>> balanced_parentheses("([]{})") + True + >>> balanced_parentheses("[()]{}{[()()]()}") + True + >>> balanced_parentheses("[(])") + False + >>> balanced_parentheses("1+2*3-4") + True + >>> balanced_parentheses("") + True + """ + stack = Stack() + bracket_pairs = {"(": ")", "[": "]", "{": "}"} + for bracket in parentheses: + if bracket in bracket_pairs: + stack.push(bracket) + elif bracket in (")", "]", "}"): + if stack.is_empty() or bracket_pairs[stack.pop()] != bracket: return False - stack.pop() return stack.is_empty() if __name__ == "__main__": + from doctest import testmod + + testmod() + examples = ["((()))", "((())", "(()))"] print("Balanced parentheses demonstration:\n") for example in examples: - print(example + ": " + str(balanced_parentheses(example))) + not_str = "" if balanced_parentheses(example) else "not " + print(f"{example} is {not_str}balanced") From 81ac1e4469500899b5ecd47e87cf8795cca973a3 Mon Sep 17 00:00:00 2001 From: ParamonPlay <56618202+ParamonPlay@users.noreply.github.com> Date: Fri, 30 Oct 2020 07:11:15 -0700 Subject: [PATCH 050/195] No issues so far (#3835) * Simplify GitHub Actions * Update stale.yml Co-authored-by: Christian Clauss --- .github/workflows/directory_writer.yml | 2 -- .github/workflows/stale.yml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/directory_writer.yml b/.github/workflows/directory_writer.yml index 6547d1c18c79..be8154a32696 100644 --- a/.github/workflows/directory_writer.yml +++ b/.github/workflows/directory_writer.yml @@ -8,8 +8,6 @@ jobs: steps: - uses: actions/checkout@v1 # v1, NOT v2 - uses: actions/setup-python@v2 - with: - python-version: 3.x - name: Write DIRECTORY.md run: | scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 4793f54f7af8..341153dbf455 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,7 +6,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v1 + - uses: actions/stale@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: > From 6ce053dabad50cf9d94669200bb9411aaa872741 Mon Sep 17 00:00:00 2001 From: Jake Gerber Date: Fri, 30 Oct 2020 15:10:44 -0700 Subject: [PATCH 051/195] Added decimal_isolate.py (#3700) * Add files via upload * Delete decimal_isolate.py * Added decimal_isolate file. * Update decimal_isolate.py * Update decimal_isolate.py * Update decimal_isolate.py * Update decimal_isolate.py * Update decimal_isolate.py * Delete decimal_isolate.py * Add files via upload * Update maths/decimal_isolate.py Co-authored-by: Christian Clauss * Update decimal_isolate.py * Update decimal_isolate.py * Update decimal_isolate.py * Update decimal_isolate.py Co-authored-by: Christian Clauss --- maths/decimal_isolate.py | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 maths/decimal_isolate.py diff --git a/maths/decimal_isolate.py b/maths/decimal_isolate.py new file mode 100644 index 000000000000..0e3967a4671d --- /dev/null +++ b/maths/decimal_isolate.py @@ -0,0 +1,45 @@ +""" +Isolate the Decimal part of a Number +https://stackoverflow.com/questions/3886402/how-to-get-numbers-after-decimal-point +""" + + +def decimal_isolate(number, digitAmount): + + """ + Isolates the decimal part of a number. + If digitAmount > 0 round to that decimal place, else print the entire decimal. + >>> decimal_isolate(1.53, 0) + 0.53 + >>> decimal_isolate(35.345, 1) + 0.3 + >>> decimal_isolate(35.345, 2) + 0.34 + >>> decimal_isolate(35.345, 3) + 0.345 + >>> decimal_isolate(-14.789, 3) + -0.789 + >>> decimal_isolate(0, 2) + 0 + >>> decimal_isolate(-14.123, 1) + -0.1 + >>> decimal_isolate(-14.123, 2) + -0.12 + >>> decimal_isolate(-14.123, 3) + -0.123 + """ + if digitAmount > 0: + return round(number - int(number), digitAmount) + return number - int(number) + + +if __name__ == "__main__": + print(decimal_isolate(1.53, 0)) + print(decimal_isolate(35.345, 1)) + print(decimal_isolate(35.345, 2)) + print(decimal_isolate(35.345, 3)) + print(decimal_isolate(-14.789, 3)) + print(decimal_isolate(0, 2)) + print(decimal_isolate(-14.123, 1)) + print(decimal_isolate(-14.123, 2)) + print(decimal_isolate(-14.123, 3)) From bc5751d0e8cf37e01eab9d3ba8ec12054274e256 Mon Sep 17 00:00:00 2001 From: jbaenaxd Date: Sun, 1 Nov 2020 08:38:11 +0100 Subject: [PATCH 052/195] Shortened code (#3855) --- searches/quick_select.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/searches/quick_select.py b/searches/quick_select.py index 17dca395f73c..5ede8c4dd07f 100644 --- a/searches/quick_select.py +++ b/searches/quick_select.py @@ -44,8 +44,7 @@ def quick_select(items: list, index: int): if index >= len(items) or index < 0: return None - pivot = random.randint(0, len(items) - 1) - pivot = items[pivot] + pivot = items[random.randint(0, len(items) - 1)] count = 0 smaller, equal, larger = _partition(items, pivot) count = len(equal) From d864f1f2cd7ac72f7e001888bb129549a987262b Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Sun, 1 Nov 2020 05:57:48 -0500 Subject: [PATCH 053/195] Added solution to Project Euler problem 301 (#3343) * Added solution to Project Euler problem 301 * Added newline to end of file * Fixed formatting and tests * Changed lossCount to loss_count * Fixed default parameter value for solution * Removed helper function and modified print stmt * Fixed code formatting * Optimized solution from O(n^2) to O(1) constant time * Update sol1.py --- project_euler/problem_301/__init__.py | 0 project_euler/problem_301/sol1.py | 58 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 project_euler/problem_301/__init__.py create mode 100644 project_euler/problem_301/sol1.py diff --git a/project_euler/problem_301/__init__.py b/project_euler/problem_301/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_301/sol1.py b/project_euler/problem_301/sol1.py new file mode 100644 index 000000000000..b1d434c189b7 --- /dev/null +++ b/project_euler/problem_301/sol1.py @@ -0,0 +1,58 @@ +""" +Project Euler Problem 301: https://projecteuler.net/problem=301 + +Problem Statement: +Nim is a game played with heaps of stones, where two players take +it in turn to remove any number of stones from any heap until no stones remain. + +We'll consider the three-heap normal-play version of +Nim, which works as follows: +- At the start of the game there are three heaps of stones. +- On each player's turn, the player may remove any positive + number of stones from any single heap. +- The first player unable to move (because no stones remain) loses. + +If (n1, n2, n3) indicates a Nim position consisting of heaps of size +n1, n2, and n3, then there is a simple function, which you may look up +or attempt to deduce for yourself, X(n1, n2, n3) that returns: +- zero if, with perfect strategy, the player about to + move will eventually lose; or +- non-zero if, with perfect strategy, the player about + to move will eventually win. + +For example X(1,2,3) = 0 because, no matter what the current player does, +the opponent can respond with a move that leaves two heaps of equal size, +at which point every move by the current player can be mirrored by the +opponent until no stones remain; so the current player loses. To illustrate: +- current player moves to (1,2,1) +- opponent moves to (1,0,1) +- current player moves to (0,0,1) +- opponent moves to (0,0,0), and so wins. + +For how many positive integers n <= 2^30 does X(n,2n,3n) = 0? +""" + + +def solution(exponent: int = 30) -> int: + """ + For any given exponent x >= 0, 1 <= n <= 2^x. + This function returns how many Nim games are lost given that + each Nim game has three heaps of the form (n, 2*n, 3*n). + >>> solution(0) + 1 + >>> solution(2) + 3 + >>> solution(10) + 144 + """ + # To find how many total games were lost for a given exponent x, + # we need to find the Fibonacci number F(x+2). + fibonacci_index = exponent + 2 + phi = (1 + 5 ** 0.5) / 2 + fibonacci = (phi ** fibonacci_index - (phi - 1) ** fibonacci_index) / 5 ** 0.5 + + return int(fibonacci) + + +if __name__ == "__main__": + print(f"{solution() = }") From 70b6a99420cd2d135af09fbf78d9512851533d02 Mon Sep 17 00:00:00 2001 From: GGn0 <44038661+GGn0@users.noreply.github.com> Date: Sun, 1 Nov 2020 14:12:21 +0100 Subject: [PATCH 054/195] Add solution to problem 74 (#3110) * Add solution to problem 74 * Fix typo * Edit unnecessary comment * Rename folder, add default params in solution() * Rename file to solve conflicts * Fix doctests --- project_euler/problem_074/sol2.py | 116 ++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 project_euler/problem_074/sol2.py diff --git a/project_euler/problem_074/sol2.py b/project_euler/problem_074/sol2.py new file mode 100644 index 000000000000..0348ef1a6628 --- /dev/null +++ b/project_euler/problem_074/sol2.py @@ -0,0 +1,116 @@ +""" + Project Euler Problem 074: https://projecteuler.net/problem=74 + + Starting from any positive integer number + it is possible to attain another one summing the factorial of its digits. + + Repeating this step, we can build chains of numbers. + It is not difficult to prove that EVERY starting number + will eventually get stuck in a loop. + + The request is to find how many numbers less than one million + produce a chain with exactly 60 non repeating items. + + Solution approach: + This solution simply consists in a loop that generates + the chains of non repeating items. + The generation of the chain stops before a repeating item + or if the size of the chain is greater then the desired one. + After generating each chain, the length is checked and the counter increases. +""" + + +def factorial(a: int) -> int: + """Returns the factorial of the input a + >>> factorial(5) + 120 + + >>> factorial(6) + 720 + + >>> factorial(0) + 1 + """ + + # The factorial function is not defined for negative numbers + if a < 0: + raise ValueError("Invalid negative input!", a) + + # The case of 0! is handled separately + if a == 0: + return 1 + else: + # use a temporary support variable to store the computation + temporary_computation = 1 + + while a > 0: + temporary_computation *= a + a -= 1 + + return temporary_computation + + +def factorial_sum(a: int) -> int: + """Function to perform the sum of the factorial + of all the digits in a + + >>> factorial_sum(69) + 363600 + """ + + # Prepare a variable to hold the computation + fact_sum = 0 + + """ Convert a in string to iterate on its digits + convert the digit back into an int + and add its factorial to fact_sum. + """ + for i in str(a): + fact_sum += factorial(int(i)) + + return fact_sum + + +def solution(chain_length: int = 60, number_limit: int = 1000000) -> int: + """Returns the number of numbers that produce + chains with exactly 60 non repeating elements. + >>> solution(60,1000000) + 402 + >>> solution(15,1000000) + 17800 + """ + + # the counter for the chains with the exact desired length + chain_counter = 0 + + for i in range(1, number_limit + 1): + + # The temporary list will contain the elements of the chain + chain_list = [i] + + # The new element of the chain + new_chain_element = factorial_sum(chain_list[-1]) + + """ Stop computing the chain when you find a repeating item + or the length it greater then the desired one. + """ + while not (new_chain_element in chain_list) and ( + len(chain_list) <= chain_length + ): + chain_list += [new_chain_element] + + new_chain_element = factorial_sum(chain_list[-1]) + + """ If the while exited because the chain list contains the exact amount of elements + increase the counter + """ + chain_counter += len(chain_list) == chain_length + + return chain_counter + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{solution()}") From 47317638eb8d1528168cc122e123a315cae779c7 Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Mon, 2 Nov 2020 00:35:31 +0800 Subject: [PATCH 055/195] Update infix to postfix (#3817) * add test to infix_to_postfix_conversion * fixed pre-commit error * fixed build error * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 5 ++ data_structures/stacks/__init__.py | 22 ------ .../stacks/infix_to_postfix_conversion.py | 70 +++++++++++-------- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index ea5e01addeb0..7c695892112a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -674,6 +674,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_056/sol1.py) * Problem 057 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_057/sol1.py) + * Problem 058 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_058/sol1.py) * Problem 062 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_062/sol1.py) * Problem 063 @@ -699,6 +701,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) * Problem 081 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) + * Problem 087 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_087/sol1.py) * Problem 091 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_091/sol1.py) * Problem 097 @@ -817,6 +821,7 @@ * [Prefix Function](https://github.com/TheAlgorithms/Python/blob/master/strings/prefix_function.py) * [Rabin Karp](https://github.com/TheAlgorithms/Python/blob/master/strings/rabin_karp.py) * [Remove Duplicate](https://github.com/TheAlgorithms/Python/blob/master/strings/remove_duplicate.py) + * [Reverse Letters](https://github.com/TheAlgorithms/Python/blob/master/strings/reverse_letters.py) * [Reverse Words](https://github.com/TheAlgorithms/Python/blob/master/strings/reverse_words.py) * [Split](https://github.com/TheAlgorithms/Python/blob/master/strings/split.py) * [Swap Case](https://github.com/TheAlgorithms/Python/blob/master/strings/swap_case.py) diff --git a/data_structures/stacks/__init__.py b/data_structures/stacks/__init__.py index f6995cf98977..e69de29bb2d1 100644 --- a/data_structures/stacks/__init__.py +++ b/data_structures/stacks/__init__.py @@ -1,22 +0,0 @@ -class Stack: - def __init__(self): - self.stack = [] - self.top = 0 - - def is_empty(self): - return self.top == 0 - - def push(self, item): - if self.top < len(self.stack): - self.stack[self.top] = item - else: - self.stack.append(item) - - self.top += 1 - - def pop(self): - if self.is_empty(): - return None - else: - self.top -= 1 - return self.stack[self.top] diff --git a/data_structures/stacks/infix_to_postfix_conversion.py b/data_structures/stacks/infix_to_postfix_conversion.py index 4a1180c9d8e4..dedba8479ac8 100644 --- a/data_structures/stacks/infix_to_postfix_conversion.py +++ b/data_structures/stacks/infix_to_postfix_conversion.py @@ -1,57 +1,67 @@ -import string +""" +https://en.wikipedia.org/wiki/Infix_notation +https://en.wikipedia.org/wiki/Reverse_Polish_notation +https://en.wikipedia.org/wiki/Shunting-yard_algorithm +""" +from .balanced_parentheses import balanced_parentheses from .stack import Stack -__author__ = "Omkar Pathak" - -def is_operand(char): - return char in string.ascii_letters or char in string.digits - - -def precedence(char): - """Return integer value representing an operator's precedence, or +def precedence(char: str) -> int: + """ + Return integer value representing an operator's precedence, or order of operation. - https://en.wikipedia.org/wiki/Order_of_operations """ - dictionary = {"+": 1, "-": 1, "*": 2, "/": 2, "^": 3} - return dictionary.get(char, -1) - + return {"+": 1, "-": 1, "*": 2, "/": 2, "^": 3}.get(char, -1) -def infix_to_postfix(expression): - """Convert infix notation to postfix notation using the Shunting-yard - algorithm. - https://en.wikipedia.org/wiki/Shunting-yard_algorithm - https://en.wikipedia.org/wiki/Infix_notation - https://en.wikipedia.org/wiki/Reverse_Polish_notation +def infix_to_postfix(expression_str: str) -> str: + """ + >>> infix_to_postfix("(1*(2+3)+4))") + Traceback (most recent call last): + ... + ValueError: Mismatched parentheses + >>> infix_to_postfix("") + '' + >>> infix_to_postfix("3+2") + '3 2 +' + >>> infix_to_postfix("(3+4)*5-6") + '3 4 + 5 * 6 -' + >>> infix_to_postfix("(1+2)*3/4-5") + '1 2 + 3 * 4 / 5 -' + >>> infix_to_postfix("a+b*c+(d*e+f)*g") + 'a b c * + d e * f + g * +' + >>> infix_to_postfix("x^y/(5*z)+2") + 'x y ^ 5 z * / 2 +' """ - stack = Stack(len(expression)) + if not balanced_parentheses(expression_str): + raise ValueError("Mismatched parentheses") + stack = Stack() postfix = [] - for char in expression: - if is_operand(char): + for char in expression_str: + if char.isalpha() or char.isdigit(): postfix.append(char) - elif char not in {"(", ")"}: - while not stack.is_empty() and precedence(char) <= precedence(stack.peek()): - postfix.append(stack.pop()) - stack.push(char) elif char == "(": stack.push(char) elif char == ")": while not stack.is_empty() and stack.peek() != "(": postfix.append(stack.pop()) - # Pop '(' from stack. If there is no '(', there is a mismatched - # parentheses. - if stack.peek() != "(": - raise ValueError("Mismatched parentheses") stack.pop() + else: + while not stack.is_empty() and precedence(char) <= precedence(stack.peek()): + postfix.append(stack.pop()) + stack.push(char) while not stack.is_empty(): postfix.append(stack.pop()) return " ".join(postfix) if __name__ == "__main__": + from doctest import testmod + + testmod() expression = "a+b*(c^d-e)^(f+g*h)-i" print("Infix to Postfix Notation demonstration:\n") From f595249089cedecd1b3f5540ea412668a3ec2495 Mon Sep 17 00:00:00 2001 From: Peter Yao Date: Mon, 2 Nov 2020 09:54:20 -0800 Subject: [PATCH 056/195] Project Euler 206 Solution (#3829) * Readd Project Euler 206 solution for issue #2695, dupe of pull request #3042 * Add PE 206 to directory * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_206/__init__.py | 0 project_euler/problem_206/sol1.py | 74 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 project_euler/problem_206/__init__.py create mode 100644 project_euler/problem_206/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 7c695892112a..0cecae28d51d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -725,6 +725,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) * Problem 191 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_191/sol1.py) + * Problem 206 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_206/sol1.py) * Problem 207 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_207/sol1.py) * Problem 234 diff --git a/project_euler/problem_206/__init__.py b/project_euler/problem_206/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_206/sol1.py b/project_euler/problem_206/sol1.py new file mode 100644 index 000000000000..ffac2b32aa77 --- /dev/null +++ b/project_euler/problem_206/sol1.py @@ -0,0 +1,74 @@ +""" +Project Euler Problem 206: https://projecteuler.net/problem=206 + +Find the unique positive integer whose square has the form 1_2_3_4_5_6_7_8_9_0, +where each “_” is a single digit. + +----- + +Instead of computing every single permutation of that number and going +through a 10^9 search space, we can narrow it down considerably. + +If the square ends in a 0, then the square root must also end in a 0. Thus, +the last missing digit must be 0 and the square root is a multiple of 10. +We can narrow the search space down to the first 8 digits and multiply the +result of that by 10 at the end. + +Now the last digit is a 9, which can only happen if the square root ends +in a 3 or 7. From this point, we can try one of two different methods to find +the answer: + +1. Start at the lowest possible base number whose square would be in the +format, and count up. The base we would start at is 101010103, whose square is +the closest number to 10203040506070809. Alternate counting up by 4 and 6 so +the last digit of the base is always a 3 or 7. + +2. Start at the highest possible base number whose square would be in the +format, and count down. That base would be 138902663, whose square is the +closest number to 1929394959697989. Alternate counting down by 6 and 4 so the +last digit of the base is always a 3 or 7. + +The solution does option 2 because the answer happens to be much closer to the +starting point. +""" + + +def is_square_form(num: int) -> bool: + """ + Determines if num is in the form 1_2_3_4_5_6_7_8_9 + + >>> is_square_form(1) + False + >>> is_square_form(112233445566778899) + True + >>> is_square_form(123456789012345678) + False + """ + digit = 9 + + while num > 0: + if num % 10 != digit: + return False + num //= 100 + digit -= 1 + + return True + + +def solution() -> int: + """ + Returns the first integer whose square is of the form 1_2_3_4_5_6_7_8_9_0 + """ + num = 138902663 + + while not is_square_form(num * num): + if num % 10 == 3: + num -= 6 # (3 - 6) % 10 = 7 + else: + num -= 4 # (7 - 4) % 10 = 3 + + return num * 10 + + +if __name__ == "__main__": + print(f"{solution() = }") From 8842ce6546c337e9545af658dea28747f08b6291 Mon Sep 17 00:00:00 2001 From: Cho Yin Yong Date: Mon, 2 Nov 2020 20:31:33 -0500 Subject: [PATCH 057/195] kth order statistic divide and conquer algorithm (#3690) * kth order statistics divide and conquer algorithm * add explanation of algorithm. * fix PEP8 line too long error * update order to be compliant to isort * add doctest * make file black compliant --- divide_and_conquer/kth_order_statistic.py | 64 +++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 divide_and_conquer/kth_order_statistic.py diff --git a/divide_and_conquer/kth_order_statistic.py b/divide_and_conquer/kth_order_statistic.py new file mode 100644 index 000000000000..f6e81a306bff --- /dev/null +++ b/divide_and_conquer/kth_order_statistic.py @@ -0,0 +1,64 @@ +""" +Find the kth smallest element in linear time using divide and conquer. +Recall we can do this trivially in O(nlogn) time. Sort the list and +access kth element in constant time. + +This is a divide and conquer algorithm that can find a solution in O(n) time. + +For more information of this algorithm: +https://web.stanford.edu/class/archive/cs/cs161/cs161.1138/lectures/08/Small08.pdf +""" +from random import choice +from typing import List + + +def random_pivot(lst): + """ + Choose a random pivot for the list. + We can use a more sophisticated algorithm here, such as the median-of-medians + algorithm. + """ + return choice(lst) + + +def kth_number(lst: List[int], k: int) -> int: + """ + Return the kth smallest number in lst. + >>> kth_number([2, 1, 3, 4, 5], 3) + 3 + >>> kth_number([2, 1, 3, 4, 5], 1) + 1 + >>> kth_number([2, 1, 3, 4, 5], 5) + 5 + >>> kth_number([3, 2, 5, 6, 7, 8], 2) + 3 + >>> kth_number([25, 21, 98, 100, 76, 22, 43, 60, 89, 87], 4) + 43 + """ + # pick a pivot and separate into list based on pivot. + pivot = random_pivot(lst) + + # partition based on pivot + # linear time + small = [e for e in lst if e < pivot] + big = [e for e in lst if e > pivot] + + # if we get lucky, pivot might be the element we want. + # we can easily see this: + # small (elements smaller than k) + # + pivot (kth element) + # + big (elements larger than k) + if len(small) == k - 1: + return pivot + # pivot is in elements bigger than k + elif len(small) < k - 1: + return kth_number(big, k - len(small) - 1) + # pivot is in elements smaller than k + else: + return kth_number(small, k) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 887bc265b062e802db77af9a811fd106566856ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Benjam=C3=ADn=20P=C3=89REZ=20MAURERA?= Date: Mon, 2 Nov 2020 23:21:13 -0300 Subject: [PATCH 058/195] Added a solution for Project Euler Problem 203 "Squarefree Binomial Coefficients" (#3513) * Added a solution for Project Euler Problem 203 (https://projecteuler.net/problem=203) * Simplified loop that calculates the coefficients of the Pascal's Triangle. Changes based on review suggestion. * Moved get_squared_primes_to_use function outside the get_squarefree function and fixed a failing doctest with the former. --- project_euler/problem_203/__init__.py | 0 project_euler/problem_203/sol1.py | 188 ++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 project_euler/problem_203/__init__.py create mode 100644 project_euler/problem_203/sol1.py diff --git a/project_euler/problem_203/__init__.py b/project_euler/problem_203/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_203/sol1.py b/project_euler/problem_203/sol1.py new file mode 100644 index 000000000000..227b476da131 --- /dev/null +++ b/project_euler/problem_203/sol1.py @@ -0,0 +1,188 @@ +""" +Project Euler Problem 203: https://projecteuler.net/problem=203 + +The binomial coefficients (n k) can be arranged in triangular form, Pascal's +triangle, like this: + 1 + 1 1 + 1 2 1 + 1 3 3 1 + 1 4 6 4 1 + 1 5 10 10 5 1 + 1 6 15 20 15 6 1 +1 7 21 35 35 21 7 1 + ......... + +It can be seen that the first eight rows of Pascal's triangle contain twelve +distinct numbers: 1, 2, 3, 4, 5, 6, 7, 10, 15, 20, 21 and 35. + +A positive integer n is called squarefree if no square of a prime divides n. +Of the twelve distinct numbers in the first eight rows of Pascal's triangle, +all except 4 and 20 are squarefree. The sum of the distinct squarefree numbers +in the first eight rows is 105. + +Find the sum of the distinct squarefree numbers in the first 51 rows of +Pascal's triangle. + +References: +- https://en.wikipedia.org/wiki/Pascal%27s_triangle +""" + +import math +from typing import List, Set + + +def get_pascal_triangle_unique_coefficients(depth: int) -> Set[int]: + """ + Returns the unique coefficients of a Pascal's triangle of depth "depth". + + The coefficients of this triangle are symmetric. A further improvement to this + method could be to calculate the coefficients once per level. Nonetheless, + the current implementation is fast enough for the original problem. + + >>> get_pascal_triangle_unique_coefficients(1) + {1} + >>> get_pascal_triangle_unique_coefficients(2) + {1} + >>> get_pascal_triangle_unique_coefficients(3) + {1, 2} + >>> get_pascal_triangle_unique_coefficients(8) + {1, 2, 3, 4, 5, 6, 7, 35, 10, 15, 20, 21} + """ + coefficients = {1} + previous_coefficients = [1] + for step in range(2, depth + 1): + coefficients_begins_one = previous_coefficients + [0] + coefficients_ends_one = [0] + previous_coefficients + previous_coefficients = [] + for x, y in zip(coefficients_begins_one, coefficients_ends_one): + coefficients.add(x + y) + previous_coefficients.append(x + y) + return coefficients + + +def get_primes_squared(max_number: int) -> List[int]: + """ + Calculates all primes between 2 and round(sqrt(max_number)) and returns + them squared up. + + >>> get_primes_squared(2) + [] + >>> get_primes_squared(4) + [4] + >>> get_primes_squared(10) + [4, 9] + >>> get_primes_squared(100) + [4, 9, 25, 49] + """ + max_prime = round(math.sqrt(max_number)) + non_primes = set() + primes = [] + for num in range(2, max_prime + 1): + if num in non_primes: + continue + + counter = 2 + while num * counter <= max_prime: + non_primes.add(num * counter) + counter += 1 + + primes.append(num ** 2) + return primes + + +def get_squared_primes_to_use( + num_to_look: int, squared_primes: List[int], previous_index: int +) -> int: + """ + Returns an int indicating the last index on which squares of primes + in primes are lower than num_to_look. + + This method supposes that squared_primes is sorted in ascending order and that + each num_to_look is provided in ascending order as well. Under these + assumptions, it needs a previous_index parameter that tells what was + the index returned by the method for the previous num_to_look. + + If all the elements in squared_primes are greater than num_to_look, then the + method returns -1. + + >>> get_squared_primes_to_use(1, [4, 9, 16, 25], 0) + -1 + >>> get_squared_primes_to_use(4, [4, 9, 16, 25], 0) + 1 + >>> get_squared_primes_to_use(16, [4, 9, 16, 25], 1) + 3 + """ + idx = max(previous_index, 0) + + while idx < len(squared_primes) and squared_primes[idx] <= num_to_look: + idx += 1 + + if idx == 0 and squared_primes[idx] > num_to_look: + return -1 + + if idx == len(squared_primes) and squared_primes[-1] > num_to_look: + return -1 + + return idx + + +def get_squarefree( + unique_coefficients: Set[int], squared_primes: List[int] +) -> Set[int]: + """ + Calculates the squarefree numbers inside unique_coefficients given a + list of square of primes. + + Based on the definition of a non-squarefree number, then any non-squarefree + n can be decomposed as n = p*p*r, where p is positive prime number and r + is a positive integer. + + Under the previous formula, any coefficient that is lower than p*p is + squarefree as r cannot be negative. On the contrary, if any r exists such + that n = p*p*r, then the number is non-squarefree. + + >>> get_squarefree({1}, []) + set() + >>> get_squarefree({1, 2}, []) + set() + >>> get_squarefree({1, 2, 3, 4, 5, 6, 7, 35, 10, 15, 20, 21}, [4, 9, 25]) + {1, 2, 3, 5, 6, 7, 35, 10, 15, 21} + """ + + if len(squared_primes) == 0: + return set() + + non_squarefrees = set() + prime_squared_idx = 0 + for num in sorted(unique_coefficients): + prime_squared_idx = get_squared_primes_to_use( + num, squared_primes, prime_squared_idx + ) + if prime_squared_idx == -1: + continue + if any(num % prime == 0 for prime in squared_primes[:prime_squared_idx]): + non_squarefrees.add(num) + + return unique_coefficients.difference(non_squarefrees) + + +def solution(n: int = 51) -> int: + """ + Returns the sum of squarefrees for a given Pascal's Triangle of depth n. + + >>> solution(1) + 0 + >>> solution(8) + 105 + >>> solution(9) + 175 + """ + unique_coefficients = get_pascal_triangle_unique_coefficients(n) + primes = get_primes_squared(max(unique_coefficients)) + squarefrees = get_squarefree(unique_coefficients, primes) + return sum(squarefrees) + + +if __name__ == "__main__": + print(f"{solution() = }") From 43573b23b22e8858e9fe5a52e9689a3ea39b1072 Mon Sep 17 00:00:00 2001 From: Shikhar Rai <34543293+kakashi215@users.noreply.github.com> Date: Mon, 2 Nov 2020 22:18:14 -0500 Subject: [PATCH 059/195] HACKTOBERFEST - Added solution to Euler 64. (#3706) * Added solution to Euler 64. Added Python solution to Project Euler Problem 64. Added a folder problem_064. Added __init__.py file. Added sol1.py file. * Update sol1.py Made formatting changes as mentioned by pre-commit * Update sol1.py Minor changes to variable naming and function calling as mentioned by @ruppysuppy * Update sol1.py Changes to function call as mentioned by @cclauss --- project_euler/problem_064/__init__.py | 0 project_euler/problem_064/sol1.py | 77 +++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 project_euler/problem_064/__init__.py create mode 100644 project_euler/problem_064/sol1.py diff --git a/project_euler/problem_064/__init__.py b/project_euler/problem_064/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_064/sol1.py b/project_euler/problem_064/sol1.py new file mode 100644 index 000000000000..69e3f6d97580 --- /dev/null +++ b/project_euler/problem_064/sol1.py @@ -0,0 +1,77 @@ +""" +Project Euler Problem 64: https://projecteuler.net/problem=64 + +All square roots are periodic when written as continued fractions. +For example, let us consider sqrt(23). +It can be seen that the sequence is repeating. +For conciseness, we use the notation sqrt(23)=[4;(1,3,1,8)], +to indicate that the block (1,3,1,8) repeats indefinitely. +Exactly four continued fractions, for N<=13, have an odd period. +How many continued fractions for N<=10000 have an odd period? + +References: +- https://en.wikipedia.org/wiki/Continued_fraction +""" + +from math import floor, sqrt + + +def continuous_fraction_period(n: int) -> int: + """ + Returns the continued fraction period of a number n. + + >>> continuous_fraction_period(2) + 1 + >>> continuous_fraction_period(5) + 1 + >>> continuous_fraction_period(7) + 4 + >>> continuous_fraction_period(11) + 2 + >>> continuous_fraction_period(13) + 5 + """ + numerator = 0.0 + denominator = 1.0 + ROOT = int(sqrt(n)) + integer_part = ROOT + period = 0 + while integer_part != 2 * ROOT: + numerator = denominator * integer_part - numerator + denominator = (n - numerator ** 2) / denominator + integer_part = int((ROOT + numerator) / denominator) + period += 1 + return period + + +def solution(n: int = 10000) -> int: + """ + Returns the count of numbers <= 10000 with odd periods. + This function calls continuous_fraction_period for numbers which are + not perfect squares. + This is checked in if sr - floor(sr) != 0 statement. + If an odd period is returned by continuous_fraction_period, + count_odd_periods is increased by 1. + + >>> solution(2) + 1 + >>> solution(5) + 2 + >>> solution(7) + 2 + >>> solution(11) + 3 + >>> solution(13) + 4 + """ + count_odd_periods = 0 + for i in range(2, n + 1): + sr = sqrt(i) + if sr - floor(sr) != 0: + if continuous_fraction_period(i) % 2 == 1: + count_odd_periods += 1 + return count_odd_periods + + +if __name__ == "__main__": + print(f"{solution(int(input().strip()))}") From f81f3b0eaa2f3ca36b618a29a2ca8cdb8a9509cf Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 5 Nov 2020 16:36:59 +0530 Subject: [PATCH 060/195] Cache pre-commit workflow (#3863) --- .github/workflows/pre-commit.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 7002d2d0a21e..96175cfecea5 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -7,6 +7,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cache/pre-commit + ~/.cache/pip + key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - uses: actions/setup-python@v2 - name: Install pre-commit run: | From 0ea3fb0b51fa44acdbbe723c7559695491fb57af Mon Sep 17 00:00:00 2001 From: Ravi Kandasamy Sundaram Date: Fri, 6 Nov 2020 17:55:02 +0100 Subject: [PATCH 061/195] Add solution for Project Euler problem 123 (#3072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Name: Prime square remainders Let pn be the nth prime: 2, 3, 5, 7, 11, ..., and let r be the remainder when (pn−1)^n + (pn+1)^n is divided by pn^2. For example, when n = 3, p3 = 5, and 43 + 63 = 280 ≡ 5 mod 25. The least value of n for which the remainder first exceeds 10^9 is 7037. Find the least value of n for which the remainder first exceeds 10^10. Reference: https://projecteuler.net/problem=123 reference: #2695 Co-authored-by: Ravi Kandasamy Sundaram --- project_euler/problem_123/__init__.py | 0 project_euler/problem_123/sol1.py | 99 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 project_euler/problem_123/__init__.py create mode 100644 project_euler/problem_123/sol1.py diff --git a/project_euler/problem_123/__init__.py b/project_euler/problem_123/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_123/sol1.py b/project_euler/problem_123/sol1.py new file mode 100644 index 000000000000..85350c8bae49 --- /dev/null +++ b/project_euler/problem_123/sol1.py @@ -0,0 +1,99 @@ +""" +Problem 123: https://projecteuler.net/problem=123 + +Name: Prime square remainders + +Let pn be the nth prime: 2, 3, 5, 7, 11, ..., and +let r be the remainder when (pn−1)^n + (pn+1)^n is divided by pn^2. + +For example, when n = 3, p3 = 5, and 43 + 63 = 280 ≡ 5 mod 25. +The least value of n for which the remainder first exceeds 10^9 is 7037. + +Find the least value of n for which the remainder first exceeds 10^10. + + +Solution: + +n=1: (p-1) + (p+1) = 2p +n=2: (p-1)^2 + (p+1)^2 + = p^2 + 1 - 2p + p^2 + 1 + 2p (Using (p+b)^2 = (p^2 + b^2 + 2pb), + (p-b)^2 = (p^2 + b^2 - 2pb) and b = 1) + = 2p^2 + 2 +n=3: (p-1)^3 + (p+1)^3 (Similarly using (p+b)^3 & (p-b)^3 formula and so on) + = 2p^3 + 6p +n=4: 2p^4 + 12p^2 + 2 +n=5: 2p^5 + 20p^3 + 10p + +As you could see, when the expression is divided by p^2. +Except for the last term, the rest will result in the remainder 0. + +n=1: 2p +n=2: 2 +n=3: 6p +n=4: 2 +n=5: 10p + +So it could be simplified as, + r = 2pn when n is odd + r = 2 when n is even. +""" + +from typing import Dict, Generator + + +def sieve() -> Generator[int, None, None]: + """ + Returns a prime number generator using sieve method. + >>> type(sieve()) + + >>> primes = sieve() + >>> next(primes) + 2 + >>> next(primes) + 3 + >>> next(primes) + 5 + >>> next(primes) + 7 + >>> next(primes) + 11 + >>> next(primes) + 13 + """ + factor_map: Dict[int, int] = {} + prime = 2 + while True: + factor = factor_map.pop(prime, None) + if factor: + x = factor + prime + while x in factor_map: + x += factor + factor_map[x] = factor + else: + factor_map[prime * prime] = prime + yield prime + prime += 1 + + +def solution(limit: float = 1e10) -> int: + """ + Returns the least value of n for which the remainder first exceeds 10^10. + >>> solution(1e8) + 2371 + >>> solution(1e9) + 7037 + """ + primes = sieve() + + n = 1 + while True: + prime = next(primes) + if (2 * prime * n) > limit: + return n + # Ignore the next prime as the reminder will be 2. + next(primes) + n += 2 + + +if __name__ == "__main__": + print(solution()) From 3e6845f7ab9139630234bd609cde12eab0fe80c2 Mon Sep 17 00:00:00 2001 From: Frank Schmitt Date: Fri, 6 Nov 2020 18:09:12 +0100 Subject: [PATCH 062/195] Fix handling of non ascii characters in swap case (fixes: #3847) (#3848) * #3847 fix handling of non-ASCII characters in swap_case * #3847 remove unused regex * Fix formatting (with black) Fixes: #3847 * Add type hints for `swap_case` function Co-authored-by: Frank Schmitt Co-authored-by: Dhruv Manilawala --- strings/swap_case.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/strings/swap_case.py b/strings/swap_case.py index 71e8aeb3a205..107fda4b52ec 100644 --- a/strings/swap_case.py +++ b/strings/swap_case.py @@ -11,14 +11,9 @@ GITHUB.COM/MAYUR200 """ -import re -# This re.compile() function saves the pattern from 'a' to 'z' and 'A' to 'Z' -# into 'regexp' variable -regexp = re.compile("[^a-zA-Z]+") - -def swap_case(sentence): +def swap_case(sentence: str) -> str: """ This function will convert all lowercase letters to uppercase letters and vice versa. @@ -30,13 +25,13 @@ def swap_case(sentence): for char in sentence: if char.isupper(): new_string += char.lower() - if char.islower(): + elif char.islower(): new_string += char.upper() - if regexp.search(char): + else: new_string += char return new_string if __name__ == "__main__": - print(swap_case(input("Please input sentence:"))) + print(swap_case(input("Please input sentence: "))) From 191366229ba2a806ab35c79360cbb4491103bc1d Mon Sep 17 00:00:00 2001 From: Simon Landry Date: Sat, 7 Nov 2020 21:56:10 -0500 Subject: [PATCH 063/195] Add project euler problem 50 (#3016) * Add project euler problem 50 * Apply format changes * Descriptive function/parameter name and type hints Co-authored-by: Dhruv Manilawala --- project_euler/problem_050/__init__.py | 0 project_euler/problem_050/sol1.py | 85 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 project_euler/problem_050/__init__.py create mode 100644 project_euler/problem_050/sol1.py diff --git a/project_euler/problem_050/__init__.py b/project_euler/problem_050/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_050/sol1.py b/project_euler/problem_050/sol1.py new file mode 100644 index 000000000000..7d142e5ffc91 --- /dev/null +++ b/project_euler/problem_050/sol1.py @@ -0,0 +1,85 @@ +""" +Project Euler Problem 50: https://projecteuler.net/problem=50 + +Consecutive prime sum + +The prime 41, can be written as the sum of six consecutive primes: +41 = 2 + 3 + 5 + 7 + 11 + 13 + +This is the longest sum of consecutive primes that adds to a prime below +one-hundred. + +The longest sum of consecutive primes below one-thousand that adds to a prime, +contains 21 terms, and is equal to 953. + +Which prime, below one-million, can be written as the sum of the most +consecutive primes? +""" +from typing import List + + +def prime_sieve(limit: int) -> List[int]: + """ + Sieve of Erotosthenes + Function to return all the prime numbers up to a number 'limit' + https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + + >>> prime_sieve(3) + [2] + + >>> prime_sieve(50) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] + """ + is_prime = [True] * limit + is_prime[0] = False + is_prime[1] = False + is_prime[2] = True + + for i in range(3, int(limit ** 0.5 + 1), 2): + index = i * 2 + while index < limit: + is_prime[index] = False + index = index + i + + primes = [2] + + for i in range(3, limit, 2): + if is_prime[i]: + primes.append(i) + + return primes + + +def solution(ceiling: int = 1_000_000) -> int: + """ + Returns the biggest prime, below the celing, that can be written as the sum + of consecutive the most consecutive primes. + + >>> solution(500) + 499 + + >>> solution(1_000) + 953 + + >>> solution(10_000) + 9521 + """ + primes = prime_sieve(ceiling) + length = 0 + largest = 0 + + for i in range(len(primes)): + for j in range(i + length, len(primes)): + sol = sum(primes[i:j]) + if sol >= ceiling: + break + + if sol in primes: + length = j - i + largest = sol + + return largest + + +if __name__ == "__main__": + print(f"{solution() = }") From cfbffee6eaec78987d71e8a8a18e80f8c3273561 Mon Sep 17 00:00:00 2001 From: Lewis Tian Date: Sun, 8 Nov 2020 23:04:01 +0800 Subject: [PATCH 064/195] Update graphs/graph_list.py (#3813) - update naming style to snake_case - add type hints --- graphs/graph_list.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/graphs/graph_list.py b/graphs/graph_list.py index a20940ab1598..a812fecd961e 100644 --- a/graphs/graph_list.py +++ b/graphs/graph_list.py @@ -7,34 +7,34 @@ class AdjacencyList: def __init__(self): - self.List = {} + self.adj_list = {} - def addEdge(self, fromVertex, toVertex): + def add_edge(self, from_vertex: int, to_vertex: int) -> None: # check if vertex is already present - if fromVertex in self.List.keys(): - self.List[fromVertex].append(toVertex) + if from_vertex in self.adj_list: + self.adj_list[from_vertex].append(to_vertex) else: - self.List[fromVertex] = [toVertex] + self.adj_list[from_vertex] = [to_vertex] - def printList(self): - for i in self.List: - print((i, "->", " -> ".join([str(j) for j in self.List[i]]))) + def print_list(self) -> None: + for i in self.adj_list: + print((i, "->", " -> ".join([str(j) for j in self.adj_list[i]]))) if __name__ == "__main__": al = AdjacencyList() - al.addEdge(0, 1) - al.addEdge(0, 4) - al.addEdge(4, 1) - al.addEdge(4, 3) - al.addEdge(1, 0) - al.addEdge(1, 4) - al.addEdge(1, 3) - al.addEdge(1, 2) - al.addEdge(2, 3) - al.addEdge(3, 4) - - al.printList() + al.add_edge(0, 1) + al.add_edge(0, 4) + al.add_edge(4, 1) + al.add_edge(4, 3) + al.add_edge(1, 0) + al.add_edge(1, 4) + al.add_edge(1, 3) + al.add_edge(1, 2) + al.add_edge(2, 3) + al.add_edge(3, 4) + + al.print_list() # OUTPUT: # 0 -> 1 -> 4 From d570389f642066acb62148b3ac1df1e3fc5c8b6a Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sun, 8 Nov 2020 21:31:14 +0530 Subject: [PATCH 065/195] Add config details for the stale action (#3870) --- .github/workflows/stale.yml | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 341153dbf455..41ded1159891 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,23 +1,31 @@ -name: Mark stale issues and pull requests +name: Mark/Close stale issues and pull requests on: schedule: - - cron: "0 0 * * *" + - cron: "0 * * * *" # Run every hour jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: > - Please reopen this issue once you add more information and updates here. - If this is not the case and you need some help, feel free to seek help - from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the - reviewers. Thank you for your contributions! - stale-pr-message: > - Please reopen this pull request once you commit the changes requested - or make improvements on the code. If this is not the case and you need - some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) - or ping one of the reviewers. Thank you for your contributions! - stale-issue-label: 'no-issue-activity' - stale-pr-label: 'no-pr-activity' + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 7 + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + close-issue-message: > + Please reopen this issue once you add more information and updates here. + If this is not the case and you need some help, feel free to seek help + from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the + reviewers. Thank you for your contributions! + stale-pr-message: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + close-pr-message: > + Please reopen this pull request once you commit the changes requested + or make improvements on the code. If this is not the case and you need + some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) + or ping one of the reviewers. Thank you for your contributions! From 85c7adf5e858d85a757a78a29e7a760923793435 Mon Sep 17 00:00:00 2001 From: Shivanirudh Date: Sun, 8 Nov 2020 22:56:22 +0530 Subject: [PATCH 066/195] DPLL algorithm (#3866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DPLL algorithm * Corrections complete * Formatting * Codespell hook * Corrections part 2 * Corrections v2 * Corrections v3 * Update and rename dpll.py to davis–putnam–logemann–loveland.py Co-authored-by: Christian Clauss --- ...42\200\223logemann\342\200\223loveland.py" | 357 ++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 "other/davis\342\200\223putnam\342\200\223logemann\342\200\223loveland.py" diff --git "a/other/davis\342\200\223putnam\342\200\223logemann\342\200\223loveland.py" "b/other/davis\342\200\223putnam\342\200\223logemann\342\200\223loveland.py" new file mode 100644 index 000000000000..d16de6dd988b --- /dev/null +++ "b/other/davis\342\200\223putnam\342\200\223logemann\342\200\223loveland.py" @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 + +""" +Davis–Putnam–Logemann–Loveland (DPLL) algorithm is a complete, backtracking-based +search algorithm for deciding the satisfiability of propositional logic formulae in +conjunctive normal form, i.e, for solving the Conjunctive Normal Form SATisfiability +(CNF-SAT) problem. + +For more information about the algorithm: https://en.wikipedia.org/wiki/DPLL_algorithm +""" + +import random +from typing import Dict, List + + +class Clause: + """ + A clause represented in Conjunctive Normal Form. + A clause is a set of literals, either complemented or otherwise. + For example: + {A1, A2, A3'} is the clause (A1 v A2 v A3') + {A5', A2', A1} is the clause (A5' v A2' v A1) + + Create model + >>> clause = Clause(["A1", "A2'", "A3"]) + >>> clause.evaluate({"A1": True}) + True + """ + + def __init__(self, literals: List[int]) -> None: + """ + Represent the literals and an assignment in a clause." + """ + # Assign all literals to None initially + self.literals = {literal: None for literal in literals} + + def __str__(self) -> str: + """ + To print a clause as in Conjunctive Normal Form. + >>> str(Clause(["A1", "A2'", "A3"])) + "{A1 , A2' , A3}" + """ + return "{" + " , ".join(self.literals) + "}" + + def __len__(self) -> int: + """ + To print a clause as in Conjunctive Normal Form. + >>> len(Clause([])) + 0 + >>> len(Clause(["A1", "A2'", "A3"])) + 3 + """ + return len(self.literals) + + def assign(self, model: Dict[str, bool]) -> None: + """ + Assign values to literals of the clause as given by model. + """ + for literal in self.literals: + symbol = literal[:2] + if symbol in model: + value = model[symbol] + else: + continue + if value is not None: + # Complement assignment if literal is in complemented form + if literal.endswith("'"): + value = not value + self.literals[literal] = value + + def evaluate(self, model: Dict[str, bool]) -> bool: + """ + Evaluates the clause with the assignments in model. + This has the following steps: + 1. Return True if both a literal and its complement exist in the clause. + 2. Return True if a single literal has the assignment True. + 3. Return None(unable to complete evaluation) if a literal has no assignment. + 4. Compute disjunction of all values assigned in clause. + """ + for literal in self.literals: + symbol = literal.rstrip("'") if literal.endswith("'") else literal + "'" + if symbol in self.literals: + return True + + self.assign(model) + for value in self.literals.values(): + if value in (True, None): + return value + return any(self.literals.values()) + + +class Formula: + """ + A formula represented in Conjunctive Normal Form. + A formula is a set of clauses. + For example, + {{A1, A2, A3'}, {A5', A2', A1}} is ((A1 v A2 v A3') and (A5' v A2' v A1)) + """ + + def __init__(self, clauses: List[Clause]) -> None: + """ + Represent the number of clauses and the clauses themselves. + """ + self.clauses = list(clauses) + + def __str__(self) -> str: + """ + To print a formula as in Conjunctive Normal Form. + str(Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])])) + "{{A1 , A2' , A3} , {A5' , A2' , A1}}" + """ + return "{" + " , ".join(str(clause) for clause in self.clauses) + "}" + + +def generate_clause() -> Clause: + """ + Randomly generate a clause. + All literals have the name Ax, where x is an integer from 1 to 5. + """ + literals = [] + no_of_literals = random.randint(1, 5) + base_var = "A" + i = 0 + while i < no_of_literals: + var_no = random.randint(1, 5) + var_name = base_var + str(var_no) + var_complement = random.randint(0, 1) + if var_complement == 1: + var_name += "'" + if var_name in literals: + i -= 1 + else: + literals.append(var_name) + i += 1 + return Clause(literals) + + +def generate_formula() -> Formula: + """ + Randomly generate a formula. + """ + clauses = set() + no_of_clauses = random.randint(1, 10) + while len(clauses) < no_of_clauses: + clauses.add(generate_clause()) + return Formula(set(clauses)) + + +def generate_parameters(formula: Formula) -> (List[Clause], List[str]): + """ + Return the clauses and symbols from a formula. + A symbol is the uncomplemented form of a literal. + For example, + Symbol of A3 is A3. + Symbol of A5' is A5. + + >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) + >>> clauses, symbols = generate_parameters(formula) + >>> clauses_list = [str(i) for i in clauses] + >>> clauses_list + ["{A1 , A2' , A3}", "{A5' , A2' , A1}"] + >>> symbols + ['A1', 'A2', 'A3', 'A5'] + """ + clauses = formula.clauses + symbols_set = [] + for clause in formula.clauses: + for literal in clause.literals: + symbol = literal[:2] + if symbol not in symbols_set: + symbols_set.append(symbol) + return clauses, symbols_set + + +def find_pure_symbols( + clauses: List[Clause], symbols: List[str], model: Dict[str, bool] +) -> (List[str], Dict[str, bool]): + """ + Return pure symbols and their values to satisfy clause. + Pure symbols are symbols in a formula that exist only + in one form, either complemented or otherwise. + For example, + { { A4 , A3 , A5' , A1 , A3' } , { A4 } , { A3 } } has + pure symbols A4, A5' and A1. + This has the following steps: + 1. Ignore clauses that have already evaluated to be True. + 2. Find symbols that occur only in one form in the rest of the clauses. + 3. Assign value True or False depending on whether the symbols occurs + in normal or complemented form respectively. + + >>> formula = Formula([Clause(["A1", "A2'", "A3"]), Clause(["A5'", "A2'", "A1"])]) + >>> clauses, symbols = generate_parameters(formula) + + >>> pure_symbols, values = find_pure_symbols(clauses, symbols, {}) + >>> pure_symbols + ['A1', 'A2', 'A3', 'A5'] + >>> values + {'A1': True, 'A2': False, 'A3': True, 'A5': False} + """ + pure_symbols = [] + assignment = dict() + literals = [] + + for clause in clauses: + if clause.evaluate(model) is True: + continue + for literal in clause.literals: + literals.append(literal) + + for s in symbols: + sym = s + "'" + if (s in literals and sym not in literals) or ( + s not in literals and sym in literals + ): + pure_symbols.append(s) + for p in pure_symbols: + assignment[p] = None + for s in pure_symbols: + sym = s + "'" + if s in literals: + assignment[s] = True + elif sym in literals: + assignment[s] = False + return pure_symbols, assignment + + +def find_unit_clauses( + clauses: List[Clause], model: Dict[str, bool] +) -> (List[str], Dict[str, bool]): + """ + Returns the unit symbols and their values to satisfy clause. + Unit symbols are symbols in a formula that are: + - Either the only symbol in a clause + - Or all other literals in that clause have been assigned False + This has the following steps: + 1. Find symbols that are the only occurrences in a clause. + 2. Find symbols in a clause where all other literals are assigned False. + 3. Assign True or False depending on whether the symbols occurs in + normal or complemented form respectively. + + >>> clause1 = Clause(["A4", "A3", "A5'", "A1", "A3'"]) + >>> clause2 = Clause(["A4"]) + >>> clause3 = Clause(["A3"]) + >>> clauses, symbols = generate_parameters(Formula([clause1, clause2, clause3])) + + >>> unit_clauses, values = find_unit_clauses(clauses, {}) + >>> unit_clauses + ['A4', 'A3'] + >>> values + {'A4': True, 'A3': True} + """ + unit_symbols = [] + for clause in clauses: + if len(clause) == 1: + unit_symbols.append(list(clause.literals.keys())[0]) + else: + Fcount, Ncount = 0, 0 + for literal, value in clause.literals.items(): + if value is False: + Fcount += 1 + elif value is None: + sym = literal + Ncount += 1 + if Fcount == len(clause) - 1 and Ncount == 1: + unit_symbols.append(sym) + assignment = dict() + for i in unit_symbols: + symbol = i[:2] + assignment[symbol] = len(i) == 2 + unit_symbols = [i[:2] for i in unit_symbols] + + return unit_symbols, assignment + + +def dpll_algorithm( + clauses: List[Clause], symbols: List[str], model: Dict[str, bool] +) -> (bool, Dict[str, bool]): + """ + Returns the model if the formula is satisfiable, else None + This has the following steps: + 1. If every clause in clauses is True, return True. + 2. If some clause in clauses is False, return False. + 3. Find pure symbols. + 4. Find unit symbols. + + >>> formula = Formula([Clause(["A4", "A3", "A5'", "A1", "A3'"]), Clause(["A4"])]) + >>> clauses, symbols = generate_parameters(formula) + + >>> soln, model = dpll_algorithm(clauses, symbols, {}) + >>> soln + True + >>> model + {'A4': True} + """ + check_clause_all_true = True + for clause in clauses: + clause_check = clause.evaluate(model) + if clause_check is False: + return False, None + elif clause_check is None: + check_clause_all_true = False + continue + + if check_clause_all_true: + return True, model + + try: + pure_symbols, assignment = find_pure_symbols(clauses, symbols, model) + except RecursionError: + print("raises a RecursionError and is") + return None, {} + P = None + if len(pure_symbols) > 0: + P, value = pure_symbols[0], assignment[pure_symbols[0]] + + if P: + tmp_model = model + tmp_model[P] = value + tmp_symbols = [i for i in symbols] + if P in tmp_symbols: + tmp_symbols.remove(P) + return dpll_algorithm(clauses, tmp_symbols, tmp_model) + + unit_symbols, assignment = find_unit_clauses(clauses, model) + P = None + if len(unit_symbols) > 0: + P, value = unit_symbols[0], assignment[unit_symbols[0]] + if P: + tmp_model = model + tmp_model[P] = value + tmp_symbols = [i for i in symbols] + if P in tmp_symbols: + tmp_symbols.remove(P) + return dpll_algorithm(clauses, tmp_symbols, tmp_model) + P = symbols[0] + rest = symbols[1:] + tmp1, tmp2 = model, model + tmp1[P], tmp2[P] = True, False + + return dpll_algorithm(clauses, rest, tmp1) or dpll_algorithm(clauses, rest, tmp2) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + formula = generate_formula() + print(f"The formula {formula} is", end=" ") + + clauses, symbols = generate_parameters(formula) + solution, model = dpll_algorithm(clauses, symbols, {}) + + if solution: + print(f"satisfiable with the assignment {model}.") + else: + print("not satisfiable.") From 61f122ecfdbdb91d292e27c23edea6720e108f28 Mon Sep 17 00:00:00 2001 From: Prakhar Gurunani Date: Tue, 10 Nov 2020 12:50:27 +0530 Subject: [PATCH 067/195] Add molecular_chemistry.py (#2944) * Create molecular_chemistry.py * round up outputs * Remove floating point * Add Wikipedia references * fixup! Format Python code with psf/black push * Add Conversions/Molecular Chemistry * updating DIRECTORY.md * Update molecular_chemistry.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + conversions/molecular_chemistry.py | 92 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 conversions/molecular_chemistry.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 0cecae28d51d..fd45eacaad9b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -100,6 +100,7 @@ * [Decimal To Hexadecimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_hexadecimal.py) * [Decimal To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_octal.py) * [Hexadecimal To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/hexadecimal_to_decimal.py) + * [Molecular Chemistry](https://github.com/TheAlgorithms/Python/blob/master/conversions/molecular_chemistry.py) * [Prefix Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/prefix_conversions.py) * [Roman To Integer](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_to_integer.py) * [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py) diff --git a/conversions/molecular_chemistry.py b/conversions/molecular_chemistry.py new file mode 100644 index 000000000000..8c68459965b0 --- /dev/null +++ b/conversions/molecular_chemistry.py @@ -0,0 +1,92 @@ +""" +Functions useful for doing molecular chemistry: +* molarity_to_normality +* moles_to_pressure +* moles_to_volume +* pressure_and_volume_to_temperature +""" + + +def molarity_to_normality(nfactor: int, moles: float, volume: float) -> float: + """ + Convert molarity to normality. + Volume is taken in litres. + + Wikipedia reference: https://en.wikipedia.org/wiki/Equivalent_concentration + Wikipedia reference: https://en.wikipedia.org/wiki/Molar_concentration + + >>> molarity_to_normality(2, 3.1, 0.31) + 20 + >>> molarity_to_normality(4, 11.4, 5.7) + 8 + """ + return round((float(moles / volume) * nfactor)) + + +def moles_to_pressure(volume: float, moles: float, temperature: float) -> float: + """ + Convert moles to pressure. + Ideal gas laws are used. + Temperature is taken in kelvin. + Volume is taken in litres. + Pressure has atm as SI unit. + + Wikipedia reference: https://en.wikipedia.org/wiki/Gas_laws + Wikipedia reference: https://en.wikipedia.org/wiki/Pressure + Wikipedia reference: https://en.wikipedia.org/wiki/Temperature + + >>> moles_to_pressure(0.82, 3, 300) + 90 + >>> moles_to_pressure(8.2, 5, 200) + 10 + """ + return round(float((moles * 0.0821 * temperature) / (volume))) + + +def moles_to_volume(pressure: float, moles: float, temperature: float) -> float: + """ + Convert moles to volume. + Ideal gas laws are used. + Temperature is taken in kelvin. + Volume is taken in litres. + Pressure has atm as SI unit. + + Wikipedia reference: https://en.wikipedia.org/wiki/Gas_laws + Wikipedia reference: https://en.wikipedia.org/wiki/Pressure + Wikipedia reference: https://en.wikipedia.org/wiki/Temperature + + >>> moles_to_volume(0.82, 3, 300) + 90 + >>> moles_to_volume(8.2, 5, 200) + 10 + """ + return round(float((moles * 0.0821 * temperature) / (pressure))) + + +def pressure_and_volume_to_temperature( + pressure: float, moles: float, volume: float +) -> float: + """ + Convert pressure and volume to temperature. + Ideal gas laws are used. + Temperature is taken in kelvin. + Volume is taken in litres. + Pressure has atm as SI unit. + + Wikipedia reference: https://en.wikipedia.org/wiki/Gas_laws + Wikipedia reference: https://en.wikipedia.org/wiki/Pressure + Wikipedia reference: https://en.wikipedia.org/wiki/Temperature + + >>> pressure_and_volume_to_temperature(0.82, 1, 2) + 20 + >>> pressure_and_volume_to_temperature(8.2, 5, 3) + 60 + """ + return round(float((pressure * volume) / (0.0821 * moles))) + + +if __name__ == "__main__": + + import doctest + + doctest.testmod() From d7b58067c9caeec2ea65ea0671f4df7cb35b0e9a Mon Sep 17 00:00:00 2001 From: poloso Date: Tue, 10 Nov 2020 21:35:11 -0500 Subject: [PATCH 068/195] Reduce complexity linear_discriminant_analysis. (#2452) * Reduce complexity linear_discriminant_analysis. * Fix whitespace * Update machine_learning/linear_discriminant_analysis.py Co-authored-by: Dhruv Manilawala * fixup! Format Python code with psf/black push * Fix format to surpass pre-commit tests * updating DIRECTORY.md * Update machine_learning/linear_discriminant_analysis.py Co-authored-by: Dhruv Manilawala * fixup! Format Python code with psf/black push Co-authored-by: Dhruv Manilawala Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .../linear_discriminant_analysis.py | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 22ee63a5a62b..0d19e970e973 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -2,6 +2,7 @@ Linear Discriminant Analysis + Assumptions About Data : 1. The input variables has a gaussian distribution. 2. The variance calculated for each input variables by class grouping is the @@ -44,6 +45,7 @@ from math import log from os import name, system from random import gauss, seed +from typing import Callable, TypeVar # Make a training dataset drawn from a gaussian distribution @@ -245,6 +247,40 @@ def accuracy(actual_y: list, predicted_y: list) -> float: return (correct / len(actual_y)) * 100 +num = TypeVar("num") + + +def valid_input( + input_type: Callable[[object], num], # Usually float or int + input_msg: str, + err_msg: str, + condition: Callable[[num], bool] = lambda x: True, + default: str = None, +) -> num: + """ + Ask for user value and validate that it fulfill a condition. + + :input_type: user input expected type of value + :input_msg: message to show user in the screen + :err_msg: message to show in the screen in case of error + :condition: function that represents the condition that user input is valid. + :default: Default value in case the user does not type anything + :return: user's input + """ + while True: + try: + user_input = input_type(input(input_msg).strip() or default) + if condition(user_input): + return user_input + else: + print(f"{user_input}: {err_msg}") + continue + except ValueError: + print( + f"{user_input}: Incorrect input type, expected {input_type.__name__!r}" + ) + + # Main Function def main(): """ This function starts execution phase """ @@ -254,48 +290,26 @@ def main(): print("First of all we should specify the number of classes that") print("we want to generate as training dataset") # Trying to get number of classes - n_classes = 0 - while True: - try: - user_input = int( - input("Enter the number of classes (Data Groupings): ").strip() - ) - if user_input > 0: - n_classes = user_input - break - else: - print( - f"Your entered value is {user_input} , Number of classes " - f"should be positive!" - ) - continue - except ValueError: - print("Your entered value is not numerical!") + n_classes = valid_input( + input_type=int, + condition=lambda x: x > 0, + input_msg="Enter the number of classes (Data Groupings): ", + err_msg="Number of classes should be positive!", + ) print("-" * 100) - std_dev = 1.0 # Default value for standard deviation of dataset # Trying to get the value of standard deviation - while True: - try: - user_sd = float( - input( - "Enter the value of standard deviation" - "(Default value is 1.0 for all classes): " - ).strip() - or "1.0" - ) - if user_sd >= 0.0: - std_dev = user_sd - break - else: - print( - f"Your entered value is {user_sd}, Standard deviation should " - f"not be negative!" - ) - continue - except ValueError: - print("Your entered value is not numerical!") + std_dev = valid_input( + input_type=float, + condition=lambda x: x >= 0, + input_msg=( + "Enter the value of standard deviation" + "(Default value is 1.0 for all classes): " + ), + err_msg="Standard deviation should not be negative!", + default="1.0", + ) print("-" * 100) @@ -303,38 +317,24 @@ def main(): # dataset counts = [] # An empty list to store instance counts of classes in dataset for i in range(n_classes): - while True: - try: - user_count = int( - input(f"Enter The number of instances for class_{i+1}: ") - ) - if user_count > 0: - counts.append(user_count) - break - else: - print( - f"Your entered value is {user_count}, Number of " - "instances should be positive!" - ) - continue - except ValueError: - print("Your entered value is not numerical!") + user_count = valid_input( + input_type=int, + condition=lambda x: x > 0, + input_msg=(f"Enter The number of instances for class_{i+1}: "), + err_msg="Number of instances should be positive!", + ) + counts.append(user_count) print("-" * 100) # An empty list to store values of user-entered means of classes user_means = [] for a in range(n_classes): - while True: - try: - user_mean = float( - input(f"Enter the value of mean for class_{a+1}: ") - ) - if isinstance(user_mean, float): - user_means.append(user_mean) - break - print(f"You entered an invalid value: {user_mean}") - except ValueError: - print("Your entered value is not numerical!") + user_mean = valid_input( + input_type=float, + input_msg=(f"Enter the value of mean for class_{a+1}: "), + err_msg="This is an invalid value.", + ) + user_means.append(user_mean) print("-" * 100) print("Standard deviation: ", std_dev) From a61c1af0718c75a5f7e9412d95f73267a870b82d Mon Sep 17 00:00:00 2001 From: Cho Yin Yong Date: Wed, 11 Nov 2020 06:17:54 -0500 Subject: [PATCH 069/195] Peak of unimodal list DNC algorithm (#3691) * Peak of unimodal list DNC algorithm * fix black formatting issues * add doctest testing * make file black compliant --- divide_and_conquer/peak.py | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 divide_and_conquer/peak.py diff --git a/divide_and_conquer/peak.py b/divide_and_conquer/peak.py new file mode 100644 index 000000000000..f94f83ed3fcb --- /dev/null +++ b/divide_and_conquer/peak.py @@ -0,0 +1,53 @@ +""" +Finding the peak of a unimodal list using divide and conquer. +A unimodal array is defined as follows: array is increasing up to index p, +then decreasing afterwards. (for p >= 1) +An obvious solution can be performed in O(n), +to find the maximum of the array. +(From Kleinberg and Tardos. Algorithm Design. +Addison Wesley 2006: Chapter 5 Solved Exercise 1) +""" +from typing import List + + +def peak(lst: List[int]) -> int: + """ + Return the peak value of `lst`. + >>> peak([1, 2, 3, 4, 5, 4, 3, 2, 1]) + 5 + >>> peak([1, 10, 9, 8, 7, 6, 5, 4]) + 10 + >>> peak([1, 9, 8, 7]) + 9 + >>> peak([1, 2, 3, 4, 5, 6, 7, 0]) + 7 + >>> peak([1, 2, 3, 4, 3, 2, 1, 0, -1, -2]) + 4 + """ + # middle index + m = len(lst) // 2 + + # choose the middle 3 elements + three = lst[m - 1 : m + 2] + + # if middle element is peak + if three[1] > three[0] and three[1] > three[2]: + return three[1] + + # if increasing, recurse on right + elif three[0] < three[2]: + if len(lst[:m]) == 2: + m -= 1 + return peak(lst[m:]) + + # decreasing + else: + if len(lst[:m]) == 2: + m += 1 + return peak(lst[:m]) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ec1fad0f03fb555846370a4bd80b28972c562b79 Mon Sep 17 00:00:00 2001 From: Rupansh Date: Wed, 11 Nov 2020 16:54:31 +0530 Subject: [PATCH 070/195] Add Quantum Full Adder circuit for classical integers (#2954) * requirements: add qiskit major library required for quantum computing * quantum: add quantum ripple adder implementation right now we are essentially performing the same task as a classic ripple adder Signed-off-by: rupansh-arch --- quantum/ripple_adder_classic.py | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 quantum/ripple_adder_classic.py diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py new file mode 100644 index 000000000000..f5b0a980c8e2 --- /dev/null +++ b/quantum/ripple_adder_classic.py @@ -0,0 +1,108 @@ +# https://github.com/rupansh/QuantumComputing/blob/master/rippleadd.py +# https://en.wikipedia.org/wiki/Adder_(electronics)#Full_adder +# https://en.wikipedia.org/wiki/Controlled_NOT_gate + +from qiskit import Aer, QuantumCircuit, execute +from qiskit.providers import BaseBackend + + +def store_two_classics(val1: int, val2: int) -> (QuantumCircuit, str, str): + """ + Generates a Quantum Circuit which stores two classical integers + Returns the circuit and binary representation of the integers + """ + x, y = bin(val1)[2:], bin(val2)[2:] # Remove leading '0b' + + # Ensure that both strings are of the same length + if len(x) > len(y): + y = y.zfill(len(x)) + else: + x = x.zfill(len(y)) + + # We need (3 * number of bits in the larger number)+1 qBits + # The second parameter is the number of classical registers, to measure the result + circuit = QuantumCircuit((len(x) * 3) + 1, len(x) + 1) + + # We are essentially "not-ing" the bits that are 1 + # Reversed because its easier to perform ops on more significant bits + for i in range(len(x)): + if x[::-1][i] == "1": + circuit.x(i) + for j in range(len(y)): + if y[::-1][j] == "1": + circuit.x(len(x) + j) + + return circuit, x, y + + +def full_adder( + circuit: QuantumCircuit, + input1_loc: int, + input2_loc: int, + carry_in: int, + carry_out: int, +): + """ + Quantum Equivalent of a Full Adder Circuit + CX/CCX is like 2-way/3-way XOR + """ + circuit.ccx(input1_loc, input2_loc, carry_out) + circuit.cx(input1_loc, input2_loc) + circuit.ccx(input2_loc, carry_in, carry_out) + circuit.cx(input2_loc, carry_in) + circuit.cx(input1_loc, input2_loc) + + +def ripple_adder( + val1: int, val2: int, backend: BaseBackend = Aer.get_backend("qasm_simulator") +) -> int: + """ + Quantum Equivalent of a Ripple Adder Circuit + Uses qasm_simulator backend by default + + Currently only adds 'emulated' Classical Bits + but nothing prevents us from doing this with hadamard'd bits :) + + Only supports adding +ve Integers + + >>> ripple_adder(3, 4) + 7 + >>> ripple_adder(10, 4) + 14 + >>> ripple_adder(-1, 10) + Traceback (most recent call last): + ... + ValueError: Both Integers must be positive! + """ + + if val1 < 0 or val2 < 0: + raise ValueError("Both Integers must be positive!") + + # Store the Integers + circuit, x, y = store_two_classics(val1, val2) + + """ + We are essentially using each bit of x & y respectively as full_adder's input + the carry_input is used from the previous circuit (for circuit num > 1) + + the carry_out is just below carry_input because + it will be essentially the carry_input for the next full_adder + """ + for i in range(len(x)): + full_adder(circuit, i, len(x) + i, len(x) + len(y) + i, len(x) + len(y) + i + 1) + circuit.barrier() # Optional, just for aesthetics + + # Measure the resultant qBits + for i in range(len(x) + 1): + circuit.measure([(len(x) * 2) + i], [i]) + + res = execute(circuit, backend, shots=1).result() + + # The result is in binary. Convert it back to int + return int(list(res.get_counts().keys())[0], 2) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ac42b46df66c40ac04fa09ec246a0ea7c2f5dd5c Mon Sep 17 00:00:00 2001 From: boyuuuun <44187125+boyuuuun@users.noreply.github.com> Date: Fri, 13 Nov 2020 22:55:23 +0900 Subject: [PATCH 071/195] add crawl_google_scholar_citation.py (#3879) * add crawl_google_scholar_citation.py * pass flack8 * pass isort * pass isort * change comment in main * modify main code * delete file * change how to build url * add a key 'hl' in params dict * Update crawl_google_scholar_citation.py * Create crawl_google_results.py * codespell: Mater Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- .../crawl_google_scholar_citation.py | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 web_programming/crawl_google_scholar_citation.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 01da6cad0335..a3288e1c5eef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: hooks: - id: codespell args: - - --ignore-words-list=ans,fo,followings,hist,iff,secant,som,tim + - --ignore-words-list=ans,fo,followings,hist,iff,mater,secant,som,tim - --skip="./.*,./other/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" - --quiet-level=2 exclude: | diff --git a/web_programming/crawl_google_scholar_citation.py b/web_programming/crawl_google_scholar_citation.py new file mode 100644 index 000000000000..d023380c0818 --- /dev/null +++ b/web_programming/crawl_google_scholar_citation.py @@ -0,0 +1,32 @@ +""" +Get the citation from google scholar +using title and year of publication, and volume and pages of journal. +""" + +import requests +from bs4 import BeautifulSoup + + +def get_citation(base_url: str, params: dict) -> str: + """ + Return the citation number. + """ + soup = BeautifulSoup(requests.get(base_url, params=params).content, "html.parser") + div = soup.find("div", attrs={"class": "gs_ri"}) + anchors = div.find("div", attrs={"class": "gs_fl"}).find_all("a") + return anchors[2].get_text() + + +if __name__ == "__main__": + params = { + "title": ( + "Precisely geometry controlled microsupercapacitors for ultrahigh areal " + "capacitance, volumetric capacitance, and energy density" + ), + "journal": "Chem. Mater.", + "volume": 30, + "pages": "3979-3990", + "year": 2018, + "hl": "en", + } + print(get_citation("http://scholar.google.com/scholar_lookup", params=params)) From 20c80ece73823330ef8c7e6761b106a0ddf4e187 Mon Sep 17 00:00:00 2001 From: Steve Kim <54872857+SteveKimSR@users.noreply.github.com> Date: Fri, 13 Nov 2020 23:26:17 +0900 Subject: [PATCH 072/195] add similarity_search.py in machine_learning (#3864) * add similarity_search.py in machine_learning adding similarity_search algorithm in machine_learning * fix pre-commit test, apply feedback isort, codespell changed. applied feedback(np -> np.ndarray) * apply feedback add type hints to euclidean method * apply feedback - changed euclidean's type hints - changed few TypeError to ValueError - changed range(len()) to enumerate() - changed error's strings to f-string - implemented without type() - add euclidean's explanation * apply feedback - deleted try/catch in euclidean - added error tests - name change(value -> value_array) * # doctest: +NORMALIZE_WHITESPACE * Update machine_learning/similarity_search.py * placate flake8 Co-authored-by: Christian Clauss --- machine_learning/similarity_search.py | 137 ++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 machine_learning/similarity_search.py diff --git a/machine_learning/similarity_search.py b/machine_learning/similarity_search.py new file mode 100644 index 000000000000..6bfb12ed88cb --- /dev/null +++ b/machine_learning/similarity_search.py @@ -0,0 +1,137 @@ +""" +Similarity Search : https://en.wikipedia.org/wiki/Similarity_search +Similarity search is a search algorithm for finding the nearest vector from +vectors, used in natural language processing. +In this algorithm, it calculates distance with euclidean distance and +returns a list containing two data for each vector: + 1. the nearest vector + 2. distance between the vector and the nearest vector (float) +""" +import math + +import numpy as np + + +def euclidean(input_a: np.ndarray, input_b: np.ndarray) -> float: + """ + Calculates euclidean distance between two data. + :param input_a: ndarray of first vector. + :param input_b: ndarray of second vector. + :return: Euclidean distance of input_a and input_b. By using math.sqrt(), + result will be float. + + >>> euclidean(np.array([0]), np.array([1])) + 1.0 + >>> euclidean(np.array([0, 1]), np.array([1, 1])) + 1.0 + >>> euclidean(np.array([0, 0, 0]), np.array([0, 0, 1])) + 1.0 + """ + return math.sqrt(sum(pow(a - b, 2) for a, b in zip(input_a, input_b))) + + +def similarity_search(dataset: np.ndarray, value_array: np.ndarray) -> list: + """ + :param dataset: Set containing the vectors. Should be ndarray. + :param value_array: vector/vectors we want to know the nearest vector from dataset. + :return: Result will be a list containing + 1. the nearest vector + 2. distance from the vector + + >>> dataset = np.array([[0], [1], [2]]) + >>> value_array = np.array([[0]]) + >>> similarity_search(dataset, value_array) + [[[0], 0.0]] + + >>> dataset = np.array([[0, 0], [1, 1], [2, 2]]) + >>> value_array = np.array([[0, 1]]) + >>> similarity_search(dataset, value_array) + [[[0, 0], 1.0]] + + >>> dataset = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2]]) + >>> value_array = np.array([[0, 0, 1]]) + >>> similarity_search(dataset, value_array) + [[[0, 0, 0], 1.0]] + + >>> dataset = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2]]) + >>> value_array = np.array([[0, 0, 0], [0, 0, 1]]) + >>> similarity_search(dataset, value_array) + [[[0, 0, 0], 0.0], [[0, 0, 0], 1.0]] + + These are the errors that might occur: + + 1. If dimensions are different. + For example, dataset has 2d array and value_array has 1d array: + >>> dataset = np.array([[1]]) + >>> value_array = np.array([1]) + >>> similarity_search(dataset, value_array) + Traceback (most recent call last): + ... + ValueError: Wrong input data's dimensions... dataset : 2, value_array : 1 + + 2. If data's shapes are different. + For example, dataset has shape of (3, 2) and value_array has (2, 3). + We are expecting same shapes of two arrays, so it is wrong. + >>> dataset = np.array([[0, 0], [1, 1], [2, 2]]) + >>> value_array = np.array([[0, 0, 0], [0, 0, 1]]) + >>> similarity_search(dataset, value_array) + Traceback (most recent call last): + ... + ValueError: Wrong input data's shape... dataset : 2, value_array : 3 + + 3. If data types are different. + When trying to compare, we are expecting same types so they should be same. + If not, it'll come up with errors. + >>> dataset = np.array([[0, 0], [1, 1], [2, 2]], dtype=np.float32) + >>> value_array = np.array([[0, 0], [0, 1]], dtype=np.int32) + >>> similarity_search(dataset, value_array) # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: Input data have different datatype... + dataset : float32, value_array : int32 + """ + + if dataset.ndim != value_array.ndim: + raise ValueError( + f"Wrong input data's dimensions... dataset : {dataset.ndim}, " + f"value_array : {value_array.ndim}" + ) + + try: + if dataset.shape[1] != value_array.shape[1]: + raise ValueError( + f"Wrong input data's shape... dataset : {dataset.shape[1]}, " + f"value_array : {value_array.shape[1]}" + ) + except IndexError: + if dataset.ndim != value_array.ndim: + raise TypeError("Wrong shape") + + if dataset.dtype != value_array.dtype: + raise TypeError( + f"Input data have different datatype... dataset : {dataset.dtype}, " + f"value_array : {value_array.dtype}" + ) + + answer = [] + + for value in value_array: + dist = euclidean(value, dataset[0]) + vector = dataset[0].tolist() + + for dataset_value in dataset[1:]: + temp_dist = euclidean(value, dataset_value) + + if dist > temp_dist: + dist = temp_dist + vector = dataset_value.tolist() + + answer.append([vector, dist]) + + return answer + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From f2ce71a2f9b16462de9fb1644952739742e89f71 Mon Sep 17 00:00:00 2001 From: poloso Date: Sat, 14 Nov 2020 12:04:29 -0500 Subject: [PATCH 073/195] Add type hints and tests. (#2461) * Add type hints, documentation and tests. * Update searches/ternary_search.py Sort collection and remove the assertion logic. Co-authored-by: Christian Clauss * Remove assert sorted logic. * Add assertion list is ordered. * updating DIRECTORY.md * updating DIRECTORY.md * Format with black. * Change names of variables to descriptive names * Remove print in doctests * Fix variables to snake_case notation. Co-authored-by: Christian Clauss Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 9 ++ searches/ternary_search.py | 199 ++++++++++++++++++++++++------------- 2 files changed, 141 insertions(+), 67 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index fd45eacaad9b..89bedfb61592 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -206,6 +206,7 @@ * [Heaps Algorithm](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/heaps_algorithm.py) * [Heaps Algorithm Iterative](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/heaps_algorithm_iterative.py) * [Inversions](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/inversions.py) + * [Kth Order Statistic](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/kth_order_statistic.py) * [Max Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/max_subarray_sum.py) * [Mergesort](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/mergesort.py) * [Power](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/power.py) @@ -390,6 +391,7 @@ * [Chudnovsky Algorithm](https://github.com/TheAlgorithms/Python/blob/master/maths/chudnovsky_algorithm.py) * [Collatz Sequence](https://github.com/TheAlgorithms/Python/blob/master/maths/collatz_sequence.py) * [Combinations](https://github.com/TheAlgorithms/Python/blob/master/maths/combinations.py) + * [Decimal Isolate](https://github.com/TheAlgorithms/Python/blob/master/maths/decimal_isolate.py) * [Entropy](https://github.com/TheAlgorithms/Python/blob/master/maths/entropy.py) * [Eulers Totient](https://github.com/TheAlgorithms/Python/blob/master/maths/eulers_totient.py) * [Explicit Euler](https://github.com/TheAlgorithms/Python/blob/master/maths/explicit_euler.py) @@ -681,6 +683,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_062/sol1.py) * Problem 063 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_063/sol1.py) + * Problem 064 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_064/sol1.py) * Problem 065 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_065/sol1.py) * Problem 067 @@ -694,6 +698,7 @@ * [Sol2](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_072/sol2.py) * Problem 074 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_074/sol1.py) + * [Sol2](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_074/sol2.py) * Problem 075 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_075/sol1.py) * Problem 076 @@ -726,12 +731,16 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) * Problem 191 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_191/sol1.py) + * Problem 203 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_203/sol1.py) * Problem 206 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_206/sol1.py) * Problem 207 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_207/sol1.py) * Problem 234 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_234/sol1.py) + * Problem 301 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_301/sol1.py) * Problem 551 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_551/sol1.py) diff --git a/searches/ternary_search.py b/searches/ternary_search.py index b01db3eb845f..9422a4ccb966 100644 --- a/searches/ternary_search.py +++ b/searches/ternary_search.py @@ -6,7 +6,6 @@ Time Complexity : O(log3 N) Space Complexity : O(1) """ -import sys from typing import List # This is the precision for this function which can be altered. @@ -15,90 +14,156 @@ # This is the linear search that will occur after the search space has become smaller. -def lin_search(left: int, right: int, A: List[int], target: int): - for i in range(left, right + 1): - if A[i] == target: - return i -# This is the iterative method of the ternary search algorithm. -def ite_ternary_search(A: List[int], target: int): - left = 0 - right = len(A) - 1 - while True: - if left < right: +def lin_search(left: int, right: int, array: List[int], target: int) -> int: + """Perform linear search in list. Returns -1 if element is not found. + + Parameters + ---------- + left : int + left index bound. + right : int + right index bound. + array : List[int] + List of elements to be searched on + target : int + Element that is searched + + Returns + ------- + int + index of element that is looked for. + + Examples + -------- + >>> lin_search(0, 4, [4, 5, 6, 7], 7) + 3 + >>> lin_search(0, 3, [4, 5, 6, 7], 7) + -1 + >>> lin_search(0, 2, [-18, 2], -18) + 0 + >>> lin_search(0, 1, [5], 5) + 0 + >>> lin_search(0, 3, ['a', 'c', 'd'], 'c') + 1 + >>> lin_search(0, 3, [.1, .4 , -.1], .1) + 0 + >>> lin_search(0, 3, [.1, .4 , -.1], -.1) + 2 + """ + for i in range(left, right): + if array[i] == target: + return i + return -1 + + +def ite_ternary_search(array: List[int], target: int) -> int: + """Iterative method of the ternary search algorithm. + >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] + >>> ite_ternary_search(test_list, 3) + -1 + >>> ite_ternary_search(test_list, 13) + 4 + >>> ite_ternary_search([4, 5, 6, 7], 4) + 0 + >>> ite_ternary_search([4, 5, 6, 7], -10) + -1 + >>> ite_ternary_search([-18, 2], -18) + 0 + >>> ite_ternary_search([5], 5) + 0 + >>> ite_ternary_search(['a', 'c', 'd'], 'c') + 1 + >>> ite_ternary_search(['a', 'c', 'd'], 'f') + -1 + >>> ite_ternary_search([], 1) + -1 + >>> ite_ternary_search([.1, .4 , -.1], .1) + 0 + """ - if right - left < precision: - return lin_search(left, right, A, target) + left = 0 + right = len(array) + while left <= right: + if right - left < precision: + return lin_search(left, right, array, target) - oneThird = (left + right) / 3 + 1 - twoThird = 2 * (left + right) / 3 + 1 + one_third = (left + right) / 3 + 1 + two_third = 2 * (left + right) / 3 + 1 - if A[oneThird] == target: - return oneThird - elif A[twoThird] == target: - return twoThird + if array[one_third] == target: + return one_third + elif array[two_third] == target: + return two_third - elif target < A[oneThird]: - right = oneThird - 1 - elif A[twoThird] < target: - left = twoThird + 1 + elif target < array[one_third]: + right = one_third - 1 + elif array[two_third] < target: + left = two_third + 1 - else: - left = oneThird + 1 - right = twoThird - 1 else: - return None - -# This is the recursive method of the ternary search algorithm. -def rec_ternary_search(left: int, right: int, A: List[int], target: int): + left = one_third + 1 + right = two_third - 1 + else: + return -1 + + +def rec_ternary_search(left: int, right: int, array: List[int], target: int) -> int: + """Recursive method of the ternary search algorithm. + + >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] + >>> rec_ternary_search(0, len(test_list), test_list, 3) + -1 + >>> rec_ternary_search(4, len(test_list), test_list, 42) + 8 + >>> rec_ternary_search(0, 2, [4, 5, 6, 7], 4) + 0 + >>> rec_ternary_search(0, 3, [4, 5, 6, 7], -10) + -1 + >>> rec_ternary_search(0, 1, [-18, 2], -18) + 0 + >>> rec_ternary_search(0, 1, [5], 5) + 0 + >>> rec_ternary_search(0, 2, ['a', 'c', 'd'], 'c') + 1 + >>> rec_ternary_search(0, 2, ['a', 'c', 'd'], 'f') + -1 + >>> rec_ternary_search(0, 0, [], 1) + -1 + >>> rec_ternary_search(0, 3, [.1, .4 , -.1], .1) + 0 + """ if left < right: - if right - left < precision: - return lin_search(left, right, A, target) - - oneThird = (left + right) / 3 + 1 - twoThird = 2 * (left + right) / 3 + 1 - - if A[oneThird] == target: - return oneThird - elif A[twoThird] == target: - return twoThird - - elif target < A[oneThird]: - return rec_ternary_search(left, oneThird - 1, A, target) - elif A[twoThird] < target: - return rec_ternary_search(twoThird + 1, right, A, target) - + return lin_search(left, right, array, target) + one_third = (left + right) / 3 + 1 + two_third = 2 * (left + right) / 3 + 1 + + if array[one_third] == target: + return one_third + elif array[two_third] == target: + return two_third + + elif target < array[one_third]: + return rec_ternary_search(left, one_third - 1, array, target) + elif array[two_third] < target: + return rec_ternary_search(two_third + 1, right, array, target) else: - return rec_ternary_search(oneThird + 1, twoThird - 1, A, target) + return rec_ternary_search(one_third + 1, two_third - 1, array, target) else: - return None - - -# This function is to check if the array is sorted. -def __assert_sorted(collection: List[int]) -> bool: - if collection != sorted(collection): - raise ValueError("Collection must be sorted") - return True + return -1 if __name__ == "__main__": - user_input = input("Enter numbers separated by coma:\n").strip() - collection = [int(item) for item in user_input.split(",")] - - try: - __assert_sorted(collection) - except ValueError: - sys.exit("Sequence must be sorted to apply the ternary search") - - target_input = input("Enter a single number to be found in the list:\n") - target = int(target_input) + user_input = input("Enter numbers separated by comma:\n").strip() + collection = [int(item.strip()) for item in user_input.split(",")] + assert collection == sorted(collection), f"List must be ordered.\n{collection}." + target = int(input("Enter the number to be found in the list:\n").strip()) result1 = ite_ternary_search(collection, target) result2 = rec_ternary_search(0, len(collection) - 1, collection, target) - - if result2 is not None: + if result2 != -1: print(f"Iterative search: {target} found at positions: {result1}") print(f"Recursive search: {target} found at positions: {result2}") else: From 2c4978c6980abac0d1b7b37a76f49b3659328306 Mon Sep 17 00:00:00 2001 From: sukyung99 <44187128+sukyung99@users.noreply.github.com> Date: Sun, 15 Nov 2020 12:44:40 +0900 Subject: [PATCH 074/195] Add Maths / Sigmoid Function (#3880) * Add Maths / Sigmoid Function * Update Sigmoid Function * Add doctest and type hints * Fix Trim Trailing Whitespace * Fix Error * Modified Black * Update sigmoid.py Co-authored-by: sukyung99 Co-authored-by: Christian Clauss --- maths/sigmoid.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 maths/sigmoid.py diff --git a/maths/sigmoid.py b/maths/sigmoid.py new file mode 100644 index 000000000000..147588e8871f --- /dev/null +++ b/maths/sigmoid.py @@ -0,0 +1,39 @@ +""" +This script demonstrates the implementation of the Sigmoid function. + +The function takes a vector of K real numbers as input and then 1 / (1 + exp(-x)). +After through Sigmoid, the element of the vector mostly 0 between 1. or 1 between -1. + +Script inspired from its corresponding Wikipedia article +https://en.wikipedia.org/wiki/Sigmoid_function +""" + +import numpy as np + + +def sigmoid(vector: np.array) -> np.array: + """ + Implements the sigmoid function + + Parameters: + vector (np.array): A numpy array of shape (1,n) + consisting of real values + + Returns: + sigmoid_vec (np.array): The input numpy array, after applying + sigmoid. + + Examples: + >>> sigmoid(np.array([-1.0, 1.0, 2.0])) + array([0.26894142, 0.73105858, 0.88079708]) + + >>> sigmoid(np.array([0.0])) + array([0.5]) + """ + return 1 / (1 + np.exp(-vector)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From bfc0aeff628f6b4d646799354e05fed58e281924 Mon Sep 17 00:00:00 2001 From: Sethu Date: Wed, 18 Nov 2020 12:07:30 +0530 Subject: [PATCH 075/195] Modified comments on upper.py (#3884) * Modified comments on upper.py ,made it more clean * Update strings/upper.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> * Update upper.py * Update upper.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> Co-authored-by: Dhruv Manilawala --- strings/upper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/strings/upper.py b/strings/upper.py index 411802a2a22f..5edd40b79808 100644 --- a/strings/upper.py +++ b/strings/upper.py @@ -12,9 +12,9 @@ def upper(word: str) -> str: 'WH[]32' """ - # converting to ascii value int value and checking to see if char is a lower letter - # if it is a capital letter it is getting shift by 32 which makes it a capital case - # letter + # Converting to ascii value int value and checking to see if char is a lower letter + # if it is a lowercase letter it is getting shift by 32 which makes it an uppercase + # case letter return "".join(chr(ord(char) - 32) if "a" <= char <= "z" else char for char in word) From db3ddb445f67a9348bbbaae95677816c563ad69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=88=98=EC=97=B0?= Date: Wed, 18 Nov 2020 19:35:51 +0900 Subject: [PATCH 076/195] update area.py (#3862) add method "area_ellipse" - Calculate the area of a ellipse Co-authored-by: 201502029 --- maths/area.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/maths/area.py b/maths/area.py index 393d45faa880..24216e223ebf 100644 --- a/maths/area.py +++ b/maths/area.py @@ -186,6 +186,32 @@ def area_circle(radius: float) -> float: return pi * radius ** 2 +def area_ellipse(radius_x: float, radius_y: float) -> float: + """ + Calculate the area of a ellipse + + >>> area_ellipse(10, 10) + 314.1592653589793 + >>> area_ellipse(10, 20) + 628.3185307179587 + >>> area_ellipse(-10, 20) + Traceback (most recent call last): + ... + ValueError: area_ellipse() only accepts non-negative values + >>> area_ellipse(10, -20) + Traceback (most recent call last): + ... + ValueError: area_ellipse() only accepts non-negative values + >>> area_ellipse(-10, -20) + Traceback (most recent call last): + ... + ValueError: area_ellipse() only accepts non-negative values + """ + if radius_x < 0 or radius_y < 0: + raise ValueError("area_ellipse() only accepts non-negative values") + return pi * radius_x * radius_y + + def area_rhombus(diagonal_1: float, diagonal_2: float) -> float: """ Calculate the area of a rhombus From 730492f9622bcb3f857e9b91beca5a9aa78265f2 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 19 Nov 2020 22:01:31 +0530 Subject: [PATCH 077/195] Move CI tests from Travis to GitHub (#3889) * Add initial support for moving tests to GitHub * Add setup Python step in the workflow * Remove Travis CI config file * Fix GitHub action file for build to trigger on PR * Use Python 3.8 as tensorflow is not yet supported * Fix ciphers.hill_cipher doctest error * Fix: instagram crawler tests failing on GitHub actions * Fix floating point errors in doctest * Small change to test cache * Apply suggestions from code review Co-authored-by: Christian Clauss * Update instagram_crawler.py Co-authored-by: Christian Clauss --- .github/workflows/build.yml | 25 +++++++++++++++++++++++++ .travis.yml | 17 ----------------- ciphers/hill_cipher.py | 4 ++-- machine_learning/forecasting/run.py | 5 +++-- web_programming/instagram_crawler.py | 8 ++++---- 5 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000000..015670558432 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: "build" + +on: + pull_request + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: "3.8" + - uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools six + python -m pip install pytest-cov -r requirements.txt + - name: Run tests + run: pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . + - if: ${{ success() }} + run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c74669ebcb51..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -os: linux -dist: focal -language: python -python: 3.8 -cache: pip -before_install: pip install --upgrade pip setuptools six -jobs: - include: - - name: Build - install: pip install pytest-cov -r requirements.txt - script: - - pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . -after_success: - - scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md -notifications: - webhooks: https://www.travisbuddy.com/ - on_success: never diff --git a/ciphers/hill_cipher.py b/ciphers/hill_cipher.py index 3dabcd3fceab..8237abf6aa5d 100644 --- a/ciphers/hill_cipher.py +++ b/ciphers/hill_cipher.py @@ -155,8 +155,8 @@ def make_decrypt_key(self): """ >>> hill_cipher = HillCipher(numpy.array([[2, 5], [1, 6]])) >>> hill_cipher.make_decrypt_key() - array([[ 6., 25.], - [ 5., 26.]]) + array([[ 6, 25], + [ 5, 26]]) """ det = round(numpy.linalg.det(self.encrypt_key)) diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index 467371e8d2ff..0e11f958825f 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -25,8 +25,9 @@ def linear_regression_prediction( First method: linear regression input : training data (date, total_user, total_event) in list of float output : list of total user prediction in float - >>> linear_regression_prediction([2,3,4,5], [5,3,4,6], [3,1,2,4], [2,1], [2,2]) - 5.000000000000003 + >>> n = linear_regression_prediction([2,3,4,5], [5,3,4,6], [3,1,2,4], [2,1], [2,2]) + >>> abs(n - 5.0) < 1e-6 # Checking precision because of floating point errors + True """ x = [[1, item, train_mtch[i]] for i, item in enumerate(train_dt)] x = np.array(x) diff --git a/web_programming/instagram_crawler.py b/web_programming/instagram_crawler.py index c81635bd3593..4536257a984e 100644 --- a/web_programming/instagram_crawler.py +++ b/web_programming/instagram_crawler.py @@ -23,7 +23,7 @@ class InstagramUser: """ Class Instagram crawl instagram user information - Usage: (doctest failing on Travis CI) + Usage: (doctest failing on GitHub Actions) # >>> instagram_user = InstagramUser("github") # >>> instagram_user.is_verified True @@ -102,10 +102,10 @@ def test_instagram_user(username: str = "github") -> None: A self running doctest >>> test_instagram_user() """ - from os import getenv + import os - if getenv("CONTINUOUS_INTEGRATION"): - return # test failing on Travis CI + if os.environ.get("CI"): + return None # test failing on GitHub Actions instagram_user = InstagramUser(username) assert instagram_user.user_data assert isinstance(instagram_user.user_data, dict) From 54aa8011680493edc75f82663ed89a8c143d4209 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 19 Nov 2020 17:48:47 +0100 Subject: [PATCH 078/195] Delete Travis_CI_tests_are_failing.md (#3902) * Delete Travis_CI_tests_are_failing.md * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 10 ++++++++++ Travis_CI_tests_are_failing.md | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 Travis_CI_tests_are_failing.md diff --git a/DIRECTORY.md b/DIRECTORY.md index 89bedfb61592..cd8f6fb8578c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -209,6 +209,7 @@ * [Kth Order Statistic](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/kth_order_statistic.py) * [Max Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/max_subarray_sum.py) * [Mergesort](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/mergesort.py) + * [Peak](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/peak.py) * [Power](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/power.py) * [Strassen Matrix Multiplication](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/strassen_matrix_multiplication.py) @@ -363,6 +364,7 @@ * [Random Forest Regressor](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/random_forest_regressor.py) * [Scoring Functions](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/scoring_functions.py) * [Sequential Minimum Optimization](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/sequential_minimum_optimization.py) + * [Similarity Search](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/similarity_search.py) * [Support Vector Machines](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/support_vector_machines.py) * [Word Frequency Functions](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/word_frequency_functions.py) @@ -455,6 +457,7 @@ * [Harmonic Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/harmonic_series.py) * [P Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/p_series.py) * [Sieve Of Eratosthenes](https://github.com/TheAlgorithms/Python/blob/master/maths/sieve_of_eratosthenes.py) + * [Sigmoid](https://github.com/TheAlgorithms/Python/blob/master/maths/sigmoid.py) * [Simpson Rule](https://github.com/TheAlgorithms/Python/blob/master/maths/simpson_rule.py) * [Softmax](https://github.com/TheAlgorithms/Python/blob/master/maths/softmax.py) * [Square Root](https://github.com/TheAlgorithms/Python/blob/master/maths/square_root.py) @@ -495,6 +498,7 @@ * [Autocomplete Using Trie](https://github.com/TheAlgorithms/Python/blob/master/other/autocomplete_using_trie.py) * [Binary Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/other/binary_exponentiation.py) * [Binary Exponentiation 2](https://github.com/TheAlgorithms/Python/blob/master/other/binary_exponentiation_2.py) + * [Davis–Putnam–Logemann–Loveland](https://github.com/TheAlgorithms/Python/blob/master/other/davis–putnam–logemann–loveland.py) * [Detecting English Programmatically](https://github.com/TheAlgorithms/Python/blob/master/other/detecting_english_programmatically.py) * [Dijkstra Bankers Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/dijkstra_bankers_algorithm.py) * [Doomsday](https://github.com/TheAlgorithms/Python/blob/master/other/doomsday.py) @@ -662,6 +666,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_048/sol1.py) * Problem 049 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_049/sol1.py) + * Problem 050 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_050/sol1.py) * Problem 051 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_051/sol1.py) * Problem 052 @@ -723,6 +729,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_119/sol1.py) * Problem 120 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_120/sol1.py) + * Problem 123 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_123/sol1.py) * Problem 125 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_125/sol1.py) * Problem 173 @@ -749,6 +757,7 @@ * [Half Adder](https://github.com/TheAlgorithms/Python/blob/master/quantum/half_adder.py) * [Not Gate](https://github.com/TheAlgorithms/Python/blob/master/quantum/not_gate.py) * [Quantum Entanglement](https://github.com/TheAlgorithms/Python/blob/master/quantum/quantum_entanglement.py) + * [Ripple Adder Classic](https://github.com/TheAlgorithms/Python/blob/master/quantum/ripple_adder_classic.py) * [Single Qubit Measure](https://github.com/TheAlgorithms/Python/blob/master/quantum/single_qubit_measure.py) ## Scheduling @@ -848,6 +857,7 @@ * [Co2 Emission](https://github.com/TheAlgorithms/Python/blob/master/web_programming/co2_emission.py) * [Covid Stats Via Xpath](https://github.com/TheAlgorithms/Python/blob/master/web_programming/covid_stats_via_xpath.py) * [Crawl Google Results](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_results.py) + * [Crawl Google Scholar Citation](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_scholar_citation.py) * [Current Stock Price](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_stock_price.py) * [Current Weather](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_weather.py) * [Daily Horoscope](https://github.com/TheAlgorithms/Python/blob/master/web_programming/daily_horoscope.py) diff --git a/Travis_CI_tests_are_failing.md b/Travis_CI_tests_are_failing.md deleted file mode 100644 index 10bf5a6655d2..000000000000 --- a/Travis_CI_tests_are_failing.md +++ /dev/null @@ -1,9 +0,0 @@ -# Travis CI test are failing -### How do I find out what is wrong with my pull request? -1. In your PR look for the failing test and click the `Details` link: ![Travis_CI_fail_1.png](images/Travis_CI_fail_1.png) -2. On the next page, click `The build failed` link: ![Travis_CI_fail_2.png](images/Travis_CI_fail_2.png) -3. Now scroll down and look for `red` text describing the error(s) in the test log. - -Pull requests will __not__ be merged if the Travis CI tests are failing. - -If anything is unclear, please read through [CONTRIBUTING.md](CONTRIBUTING.md) and attempt to run the failing tests on your computer before asking for assistance. From 538ab3d03875b35b04cfb50c1bcd1ab88396ef17 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 19 Nov 2020 22:34:57 +0530 Subject: [PATCH 079/195] Update related to the change in CI testing (#3903) * Update README badge for GitHub CI * Run GitHub CI everyday as was done in Travis --- .github/workflows/build.yml | 4 +++- README.md | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 015670558432..01ac9aea7a7c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,9 @@ name: "build" on: - pull_request + pull_request: + schedule: + - cron: "0 0 * * *" # Run everyday jobs: build: diff --git a/README.md b/README.md index ac98b6371682..f81031b53ebb 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # The Algorithms - Python -[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/TheAlgorithms/Python) +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/TheAlgorithms/Python)  [![Gitter chat](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter&style=flat-square)](https://gitter.im/TheAlgorithms)  -[![Build Status](https://img.shields.io/travis/TheAlgorithms/Python.svg?label=Travis%20CI&logo=travis&style=flat-square)](https://travis-ci.com/TheAlgorithms/Python)  +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/TheAlgorithms/Python/build?label=CI&logo=github&style=flat-square)](https://github.com/TheAlgorithms/Python/actions)  [![LGTM](https://img.shields.io/lgtm/alerts/github/TheAlgorithms/Python.svg?label=LGTM&logo=LGTM&style=flat-square)](https://lgtm.com/projects/g/TheAlgorithms/Python/alerts)  [![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md)  [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?logo=paypal&style=flat-square)](https://www.paypal.me/TheAlgorithms/100)  ![](https://img.shields.io/github/repo-size/TheAlgorithms/Python.svg?label=Repo%20size&style=flat-square)  -[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)](https://github.com/pre-commit/pre-commit) -[![code style: black](https://img.shields.io/static/v1?label=code%20style&message=black&color=black&style=flat-square)](https://github.com/psf/black) - +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)](https://github.com/pre-commit/pre-commit)  +[![code style: black](https://img.shields.io/static/v1?label=code%20style&message=black&color=black&style=flat-square)](https://github.com/psf/black)  + ### All algorithms implemented in Python (for education) From 28b6402dec1959c590df22b2b4a9f34b02275f27 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Fri, 20 Nov 2020 00:39:31 +0530 Subject: [PATCH 080/195] Run latest stale action (#3904) --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 41ded1159891..4b12a71d7aff 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,7 +6,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 + - uses: actions/stale@v3.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 From 1489c66ce39ced9c591870372b3dbec5321909be Mon Sep 17 00:00:00 2001 From: tacitvenom Date: Fri, 20 Nov 2020 04:14:08 +0000 Subject: [PATCH 081/195] Fixes: #3869: Optimize CI runtime of Project Euler's Problem 74's Solution 2 (#3893) * Fixes: #3869: Optimize Project Euler's Problem 74's Solution 2 * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- project_euler/problem_074/sol2.py | 68 ++++++++++++++++++------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/project_euler/problem_074/sol2.py b/project_euler/problem_074/sol2.py index 0348ef1a6628..689593277a81 100644 --- a/project_euler/problem_074/sol2.py +++ b/project_euler/problem_074/sol2.py @@ -16,9 +16,13 @@ the chains of non repeating items. The generation of the chain stops before a repeating item or if the size of the chain is greater then the desired one. - After generating each chain, the length is checked and the counter increases. + After generating each chain, the length is checked and the + counter increases. """ +factorial_cache = {} +factorial_sum_cache = {} + def factorial(a: int) -> int: """Returns the factorial of the input a @@ -36,18 +40,23 @@ def factorial(a: int) -> int: if a < 0: raise ValueError("Invalid negative input!", a) + if a in factorial_cache: + return factorial_cache[a] + # The case of 0! is handled separately if a == 0: - return 1 + factorial_cache[a] = 1 else: # use a temporary support variable to store the computation + temporary_number = a temporary_computation = 1 - while a > 0: - temporary_computation *= a - a -= 1 + while temporary_number > 0: + temporary_computation *= temporary_number + temporary_number -= 1 - return temporary_computation + factorial_cache[a] = temporary_computation + return factorial_cache[a] def factorial_sum(a: int) -> int: @@ -57,7 +66,8 @@ def factorial_sum(a: int) -> int: >>> factorial_sum(69) 363600 """ - + if a in factorial_sum_cache: + return factorial_sum_cache[a] # Prepare a variable to hold the computation fact_sum = 0 @@ -67,17 +77,15 @@ def factorial_sum(a: int) -> int: """ for i in str(a): fact_sum += factorial(int(i)) - + factorial_sum_cache[a] = fact_sum return fact_sum def solution(chain_length: int = 60, number_limit: int = 1000000) -> int: """Returns the number of numbers that produce chains with exactly 60 non repeating elements. - >>> solution(60,1000000) - 402 - >>> solution(15,1000000) - 17800 + >>> solution(10, 1000) + 26 """ # the counter for the chains with the exact desired length @@ -86,25 +94,27 @@ def solution(chain_length: int = 60, number_limit: int = 1000000) -> int: for i in range(1, number_limit + 1): # The temporary list will contain the elements of the chain - chain_list = [i] + chain_set = {i} + len_chain_set = 1 + last_chain_element = i # The new element of the chain - new_chain_element = factorial_sum(chain_list[-1]) - - """ Stop computing the chain when you find a repeating item - or the length it greater then the desired one. - """ - while not (new_chain_element in chain_list) and ( - len(chain_list) <= chain_length - ): - chain_list += [new_chain_element] - - new_chain_element = factorial_sum(chain_list[-1]) - - """ If the while exited because the chain list contains the exact amount of elements - increase the counter - """ - chain_counter += len(chain_list) == chain_length + new_chain_element = factorial_sum(last_chain_element) + + # Stop computing the chain when you find a repeating item + # or the length it greater then the desired one. + + while new_chain_element not in chain_set and len_chain_set <= chain_length: + chain_set.add(new_chain_element) + + len_chain_set += 1 + last_chain_element = new_chain_element + new_chain_element = factorial_sum(last_chain_element) + + # If the while exited because the chain list contains the exact amount + # of elements increase the counter + if len_chain_set == chain_length: + chain_counter += 1 return chain_counter From 6df81d631390f50de3fcc47a95c1a17b2d82cad2 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Fri, 20 Nov 2020 11:41:22 +0530 Subject: [PATCH 082/195] Try the stale bot instead of stale action (#3906) * Try the stale bot instead * Add config file for the stale bot --- .github/stale.yml | 63 +++++++++++++++++++++++++++++++++++++ .github/workflows/stale.yml | 62 ++++++++++++++++++------------------ 2 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000000..ba6fd155d7a3 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,63 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 30 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "Status: on hold" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +pulls: + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + + # Comment to post when closing a stale Pull Request. + closeComment: > + Please reopen this pull request once you commit the changes requested + or make improvements on the code. If this is not the case and you need + some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) + or ping one of the reviewers. Thank you for your contributions! + +issues: + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + + # Comment to post when closing a stale Issue. + closeComment: > + Please reopen this issue once you add more information and updates here. + If this is not the case and you need some help, feel free to seek help + from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the + reviewers. Thank you for your contributions! diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 4b12a71d7aff..42353d233a29 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,31 +1,31 @@ -name: Mark/Close stale issues and pull requests -on: - schedule: - - cron: "0 * * * *" # Run every hour -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v3.0.13 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - days-before-stale: 30 - days-before-close: 7 - stale-issue-message: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. - close-issue-message: > - Please reopen this issue once you add more information and updates here. - If this is not the case and you need some help, feel free to seek help - from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the - reviewers. Thank you for your contributions! - stale-pr-message: > - This pull request has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. - close-pr-message: > - Please reopen this pull request once you commit the changes requested - or make improvements on the code. If this is not the case and you need - some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) - or ping one of the reviewers. Thank you for your contributions! +# name: Mark/Close stale issues and pull requests +# on: +# schedule: +# - cron: "0 * * * *" # Run every hour +# jobs: +# stale: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/stale@v3.0.13 +# with: +# repo-token: ${{ secrets.GITHUB_TOKEN }} +# days-before-stale: 30 +# days-before-close: 7 +# stale-issue-message: > +# This issue has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. +# close-issue-message: > +# Please reopen this issue once you add more information and updates here. +# If this is not the case and you need some help, feel free to seek help +# from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the +# reviewers. Thank you for your contributions! +# stale-pr-message: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. +# close-pr-message: > +# Please reopen this pull request once you commit the changes requested +# or make improvements on the code. If this is not the case and you need +# some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) +# or ping one of the reviewers. Thank you for your contributions! From 53f3a417ea6dd338e8f561061f9395ba25c2bb9f Mon Sep 17 00:00:00 2001 From: Michael D Date: Sat, 21 Nov 2020 03:07:47 +0100 Subject: [PATCH 083/195] Add solution for Project Euler problem 188 (#2880) * Project Euler problem 188 solution * fix superscript notation * split out modexpt() function, and rename parameters * Add some more doctest, and add type hints * Add some reference links * Update docstrings and mark helper function private * Fix doctests and remove/improve redundant comments * fix as per style guide --- project_euler/problem_188/__init__.py | 0 project_euler/problem_188/sol1.py | 68 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 project_euler/problem_188/__init__.py create mode 100644 project_euler/problem_188/sol1.py diff --git a/project_euler/problem_188/__init__.py b/project_euler/problem_188/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py new file mode 100644 index 000000000000..6473c63620ed --- /dev/null +++ b/project_euler/problem_188/sol1.py @@ -0,0 +1,68 @@ +""" +Project Euler Problem 188: https://projecteuler.net/problem=188 + +The hyperexponentiation of a number + +The hyperexponentiation or tetration of a number a by a positive integer b, +denoted by a↑↑b or b^a, is recursively defined by: + +a↑↑1 = a, +a↑↑(k+1) = a(a↑↑k). + +Thus we have e.g. 3↑↑2 = 3^3 = 27, hence 3↑↑3 = 3^27 = 7625597484987 and +3↑↑4 is roughly 103.6383346400240996*10^12. + +Find the last 8 digits of 1777↑↑1855. + +References: + - https://en.wikipedia.org/wiki/Tetration +""" + + +# small helper function for modular exponentiation +def _modexpt(base: int, exponent: int, modulo_value: int) -> int: + """ + Returns the modular exponentiation, that is the value + of `base ** exponent % modulo_value`, without calculating + the actual number. + >>> _modexpt(2, 4, 10) + 6 + >>> _modexpt(2, 1024, 100) + 16 + >>> _modexpt(13, 65535, 7) + 6 + """ + + if exponent == 1: + return base + if exponent % 2 == 0: + x = _modexpt(base, exponent / 2, modulo_value) % modulo_value + return (x * x) % modulo_value + else: + return (base * _modexpt(base, exponent - 1, modulo_value)) % modulo_value + + +def solution(base: int = 1777, height: int = 1855, digits: int = 8) -> int: + """ + Returns the last 8 digits of the hyperexponentiation of base by + height, i.e. the number base↑↑height: + + >>> solution(base=3, height=2) + 27 + >>> solution(base=3, height=3) + 97484987 + >>> solution(base=123, height=456, digits=4) + 2547 + """ + + # calculate base↑↑height by right-assiciative repeated modular + # exponentiation + result = base + for i in range(1, height): + result = _modexpt(base, result, 10 ** digits) + + return result + + +if __name__ == "__main__": + print(f"{solution() = }") From c56a18a79f89aee90707045e8c68dee124ecf39e Mon Sep 17 00:00:00 2001 From: Peter Yao Date: Fri, 20 Nov 2020 18:42:07 -0800 Subject: [PATCH 084/195] Project Euler 70 Solution (#3041) * Add solution for Project Euler 70, Fixes: #2695 * Remove parameter from solution() * Add tests for all functions, add fstring and positional arg for solution() * Rename directory to 070 * Move up explanation to module code block * Move solution() below helper functions, rename variables * Remove whitespace from defining min_numerator * Add whitespace * Improve type hints with typing.List Co-authored-by: Dhruv Manilawala --- DIRECTORY.md | 2 + project_euler/problem_070/__init__.py | 0 project_euler/problem_070/sol1.py | 119 ++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 project_euler/problem_070/__init__.py create mode 100644 project_euler/problem_070/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index cd8f6fb8578c..71da6a402b31 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -697,6 +697,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_067/sol1.py) * Problem 069 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_069/sol1.py) + * Problem 070 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_070/sol1.py) * Problem 071 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_071/sol1.py) * Problem 072 diff --git a/project_euler/problem_070/__init__.py b/project_euler/problem_070/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py new file mode 100644 index 000000000000..9d27119ba95c --- /dev/null +++ b/project_euler/problem_070/sol1.py @@ -0,0 +1,119 @@ +""" +Project Euler Problem 70: https://projecteuler.net/problem=70 + +Euler's Totient function, φ(n) [sometimes called the phi function], is used to +determine the number of positive numbers less than or equal to n which are +relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than +nine and relatively prime to nine, φ(9)=6. + +The number 1 is considered to be relatively prime to every positive number, so +φ(1)=1. + +Interestingly, φ(87109)=79180, and it can be seen that 87109 is a permutation +of 79180. + +Find the value of n, 1 < n < 10^7, for which φ(n) is a permutation of n and +the ratio n/φ(n) produces a minimum. + +----- + +This is essentially brute force. Calculate all totients up to 10^7 and +find the minimum ratio of n/φ(n) that way. To minimize the ratio, we want +to minimize n and maximize φ(n) as much as possible, so we can store the +minimum fraction's numerator and denominator and calculate new fractions +with each totient to compare against. To avoid dividing by zero, I opt to +use cross multiplication. + +References: +Finding totients +https://en.wikipedia.org/wiki/Euler's_totient_function#Euler's_product_formula +""" +from typing import List + + +def get_totients(max_one: int) -> List[int]: + """ + Calculates a list of totients from 0 to max_one exclusive, using the + definition of Euler's product formula. + + >>> get_totients(5) + [0, 1, 1, 2, 2] + + >>> get_totients(10) + [0, 1, 1, 2, 2, 4, 2, 6, 4, 6] + """ + totients = [0] * max_one + + for i in range(0, max_one): + totients[i] = i + + for i in range(2, max_one): + if totients[i] == i: + for j in range(i, max_one, i): + totients[j] -= totients[j] // i + + return totients + + +def has_same_digits(num1: int, num2: int) -> bool: + """ + Return True if num1 and num2 have the same frequency of every digit, False + otherwise. + + digits[] is a frequency table where the index represents the digit from + 0-9, and the element stores the number of appearances. Increment the + respective index every time you see the digit in num1, and decrement if in + num2. At the end, if the numbers have the same digits, every index must + contain 0. + + >>> has_same_digits(123456789, 987654321) + True + + >>> has_same_digits(123, 12) + False + + >>> has_same_digits(1234566, 123456) + False + """ + digits = [0] * 10 + + while num1 > 0 and num2 > 0: + digits[num1 % 10] += 1 + digits[num2 % 10] -= 1 + num1 //= 10 + num2 //= 10 + + for digit in digits: + if digit != 0: + return False + + return True + + +def solution(max: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + + >>> solution(100) + 21 + + >>> solution(10000) + 4435 + """ + + min_numerator = 1 # i + min_denominator = 0 # φ(i) + totients = get_totients(max + 1) + + for i in range(2, max + 1): + t = totients[i] + + if i * min_denominator < min_numerator * t and has_same_digits(i, t): + min_numerator = i + min_denominator = t + + return min_numerator + + +if __name__ == "__main__": + print(f"{solution() = }") From f7e3953c55fae4db474d55abdc894a0675e04561 Mon Sep 17 00:00:00 2001 From: fpringle Date: Sat, 21 Nov 2020 03:52:26 +0100 Subject: [PATCH 085/195] Added solution for Project Euler problem 129. (#3113) * Added solution for Project Euler problem 129. * Added doctest for solution() in project_euler/problem_129/sol1.py * Update formatting. Reference: #3256 * More descriptive function and variable names, more doctests. --- project_euler/problem_129/__init__.py | 0 project_euler/problem_129/sol1.py | 57 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 project_euler/problem_129/__init__.py create mode 100644 project_euler/problem_129/sol1.py diff --git a/project_euler/problem_129/__init__.py b/project_euler/problem_129/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_129/sol1.py b/project_euler/problem_129/sol1.py new file mode 100644 index 000000000000..8afe82df162e --- /dev/null +++ b/project_euler/problem_129/sol1.py @@ -0,0 +1,57 @@ +""" +Project Euler Problem 129: https://projecteuler.net/problem=129 + +A number consisting entirely of ones is called a repunit. We shall define R(k) to be +a repunit of length k; for example, R(6) = 111111. + +Given that n is a positive integer and GCD(n, 10) = 1, it can be shown that there +always exists a value, k, for which R(k) is divisible by n, and let A(n) be the least +such value of k; for example, A(7) = 6 and A(41) = 5. + +The least value of n for which A(n) first exceeds ten is 17. + +Find the least value of n for which A(n) first exceeds one-million. +""" + + +def least_divisible_repunit(divisor: int) -> int: + """ + Return the least value k such that the Repunit of length k is divisible by divisor. + >>> least_divisible_repunit(7) + 6 + >>> least_divisible_repunit(41) + 5 + >>> least_divisible_repunit(1234567) + 34020 + """ + if divisor % 5 == 0 or divisor % 2 == 0: + return 0 + repunit = 1 + repunit_index = 1 + while repunit: + repunit = (10 * repunit + 1) % divisor + repunit_index += 1 + return repunit_index + + +def solution(limit: int = 1000000) -> int: + """ + Return the least value of n for which least_divisible_repunit(n) + first exceeds limit. + >>> solution(10) + 17 + >>> solution(100) + 109 + >>> solution(1000) + 1017 + """ + divisor = limit - 1 + if divisor % 2 == 0: + divisor += 1 + while least_divisible_repunit(divisor) <= limit: + divisor += 2 + return divisor + + +if __name__ == "__main__": + print(f"{solution() = }") From 4ab43a16d092eb018f370a1a102b9da5bd06d7e7 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 21 Nov 2020 09:04:49 +0530 Subject: [PATCH 086/195] Remove stale action workflow file (#3915) --- .github/workflows/stale.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 42353d233a29..000000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,31 +0,0 @@ -# name: Mark/Close stale issues and pull requests -# on: -# schedule: -# - cron: "0 * * * *" # Run every hour -# jobs: -# stale: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/stale@v3.0.13 -# with: -# repo-token: ${{ secrets.GITHUB_TOKEN }} -# days-before-stale: 30 -# days-before-close: 7 -# stale-issue-message: > -# This issue has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. -# close-issue-message: > -# Please reopen this issue once you add more information and updates here. -# If this is not the case and you need some help, feel free to seek help -# from our [Gitter](https://gitter.im/TheAlgorithms) or ping one of the -# reviewers. Thank you for your contributions! -# stale-pr-message: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. -# close-pr-message: > -# Please reopen this pull request once you commit the changes requested -# or make improvements on the code. If this is not the case and you need -# some help, feel free to seek help from our [Gitter](https://gitter.im/TheAlgorithms) -# or ping one of the reviewers. Thank you for your contributions! From a872c1ab7f351e16abf18408945cb78556db7b88 Mon Sep 17 00:00:00 2001 From: Cory Metcalfe Date: Fri, 20 Nov 2020 23:29:29 -0600 Subject: [PATCH 087/195] Add solution for Project Euler: Problem 89 (#2948) * add solution for euler problem 89 * updates to accommodate euler solution guideline updates * use more descriptive vars * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 6 + project_euler/problem_089/__init__.py | 1 + .../problem_089/numeralcleanup_test.txt | 5 + project_euler/problem_089/p089_roman.txt | 1000 +++++++++++++++++ project_euler/problem_089/sol1.py | 141 +++ 5 files changed, 1153 insertions(+) create mode 100644 project_euler/problem_089/__init__.py create mode 100644 project_euler/problem_089/numeralcleanup_test.txt create mode 100644 project_euler/problem_089/p089_roman.txt create mode 100644 project_euler/problem_089/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 71da6a402b31..2b3f3073c3d4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -717,6 +717,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) * Problem 087 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_087/sol1.py) + * Problem 089 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_089/sol1.py) * Problem 091 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_091/sol1.py) * Problem 097 @@ -735,10 +737,14 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_123/sol1.py) * Problem 125 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_125/sol1.py) + * Problem 129 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_129/sol1.py) * Problem 173 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_173/sol1.py) * Problem 174 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) + * Problem 188 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_188/sol1.py) * Problem 191 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_191/sol1.py) * Problem 203 diff --git a/project_euler/problem_089/__init__.py b/project_euler/problem_089/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_089/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_089/numeralcleanup_test.txt b/project_euler/problem_089/numeralcleanup_test.txt new file mode 100644 index 000000000000..06142142cca9 --- /dev/null +++ b/project_euler/problem_089/numeralcleanup_test.txt @@ -0,0 +1,5 @@ +IIII +IV +IIIIIIIIII +X +VIIIII diff --git a/project_euler/problem_089/p089_roman.txt b/project_euler/problem_089/p089_roman.txt new file mode 100644 index 000000000000..50651c355a5b --- /dev/null +++ b/project_euler/problem_089/p089_roman.txt @@ -0,0 +1,1000 @@ +MMMMDCLXXII +MMDCCCLXXXIII +MMMDLXVIIII +MMMMDXCV +DCCCLXXII +MMCCCVI +MMMCDLXXXVII +MMMMCCXXI +MMMCCXX +MMMMDCCCLXXIII +MMMCCXXXVII +MMCCCLXXXXIX +MDCCCXXIIII +MMCXCVI +CCXCVIII +MMMCCCXXXII +MDCCXXX +MMMDCCCL +MMMMCCLXXXVI +MMDCCCXCVI +MMMDCII +MMMCCXII +MMMMDCCCCI +MMDCCCXCII +MDCXX +CMLXXXVII +MMMXXI +MMMMCCCXIV +MLXXII +MCCLXXVIIII +MMMMCCXXXXI +MMDCCCLXXII +MMMMXXXI +MMMDCCLXXX +MMDCCCLXXIX +MMMMLXXXV +MCXXI +MDCCCXXXVII +MMCCCLXVII +MCDXXXV +CCXXXIII +CMXX +MMMCLXIV +MCCCLXXXVI +DCCCXCVIII +MMMDCCCCXXXIV +CDXVIIII +MMCCXXXV +MDCCCXXXII +MMMMD +MMDCCLXIX +MMMMCCCLXXXXVI +MMDCCXLII +MMMDCCCVIIII +DCCLXXXIIII +MDCCCCXXXII +MMCXXVII +DCCCXXX +CCLXIX +MMMXI +MMMMCMLXXXXVIII +MMMMDLXXXVII +MMMMDCCCLX +MMCCLIV +CMIX +MMDCCCLXXXIIII +CLXXXII +MMCCCCXXXXV +MMMMDLXXXVIIII +MMMDCCCXXI +MMDCCCCLXXVI +MCCCCLXX +MMCDLVIIII +MMMDCCCLIX +MMMMCCCCXIX +MMMDCCCLXXV +XXXI +CDLXXXIII +MMMCXV +MMDCCLXIII +MMDXXX +MMMMCCCLVII +MMMDCI +MMMMCDLXXXIIII +MMMMCCCXVI +CCCLXXXVIII +MMMMCML +MMMMXXIV +MMMCCCCXXX +DCCX +MMMCCLX +MMDXXXIII +CCCLXIII +MMDCCXIII +MMMCCCXLIV +CLXXXXI +CXVI +MMMMCXXXIII +CLXX +DCCCXVIII +MLXVII +DLXXXX +MMDXXI +MMMMDLXXXXVIII +MXXII +LXI +DCCCCXLIII +MMMMDV +MMMMXXXIV +MDCCCLVIII +MMMCCLXXII +MMMMDCCXXXVI +MMMMLXXXIX +MDCCCLXXXI +MMMMDCCCXV +MMMMCCCCXI +MMMMCCCLIII +MDCCCLXXI +MMCCCCXI +MLXV +MMCDLXII +MMMMDXXXXII +MMMMDCCCXL +MMMMCMLVI +CCLXXXIV +MMMDCCLXXXVI +MMCLII +MMMCCCCXV +MMLXXXIII +MMMV +MMMV +DCCLXII +MMDCCCCXVI +MMDCXLVIII +CCLIIII +CCCXXV +MMDCCLXXXVIIII +MMMMDCLXXVIII +MMMMDCCCXCI +MMMMCCCXX +MMCCXLV +MMMDCCCLXIX +MMCCLXIIII +MMMDCCCXLIX +MMMMCCCLXIX +CMLXXXXI +MCMLXXXIX +MMCDLXI +MMDCLXXVIII +MMMMDCCLXI +MCDXXV +DL +CCCLXXII +MXVIIII +MCCCCLXVIII +CIII +MMMDCCLXXIIII +MMMDVIII +MMMMCCCLXXXXVII +MMDXXVII +MMDCCLXXXXV +MMMMCXLVI +MMMDCCLXXXII +MMMDXXXVI +MCXXII +CLI +DCLXXXIX +MMMCLI +MDCLXIII +MMMMDCCXCVII +MMCCCLXXXV +MMMDCXXVIII +MMMCDLX +MMMCMLII +MMMIV +MMMMDCCCLVIII +MMMDLXXXVIII +MCXXIV +MMMMLXXVI +CLXXIX +MMMCCCCXXVIIII +DCCLXXXV +MMMDCCCVI +LI +CLXXXVI +MMMMCCCLXXVI +MCCCLXVI +CCXXXIX +MMDXXXXI +MMDCCCXLI +DCCCLXXXVIII +MMMMDCCCIV +MDCCCCXV +MMCMVI +MMMMCMLXXXXV +MMDCCLVI +MMMMCCXLVIII +DCCCCIIII +MMCCCCIII +MMMDCCLXXXVIIII +MDCCCLXXXXV +DVII +MMMV +DCXXV +MMDCCCXCV +DCVIII +MMCDLXVI +MCXXVIII +MDCCXCVIII +MMDCLX +MMMDCCLXIV +MMCDLXXVII +MMDLXXXIIII +MMMMCCCXXII +MMMDCCCXLIIII +DCCCCLXVII +MMMCLXXXXIII +MCCXV +MMMMDCXI +MMMMDCLXXXXV +MMMCCCLII +MMCMIX +MMDCCXXV +MMDLXXXVI +MMMMDCXXVIIII +DCCCCXXXVIIII +MMCCXXXIIII +MMDCCLXXVIII +MDCCLXVIIII +MMCCLXXXV +MMMMDCCCLXXXVIII +MMCMXCI +MDXLII +MMMMDCCXIV +MMMMLI +DXXXXIII +MMDCCXI +MMMMCCLXXXIII +MMMDCCCLXXIII +MDCLVII +MMCD +MCCCXXVII +MMMMDCCIIII +MMMDCCXLVI +MMMCLXXXVII +MMMCCVIIII +MCCCCLXXIX +DL +DCCCLXXVI +MMDXCI +MMMMDCCCCXXXVI +MMCII +MMMDCCCXXXXV +MMMCDXLV +MMDCXXXXIV +MMD +MDCCCLXXXX +MMDCXLIII +MMCCXXXII +MMDCXXXXVIIII +DCCCLXXI +MDXCVIIII +MMMMCCLXXVIII +MDCLVIIII +MMMCCCLXXXIX +MDCLXXXV +MDLVIII +MMMMCCVII +MMMMDCXIV +MMMCCCLXIIII +MMIIII +MMMMCCCLXXIII +CCIII +MMMCCLV +MMMDXIII +MMMCCCXC +MMMDCCCXXI +MMMMCCCCXXXII +CCCLVI +MMMCCCLXXXVI +MXVIIII +MMMCCCCXIIII +CLXVII +MMMCCLXX +CCCCLXIV +MMXXXXII +MMMMCCLXXXX +MXL +CCXVI +CCCCLVIIII +MMCCCII +MCCCLVIII +MMMMCCCX +MCDLXXXXIV +MDCCCXIII +MMDCCCXL +MMMMCCCXXIII +DXXXIV +CVI +MMMMDCLXXX +DCCCVII +MMCMLXIIII +MMMDCCCXXXIII +DCCC +MDIII +MMCCCLXVI +MMMCCCCLXXI +MMDCCCCXVIII +CCXXXVII +CCCXXV +MDCCCXII +MMMCMV +MMMMCMXV +MMMMDCXCI +DXXI +MMCCXLVIIII +MMMMCMLII +MDLXXX +MMDCLXVI +CXXI +MMMDCCCLIIII +MMMCXXI +MCCIII +MMDCXXXXI +CCXCII +MMMMDXXXV +MMMCCCLXV +MMMMDLXV +MMMCCCCXXXII +MMMCCCVIII +DCCCCLXXXXII +MMCLXIV +MMMMCXI +MLXXXXVII +MMMCDXXXVIII +MDXXII +MLV +MMMMDLXVI +MMMCXII +XXXIII +MMMMDCCCXXVI +MMMLXVIIII +MMMLX +MMMCDLXVII +MDCCCLVII +MMCXXXVII +MDCCCCXXX +MMDCCCLXIII +MMMMDCXLIX +MMMMCMXLVIII +DCCCLXXVIIII +MDCCCLIII +MMMCMLXI +MMMMCCLXI +MMDCCCLIII +MMMDCCCVI +MMDXXXXIX +MMCLXXXXV +MMDXXX +MMMXIII +DCLXXIX +DCCLXII +MMMMDCCLXVIII +MDCCXXXXIII +CCXXXII +MMMMDCXXV +MMMCCCXXVIII +MDCVIII +MMMCLXXXXIIII +CLXXXI +MDCCCCXXXIII +MMMMDCXXX +MMMDCXXIV +MMMCCXXXVII +MCCCXXXXIIII +CXVIII +MMDCCCCIV +MMMMCDLXXV +MMMDLXIV +MDXCIII +MCCLXXXI +MMMDCCCXXIV +MCXLIII +MMMDCCCI +MCCLXXX +CCXV +MMDCCLXXI +MMDLXXXIII +MMMMDCXVII +MMMCMLXV +MCLXVIII +MMMMCCLXXVI +MMMDCCLXVIIII +MMMMDCCCIX +DLXXXXIX +DCCCXXII +MMMMIII +MMMMCCCLXXVI +DCCCXCIII +DXXXI +MXXXIIII +CCXII +MMMDCCLXXXIIII +MMMCXX +MMMCMXXVII +DCCCXXXX +MMCDXXXVIIII +MMMMDCCXVIII +LV +MMMDCCCCVI +MCCCII +MMCMLXVIIII +MDCCXI +MMMMDLXVII +MMCCCCLXI +MMDCCV +MMMCCCXXXIIII +MMMMDI +MMMDCCCXCV +MMDCCLXXXXI +MMMDXXVI +MMMDCCCLVI +MMDCXXX +MCCCVII +MMMMCCCLXII +MMMMXXV +MMCMXXV +MMLVI +MMDXXX +MMMMCVII +MDC +MCCIII +MMMMDCC +MMCCLXXV +MMDCCCXXXXVI +MMMMCCCLXV +CDXIIII +MLXIIII +CCV +MMMCMXXXI +CCCCLXVI +MDXXXII +MMMMCCCLVIII +MMV +MMMCLII +MCMLI +MMDCCXX +MMMMCCCCXXXVI +MCCLXXXI +MMMCMVI +DCCXXX +MMMMCCCLXV +DCCCXI +MMMMDCCCXIV +CCCXXI +MMDLXXV +CCCCLXXXX +MCCCLXXXXII +MMDCIX +DCCXLIIII +DXIV +MMMMCLII +CDLXI +MMMCXXVII +MMMMDCCCCLXIII +MMMDCLIIII +MCCCCXXXXII +MMCCCLX +CCCCLIII +MDCCLXXVI +MCMXXIII +MMMMDLXXVIII +MMDCCCCLX +MMMCCCLXXXX +MMMCDXXVI +MMMDLVIII +CCCLXI +MMMMDCXXII +MMDCCCXXI +MMDCCXIII +MMMMCLXXXVI +MDCCCCXXVI +MDV +MMDCCCCLXXVI +MMMMCCXXXVII +MMMDCCLXXVIIII +MMMCCCCLXVII +DCCXLI +MMCLXXXVIII +MCCXXXVI +MMDCXLVIII +MMMMCXXXII +MMMMDCCLXVI +MMMMCMLI +MMMMCLXV +MMMMDCCCXCIV +MCCLXXVII +LXXVIIII +DCCLII +MMMCCCXCVI +MMMCLV +MMDCCCXXXXVIII +DCCCXV +MXC +MMDCCLXXXXVII +MMMMCML +MMDCCCLXXVIII +DXXI +MCCCXLI +DCLXXXXI +MMCCCLXXXXVIII +MDCCCCLXXVIII +MMMMDXXV +MMMDCXXXVI +MMMCMXCVII +MMXVIIII +MMMDCCLXXIV +MMMCXXV +DXXXVIII +MMMMCLXVI +MDXII +MMCCCLXX +CCLXXI +DXIV +MMMCLIII +DLII +MMMCCCXLIX +MMCCCCXXVI +MMDCXLIII +MXXXXII +CCCLXXXV +MDCLXXVI +MDCXII +MMMCCCLXXXIII +MMDCCCCLXXXII +MMMMCCCLXXXV +MMDCXXI +DCCCXXX +MMMDCCCCLII +MMMDCCXXII +MMMMCDXCVIII +MMMCCLXVIIII +MMXXV +MMMMCDXIX +MMMMCCCX +MMMCCCCLXVI +MMMMDCLXXVIIII +MMMMDCXXXXIV +MMMCMXII +MMMMXXXIII +MMMMDLXXXII +DCCCLIV +MDXVIIII +MMMCLXXXXV +CCCCXX +MMDIX +MMCMLXXXVIII +DCCXLIII +DCCLX +D +MCCCVII +MMMMCCCLXXXIII +MDCCCLXXIIII +MMMDCCCCLXXXVII +MMMMCCCVII +MMMDCCLXXXXVI +CDXXXIV +MCCLXVIII +MMMMDLX +MMMMDXII +MMMMCCCCLIIII +MCMLXXXXIII +MMMMDCCCIII +MMDCLXXXIII +MDCCCXXXXIV +XXXXVII +MMMDCCCXXXII +MMMDCCCXLII +MCXXXV +MDCXXVIIII +MMMCXXXXIIII +MMMMCDXVII +MMMDXXIII +MMMMCCCCLXI +DCLXXXXVIIII +LXXXXI +CXXXIII +MCDX +MCCLVII +MDCXXXXII +MMMCXXIV +MMMMLXXXX +MMDCCCCXLV +MLXXX +MMDCCCCLX +MCDLIII +MMMCCCLXVII +MMMMCCCLXXIV +MMMDCVIII +DCCCCXXIII +MMXCI +MMDCCIV +MMMMDCCCXXXIV +CCCLXXI +MCCLXXXII +MCMIII +CCXXXI +DCCXXXVIII +MMMMDCCXLVIIII +MMMMCMXXXV +DCCCLXXV +DCCXCI +MMMMDVII +MMMMDCCCLXVIIII +CCCXCV +MMMMDCCXX +MCCCCII +MMMCCCXC +MMMCCCII +MMDCCLXXVII +MMDCLIIII +CCXLIII +MMMDCXVIII +MMMCCCIX +MCXV +MMCCXXV +MLXXIIII +MDCCXXVI +MMMCCCXX +MMDLXX +MMCCCCVI +MMDCCXX +MMMMDCCCCXCV +MDCCCXXXII +MMMMDCCCCXXXX +XCIV +MMCCCCLX +MMXVII +MLXXI +MMMDXXVIII +MDCCCCII +MMMCMLVII +MMCLXXXXVIII +MDCCCCLV +MCCCCLXXIIII +MCCCLII +MCDXLVI +MMMMDXVIII +DCCLXXXIX +MMMDCCLXIV +MDCCCCXLIII +CLXXXXV +MMMMCCXXXVI +MMMDCCCXXI +MMMMCDLXXVII +MCDLIII +MMCCXLVI +DCCCLV +MCDLXX +DCLXXVIII +MMDCXXXIX +MMMMDCLX +MMDCCLI +MMCXXXV +MMMCCXII +MMMMCMLXII +MMMMCCV +MCCCCLXIX +MMMMCCIII +CLXVII +MCCCLXXXXIIII +MMMMDCVIII +MMDCCCLXI +MMLXXIX +CMLXIX +MMDCCCXLVIIII +DCLXII +MMMCCCXLVII +MDCCCXXXV +MMMMDCCXCVI +DCXXX +XXVI +MMLXIX +MMCXI +DCXXXVII +MMMMCCCXXXXVIII +MMMMDCLXI +MMMMDCLXXIIII +MMMMVIII +MMMMDCCCLXII +MDCXCI +MMCCCXXIIII +CCCCXXXXV +MMDCCCXXI +MCVI +MMDCCLXVIII +MMMMCXL +MLXVIII +CMXXVII +CCCLV +MDCCLXXXIX +MMMCCCCLXV +MMDCCLXII +MDLXVI +MMMCCCXVIII +MMMMCCLXXXI +MMCXXVII +MMDCCCLXVIII +MMMCXCII +MMMMDCLVIII +MMMMDCCCXXXXII +MMDCCCCLXXXXVI +MDCCXL +MDCCLVII +MMMMDCCCLXXXVI +DCCXXXIII +MMMMDCCCCLXXXV +MMCCXXXXVIII +MMMCCLXXVIII +MMMDCLXXVIII +DCCCI +MMMMLXXXXVIIII +MMMCCCCLXXII +MMCLXXXVII +CCLXVI +MCDXLIII +MMCXXVIII +MDXIV +CCCXCVIII +CLXXVIII +MMCXXXXVIIII +MMMDCLXXXIV +CMLVIII +MCDLIX +MMMMDCCCXXXII +MMMMDCXXXIIII +MDCXXI +MMMDCXLV +MCLXXVIII +MCDXXII +IV +MCDLXXXXIII +MMMMDCCLXV +CCLI +MMMMDCCCXXXVIII +DCLXII +MCCCLXVII +MMMMDCCCXXXVI +MMDCCXLI +MLXI +MMMCDLXVIII +MCCCCXCIII +XXXIII +MMMDCLXIII +MMMMDCL +DCCCXXXXIIII +MMDLVII +DXXXVII +MCCCCXXIIII +MCVII +MMMMDCCXL +MMMMCXXXXIIII +MCCCCXXIV +MMCLXVIII +MMXCIII +MDCCLXXX +MCCCLIIII +MMDCLXXI +MXI +MCMLIV +MMMCCIIII +DCCLXXXVIIII +MDCLIV +MMMDCXIX +CMLXXXI +DCCLXXXVII +XXV +MMMXXXVI +MDVIIII +CLXIII +MMMCDLVIIII +MMCCCCVII +MMMLXX +MXXXXII +MMMMCCCLXVIII +MMDCCCXXVIII +MMMMDCXXXXI +MMMMDCCCXXXXV +MMMXV +MMMMCCXVIIII +MMDCCXIIII +MMMXXVII +MDCCLVIIII +MMCXXIIII +MCCCLXXIV +DCLVIII +MMMLVII +MMMCXLV +MMXCVII +MMMCCCLXXXVII +MMMMCCXXII +DXII +MMMDLV +MCCCLXXVIII +MMMCLIIII +MMMMCLXXXX +MMMCLXXXIIII +MDCXXIII +MMMMCCXVI +MMMMDLXXXIII +MMMDXXXXIII +MMMMCCCCLV +MMMDLXXXI +MMMCCLXXVI +MMMMXX +MMMMDLVI +MCCCCLXXX +MMMXXII +MMXXII +MMDCCCCXXXI +MMMDXXV +MMMDCLXXXVIIII +MMMDLXXXXVII +MDLXIIII +CMXC +MMMXXXVIII +MDLXXXVIII +MCCCLXXVI +MMCDLIX +MMDCCCXVIII +MDCCCXXXXVI +MMMMCMIV +MMMMDCIIII +MMCCXXXV +XXXXVI +MMMMCCXVII +MMCCXXIV +MCMLVIIII +MLXXXIX +MMMMLXXXIX +CLXXXXIX +MMMDCCCCLVIII +MMMMCCLXXIII +MCCCC +DCCCLIX +MMMCCCLXXXII +MMMCCLXVIIII +MCLXXXV +CDLXXXVII +DCVI +MMX +MMCCXIII +MMMMDCXX +MMMMXXVIII +DCCCLXII +MMMMCCCXLIII +MMMMCLXV +DXCI +MMMMCLXXX +MMMDCCXXXXI +MMMMXXXXVI +DCLX +MMMCCCXI +MCCLXXX +MMCDLXXII +DCCLXXI +MMMCCCXXXVI +MCCCCLXXXVIIII +CDLVIII +DCCLVI +MMMMDCXXXVIII +MMCCCLXXXIII +MMMMDCCLXXV +MMMXXXVI +CCCLXXXXIX +CV +CCCCXIII +CCCCXVI +MDCCCLXXXIIII +MMDCCLXXXII +MMMMCCCCLXXXI +MXXV +MMCCCLXXVIIII +MMMCCXII +MMMMCCXXXIII +MMCCCLXXXVI +MMMDCCCLVIIII +MCCXXXVII +MDCLXXV +XXXV +MMDLI +MMMCCXXX +MMMMCXXXXV +CCCCLIX +MMMMDCCCLXXIII +MMCCCXVII +DCCCXVI +MMMCCCXXXXV +MDCCCCXCV +CLXXXI +MMMMDCCLXX +MMMDCCCIII +MMCLXXVII +MMMDCCXXIX +MMDCCCXCIIII +MMMCDXXIIII +MMMMXXVIII +MMMMDCCCCLXVIII +MDCCCXX +MMMMCDXXI +MMMMDLXXXIX +CCXVI +MDVIII +MMCCLXXI +MMMDCCCLXXI +MMMCCCLXXVI +MMCCLXI +MMMMDCCCXXXIV +DLXXXVI +MMMMDXXXII +MMMXXIIII +MMMMCDIV +MMMMCCCXLVIII +MMMMCXXXVIII +MMMCCCLXVI +MDCCXVIII +MMCXX +CCCLIX +MMMMDCCLXXII +MDCCCLXXV +MMMMDCCCXXIV +DCCCXXXXVIII +MMMDCCCCXXXVIIII +MMMMCCXXXV +MDCLXXXIII +MMCCLXXXIV +MCLXXXXIIII +DXXXXIII +MCCCXXXXVIII +MMCLXXIX +MMMMCCLXIV +MXXII +MMMCXIX +MDCXXXVII +MMDCCVI +MCLXXXXVIII +MMMCXVI +MCCCLX +MMMCDX +CCLXVIIII +MMMCCLX +MCXXVIII +LXXXII +MCCCCLXXXI +MMMI +MMMCCCLXIV +MMMCCCXXVIIII +CXXXVIII +MMCCCXX +MMMCCXXVIIII +MCCLXVI +MMMCCCCXXXXVI +MMDCCXCIX +MCMLXXI +MMCCLXVIII +CDLXXXXIII +MMMMDCCXXII +MMMMDCCLXXXVII +MMMDCCLIV +MMCCLXIII +MDXXXVII +DCCXXXIIII +MCII +MMMDCCCLXXI +MMMLXXIII +MDCCCLIII +MMXXXVIII +MDCCXVIIII +MDCCCCXXXVII +MMCCCXVI +MCMXXII +MMMCCCLVIII +MMMMDCCCXX +MCXXIII +MMMDLXI +MMMMDXXII +MDCCCX +MMDXCVIIII +MMMDCCCCVIII +MMMMDCCCCXXXXVI +MMDCCCXXXV +MMCXCIV +MCMLXXXXIII +MMMCCCLXXVI +MMMMDCLXXXV +CMLXIX +DCXCII +MMXXVIII +MMMMCCCXXX +XXXXVIIII \ No newline at end of file diff --git a/project_euler/problem_089/sol1.py b/project_euler/problem_089/sol1.py new file mode 100644 index 000000000000..11582aa4ab1a --- /dev/null +++ b/project_euler/problem_089/sol1.py @@ -0,0 +1,141 @@ +""" +Project Euler Problem 89: https://projecteuler.net/problem=89 + +For a number written in Roman numerals to be considered valid there are basic rules +which must be followed. Even though the rules allow some numbers to be expressed in +more than one way there is always a "best" way of writing a particular number. + +For example, it would appear that there are at least six ways of writing the number +sixteen: + +IIIIIIIIIIIIIIII +VIIIIIIIIIII +VVIIIIII +XIIIIII +VVVI +XVI + +However, according to the rules only XIIIIII and XVI are valid, and the last example +is considered to be the most efficient, as it uses the least number of numerals. + +The 11K text file, roman.txt (right click and 'Save Link/Target As...'), contains one +thousand numbers written in valid, but not necessarily minimal, Roman numerals; see +About... Roman Numerals for the definitive rules for this problem. + +Find the number of characters saved by writing each of these in their minimal form. + +Note: You can assume that all the Roman numerals in the file contain no more than four +consecutive identical units. +""" + +import os + +SYMBOLS = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} + + +def parse_roman_numerals(numerals: str) -> int: + """ + Converts a string of roman numerals to an integer. + e.g. + >>> parse_roman_numerals("LXXXIX") + 89 + >>> parse_roman_numerals("IIII") + 4 + """ + + total_value = 0 + + index = 0 + while index < len(numerals) - 1: + current_value = SYMBOLS[numerals[index]] + next_value = SYMBOLS[numerals[index + 1]] + if current_value < next_value: + total_value -= current_value + else: + total_value += current_value + index += 1 + total_value += SYMBOLS[numerals[index]] + + return total_value + + +def generate_roman_numerals(num: int) -> str: + """ + Generates a string of roman numerals for a given integer. + e.g. + >>> generate_roman_numerals(89) + 'LXXXIX' + >>> generate_roman_numerals(4) + 'IV' + """ + + numerals = "" + + m_count = num // 1000 + numerals += m_count * "M" + num %= 1000 + + c_count = num // 100 + if c_count == 9: + numerals += "CM" + c_count -= 9 + elif c_count == 4: + numerals += "CD" + c_count -= 4 + if c_count >= 5: + numerals += "D" + c_count -= 5 + numerals += c_count * "C" + num %= 100 + + x_count = num // 10 + if x_count == 9: + numerals += "XC" + x_count -= 9 + elif x_count == 4: + numerals += "XL" + x_count -= 4 + if x_count >= 5: + numerals += "L" + x_count -= 5 + numerals += x_count * "X" + num %= 10 + + if num == 9: + numerals += "IX" + num -= 9 + elif num == 4: + numerals += "IV" + num -= 4 + if num >= 5: + numerals += "V" + num -= 5 + numerals += num * "I" + + return numerals + + +def solution(roman_numerals_filename: str = "/p089_roman.txt") -> int: + """ + Calculates and returns the answer to project euler problem 89. + + >>> solution("/numeralcleanup_test.txt") + 16 + """ + + savings = 0 + + file1 = open(os.path.dirname(__file__) + roman_numerals_filename, "r") + lines = file1.readlines() + for line in lines: + original = line.strip() + num = parse_roman_numerals(original) + shortened = generate_roman_numerals(num) + savings += len(original) - len(shortened) + + return savings + + +if __name__ == "__main__": + + print(f"{solution() = }") From dccfddfda46f2bac28e888ed7023e2485d4cb094 Mon Sep 17 00:00:00 2001 From: Akash G Krishnan Date: Sat, 21 Nov 2020 12:28:52 +0530 Subject: [PATCH 088/195] Changed how the Visited nodes are tracked (#3811) Updated the code to track visited Nodes with Set data structure instead of Lists to bring down the lookup time in visited from O(N) to O(1) as doing O(N) lookup each time in the visited List will become significantly slow when the graph grows --- graphs/bfs_shortest_path.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/graphs/bfs_shortest_path.py b/graphs/bfs_shortest_path.py index 1655ca64208d..754ba403537e 100644 --- a/graphs/bfs_shortest_path.py +++ b/graphs/bfs_shortest_path.py @@ -1,8 +1,6 @@ """Breadth-first search shortest path implementations. - doctest: python -m doctest -v bfs_shortest_path.py - Manual test: python bfs_shortest_path.py """ @@ -19,22 +17,19 @@ def bfs_shortest_path(graph: dict, start, goal) -> str: """Find shortest path between `start` and `goal` nodes. - Args: graph (dict): node/list of neighboring nodes key/value pairs. start: start node. goal: target node. - Returns: Shortest path between `start` and `goal` nodes as a string of nodes. 'Not found' string if no path found. - Example: >>> bfs_shortest_path(graph, "G", "D") ['G', 'C', 'A', 'B', 'D'] """ # keep track of explored nodes - explored = [] + explored = set() # keep track of all the paths to be checked queue = [[start]] @@ -61,7 +56,7 @@ def bfs_shortest_path(graph: dict, start, goal) -> str: return new_path # mark node as explored - explored.append(node) + explored.add(node) # in case there's no path between the 2 nodes return "So sorry, but a connecting path doesn't exist :(" @@ -69,16 +64,13 @@ def bfs_shortest_path(graph: dict, start, goal) -> str: def bfs_shortest_path_distance(graph: dict, start, target) -> int: """Find shortest path distance between `start` and `target` nodes. - Args: graph: node/list of neighboring nodes key/value pairs. start: node to start search from. target: node to search for. - Returns: Number of edges in shortest path between `start` and `target` nodes. -1 if no path exists. - Example: >>> bfs_shortest_path_distance(graph, "G", "D") 4 @@ -92,7 +84,7 @@ def bfs_shortest_path_distance(graph: dict, start, target) -> int: if start == target: return 0 queue = [start] - visited = [start] + visited = set(start) # Keep tab on distances from `start` node. dist = {start: 0, target: -1} while queue: @@ -103,7 +95,7 @@ def bfs_shortest_path_distance(graph: dict, start, target) -> int: ) for adjacent in graph[node]: if adjacent not in visited: - visited.append(adjacent) + visited.add(adjacent) queue.append(adjacent) dist[adjacent] = dist[node] + 1 return dist[target] From 2222757c80128a93ac6fbd5f3e8dbf87e7ba1b8c Mon Sep 17 00:00:00 2001 From: Niranjan Hegde Date: Sat, 21 Nov 2020 13:34:08 +0530 Subject: [PATCH 089/195] Web programming contribution (#2436) * Currency Converter * currency converter * Currency Converter * currency converter * implemented changes * Implemented changes requested * TESTING = os.getenv("CONTINUOUS_INTEGRATION", False) * Update currency_converter.py * Update currency_converter.py Co-authored-by: Christian Clauss --- web_programming/currency_converter.py | 192 ++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 web_programming/currency_converter.py diff --git a/web_programming/currency_converter.py b/web_programming/currency_converter.py new file mode 100644 index 000000000000..6aed2a5578a5 --- /dev/null +++ b/web_programming/currency_converter.py @@ -0,0 +1,192 @@ +""" +This is used to convert the currency using the Amdoren Currency API +https://www.amdoren.com +""" + +import os + +import requests + +URL_BASE = "https://www.amdoren.com/api/currency.php" +TESTING = os.getenv("CI", False) +API_KEY = os.getenv("AMDOREN_API_KEY") +if not API_KEY and not TESTING: + raise KeyError("Please put your API key in an environment variable.") + + +# Currency and their description +list_of_currencies = """ +AED United Arab Emirates Dirham +AFN Afghan Afghani +ALL Albanian Lek +AMD Armenian Dram +ANG Netherlands Antillean Guilder +AOA Angolan Kwanza +ARS Argentine Peso +AUD Australian Dollar +AWG Aruban Florin +AZN Azerbaijani Manat +BAM Bosnia & Herzegovina Convertible Mark +BBD Barbadian Dollar +BDT Bangladeshi Taka +BGN Bulgarian Lev +BHD Bahraini Dinar +BIF Burundian Franc +BMD Bermudian Dollar +BND Brunei Dollar +BOB Bolivian Boliviano +BRL Brazilian Real +BSD Bahamian Dollar +BTN Bhutanese Ngultrum +BWP Botswana Pula +BYN Belarus Ruble +BZD Belize Dollar +CAD Canadian Dollar +CDF Congolese Franc +CHF Swiss Franc +CLP Chilean Peso +CNY Chinese Yuan +COP Colombian Peso +CRC Costa Rican Colon +CUC Cuban Convertible Peso +CVE Cape Verdean Escudo +CZK Czech Republic Koruna +DJF Djiboutian Franc +DKK Danish Krone +DOP Dominican Peso +DZD Algerian Dinar +EGP Egyptian Pound +ERN Eritrean Nakfa +ETB Ethiopian Birr +EUR Euro +FJD Fiji Dollar +GBP British Pound Sterling +GEL Georgian Lari +GHS Ghanaian Cedi +GIP Gibraltar Pound +GMD Gambian Dalasi +GNF Guinea Franc +GTQ Guatemalan Quetzal +GYD Guyanaese Dollar +HKD Hong Kong Dollar +HNL Honduran Lempira +HRK Croatian Kuna +HTG Haiti Gourde +HUF Hungarian Forint +IDR Indonesian Rupiah +ILS Israeli Shekel +INR Indian Rupee +IQD Iraqi Dinar +IRR Iranian Rial +ISK Icelandic Krona +JMD Jamaican Dollar +JOD Jordanian Dinar +JPY Japanese Yen +KES Kenyan Shilling +KGS Kyrgystani Som +KHR Cambodian Riel +KMF Comorian Franc +KPW North Korean Won +KRW South Korean Won +KWD Kuwaiti Dinar +KYD Cayman Islands Dollar +KZT Kazakhstan Tenge +LAK Laotian Kip +LBP Lebanese Pound +LKR Sri Lankan Rupee +LRD Liberian Dollar +LSL Lesotho Loti +LYD Libyan Dinar +MAD Moroccan Dirham +MDL Moldovan Leu +MGA Malagasy Ariary +MKD Macedonian Denar +MMK Myanma Kyat +MNT Mongolian Tugrik +MOP Macau Pataca +MRO Mauritanian Ouguiya +MUR Mauritian Rupee +MVR Maldivian Rufiyaa +MWK Malawi Kwacha +MXN Mexican Peso +MYR Malaysian Ringgit +MZN Mozambican Metical +NAD Namibian Dollar +NGN Nigerian Naira +NIO Nicaragua Cordoba +NOK Norwegian Krone +NPR Nepalese Rupee +NZD New Zealand Dollar +OMR Omani Rial +PAB Panamanian Balboa +PEN Peruvian Nuevo Sol +PGK Papua New Guinean Kina +PHP Philippine Peso +PKR Pakistani Rupee +PLN Polish Zloty +PYG Paraguayan Guarani +QAR Qatari Riyal +RON Romanian Leu +RSD Serbian Dinar +RUB Russian Ruble +RWF Rwanda Franc +SAR Saudi Riyal +SBD Solomon Islands Dollar +SCR Seychellois Rupee +SDG Sudanese Pound +SEK Swedish Krona +SGD Singapore Dollar +SHP Saint Helena Pound +SLL Sierra Leonean Leone +SOS Somali Shilling +SRD Surinamese Dollar +SSP South Sudanese Pound +STD Sao Tome and Principe Dobra +SYP Syrian Pound +SZL Swazi Lilangeni +THB Thai Baht +TJS Tajikistan Somoni +TMT Turkmenistani Manat +TND Tunisian Dinar +TOP Tonga Paanga +TRY Turkish Lira +TTD Trinidad and Tobago Dollar +TWD New Taiwan Dollar +TZS Tanzanian Shilling +UAH Ukrainian Hryvnia +UGX Ugandan Shilling +USD United States Dollar +UYU Uruguayan Peso +UZS Uzbekistan Som +VEF Venezuelan Bolivar +VND Vietnamese Dong +VUV Vanuatu Vatu +WST Samoan Tala +XAF Central African CFA franc +XCD East Caribbean Dollar +XOF West African CFA franc +XPF CFP Franc +YER Yemeni Rial +ZAR South African Rand +ZMW Zambian Kwacha +""" + + +def convert_currency( + from_: str = "USD", to: str = "INR", amount: float = 1.0, api_key: str = API_KEY +) -> str: + """https://www.amdoren.com/currency-api/""" + params = locals() + params["from"] = params.pop("from_") + res = requests.get(URL_BASE, params=params).json() + return str(res["amount"]) if res["error"] == 0 else res["error_message"] + + +if __name__ == "__main__": + print( + convert_currency( + input("Enter from currency: ").strip(), + input("Enter to currency: ").strip(), + float(input("Enter the amount: ").strip()), + ) + ) From 94c25c643d14496229b9bc7c1e630e5ad49b7ec5 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 21 Nov 2020 16:12:00 +0530 Subject: [PATCH 090/195] Remove workflow file, task checked by the bot (#3917) --- .github/workflows/auto_close_empty_issues.yml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/auto_close_empty_issues.yml diff --git a/.github/workflows/auto_close_empty_issues.yml b/.github/workflows/auto_close_empty_issues.yml deleted file mode 100644 index a6334d6ade32..000000000000 --- a/.github/workflows/auto_close_empty_issues.yml +++ /dev/null @@ -1,20 +0,0 @@ -# GitHub Action that uses close-issue auto-close empty issues after they are opened. -# If the issue body text is empty the Action auto-closes it and sends a notification. -# Otherwise if the issue body is not empty, it does nothing and the issue remains open. -# https://github.com/marketplace/actions/close-issue - -name: auto_close_empty_issues -on: - issues: - types: [opened] -jobs: - check-issue-body-not-empty: - runs-on: ubuntu-latest - steps: - - if: github.event.issue.body == 0 - name: Close Issue - uses: peter-evans/close-issue@v1 - with: - comment: | - Issue body must contain content. - Auto-closing this issue. From 18416c2f5b9117151e149fdebdcbc0f67c4e2def Mon Sep 17 00:00:00 2001 From: Joyce Date: Mon, 23 Nov 2020 13:37:42 +0800 Subject: [PATCH 091/195] [mypy] math/sieve_of_eratosthenes: Add type hints (#2627) * add type hints to math/sieve * add doctest * math/sieve: remove manual doctest * add check for negative * Update maths/sieve_of_eratosthenes.py * Update sieve_of_eratosthenes.py Co-authored-by: Dhruv Manilawala --- maths/sieve_of_eratosthenes.py | 38 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/maths/sieve_of_eratosthenes.py b/maths/sieve_of_eratosthenes.py index faf6fc0f9a98..47a086546900 100644 --- a/maths/sieve_of_eratosthenes.py +++ b/maths/sieve_of_eratosthenes.py @@ -8,54 +8,58 @@ Reference: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes doctest provider: Bruno Simas Hadlich (https://github.com/brunohadlich) -Also thanks Dmitry (https://github.com/LizardWizzard) for finding the problem +Also thanks to Dmitry (https://github.com/LizardWizzard) for finding the problem """ import math +from typing import List -def sieve(n): +def prime_sieve(num: int) -> List[int]: """ Returns a list with all prime numbers up to n. - >>> sieve(50) + >>> prime_sieve(50) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] - >>> sieve(25) + >>> prime_sieve(25) [2, 3, 5, 7, 11, 13, 17, 19, 23] - >>> sieve(10) + >>> prime_sieve(10) [2, 3, 5, 7] - >>> sieve(9) + >>> prime_sieve(9) [2, 3, 5, 7] - >>> sieve(2) + >>> prime_sieve(2) [2] - >>> sieve(1) + >>> prime_sieve(1) [] """ - l = [True] * (n + 1) # noqa: E741 + if num <= 0: + raise ValueError(f"{num}: Invalid input, please enter a positive integer.") + + sieve = [True] * (num + 1) prime = [] start = 2 - end = int(math.sqrt(n)) + end = int(math.sqrt(num)) while start <= end: # If start is a prime - if l[start] is True: + if sieve[start] is True: prime.append(start) # Set multiples of start be False - for i in range(start * start, n + 1, start): - if l[i] is True: - l[i] = False + for i in range(start * start, num + 1, start): + if sieve[i] is True: + sieve[i] = False start += 1 - for j in range(end + 1, n + 1): - if l[j] is True: + for j in range(end + 1, num + 1): + if sieve[j] is True: prime.append(j) return prime if __name__ == "__main__": - print(sieve(int(input("Enter n: ").strip()))) + print(prime_sieve(int(input("Enter a positive integer: ").strip()))) From 12849c6123a84a6f7042db16fd33c45fbeee6d60 Mon Sep 17 00:00:00 2001 From: Mikail Farid Date: Mon, 23 Nov 2020 07:11:28 +0100 Subject: [PATCH 092/195] Renamed octal_to_decimal to octal_to_decimal.py (#3420) * Renamed octal_to_decimal to octal_to_decimal.py * Updated octal_to_decimal.py * modified doctests * updated octal_to_decimal.py --- conversions/{octal_to_decimal => octal_to_decimal.py} | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) rename conversions/{octal_to_decimal => octal_to_decimal.py} (77%) diff --git a/conversions/octal_to_decimal b/conversions/octal_to_decimal.py similarity index 77% rename from conversions/octal_to_decimal rename to conversions/octal_to_decimal.py index a5b027e3ae8d..5a7373fef7e3 100644 --- a/conversions/octal_to_decimal +++ b/conversions/octal_to_decimal.py @@ -9,10 +9,16 @@ def oct_to_decimal(oct_string: str) -> int: >>> oct_to_decimal("-45") -37 >>> oct_to_decimal("2-0Fm") + Traceback (most recent call last): + ... ValueError: Non-octal value was passed to the function >>> oct_to_decimal("") - ValueError: Empty string value was passed to the function + Traceback (most recent call last): + ... + ValueError: Empty string was passed to the function >>> oct_to_decimal("19") + Traceback (most recent call last): + ... ValueError: Non-octal value was passed to the function """ oct_string = str(oct_string).strip() @@ -21,7 +27,7 @@ def oct_to_decimal(oct_string: str) -> int: is_negative = oct_string[0] == "-" if is_negative: oct_string = oct_string[1:] - if not all(0 <= int(char) <= 7 for char in oct_string): + if not oct_string.isdigit() or not all(0 <= int(char) <= 7 for char in oct_string): raise ValueError("Non-octal value was passed to the function") decimal_number = 0 for char in oct_string: From e8c25b4573dcaf961aa517a16a50064b7a812887 Mon Sep 17 00:00:00 2001 From: Tan Yong He Date: Mon, 23 Nov 2020 15:31:43 +0800 Subject: [PATCH 093/195] Improve Base16 Codebase (#3534) * Add doctest and remove input() usage * Apply suggestions from code review Co-authored-by: Dhruv Manilawala --- ciphers/base16.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ciphers/base16.py b/ciphers/base16.py index 0210315d54e6..f27ea4628e54 100644 --- a/ciphers/base16.py +++ b/ciphers/base16.py @@ -1,13 +1,22 @@ import base64 -def main(): - inp = input("->") +def encode_to_b16(inp: str) -> bytes: + """ + Encodes a given utf-8 string into base-16. + >>> encode_to_b16('Hello World!') + b'48656C6C6F20576F726C6421' + >>> encode_to_b16('HELLO WORLD!') + b'48454C4C4F20574F524C4421' + >>> encode_to_b16('') + b'' + """ encoded = inp.encode("utf-8") # encoded the input (we need a bytes like object) b16encoded = base64.b16encode(encoded) # b16encoded the encoded string - print(b16encoded) - print(base64.b16decode(b16encoded).decode("utf-8")) # decoded it + return b16encoded if __name__ == "__main__": - main() + import doctest + + doctest.testmod() From a239b1ae626ff973566449a6931411e6617a9952 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 24 Nov 2020 12:41:10 +0100 Subject: [PATCH 094/195] Python 3.9 (#3926) * Upgrade to Python 3.9 * pip install wheel for faster builds * updating DIRECTORY.md * requirements.txt: tensorflow; python_version < '3.9' * keras requires tensorflow * Rename lstm_prediction.py to lstm_prediction.py_tf * Update requirements.txt * updating DIRECTORY.md * Update requirements.txt Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Dhruv Manilawala --- .github/workflows/build.yml | 4 ++-- DIRECTORY.md | 3 +-- .../lstm/{lstm_prediction.py => lstm_prediction.py_tf} | 0 requirements.txt | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) rename machine_learning/lstm/{lstm_prediction.py => lstm_prediction.py_tf} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01ac9aea7a7c..ae9b4e36b1ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,14 +12,14 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: "3.8" + python-version: "3.9" - uses: actions/cache@v2 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools six + python -m pip install --upgrade pip setuptools six wheel python -m pip install pytest-cov -r requirements.txt - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . diff --git a/DIRECTORY.md b/DIRECTORY.md index 2b3f3073c3d4..e1e57307d593 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -356,8 +356,6 @@ * [Linear Discriminant Analysis](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/linear_discriminant_analysis.py) * [Linear Regression](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/linear_regression.py) * [Logistic Regression](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/logistic_regression.py) - * Lstm - * [Lstm Prediction](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/lstm/lstm_prediction.py) * [Multilayer Perceptron Classifier](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/multilayer_perceptron_classifier.py) * [Polymonial Regression](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/polymonial_regression.py) * [Random Forest Classifier](https://github.com/TheAlgorithms/Python/blob/master/machine_learning/random_forest_classifier.py) @@ -866,6 +864,7 @@ * [Covid Stats Via Xpath](https://github.com/TheAlgorithms/Python/blob/master/web_programming/covid_stats_via_xpath.py) * [Crawl Google Results](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_results.py) * [Crawl Google Scholar Citation](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_scholar_citation.py) + * [Currency Converter](https://github.com/TheAlgorithms/Python/blob/master/web_programming/currency_converter.py) * [Current Stock Price](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_stock_price.py) * [Current Weather](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_weather.py) * [Daily Horoscope](https://github.com/TheAlgorithms/Python/blob/master/web_programming/daily_horoscope.py) diff --git a/machine_learning/lstm/lstm_prediction.py b/machine_learning/lstm/lstm_prediction.py_tf similarity index 100% rename from machine_learning/lstm/lstm_prediction.py rename to machine_learning/lstm/lstm_prediction.py_tf diff --git a/requirements.txt b/requirements.txt index 8bbb8d524ed4..349d88944656 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ beautifulsoup4 fake_useragent -keras +keras; python_version < '3.9' lxml matplotlib numpy @@ -13,5 +13,5 @@ scikit-fuzzy sklearn statsmodels sympy -tensorflow +tensorflow; python_version < '3.9' xgboost From d429bb8fb15b3cd88705adab900c8a9d40d057d9 Mon Sep 17 00:00:00 2001 From: Sullivan <38718448+Epic-R-R@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:48:00 +0330 Subject: [PATCH 095/195] Create instagram_pic (#3945) * Create instagram_pic * Update instagram_pic * Update instagram_pic * isort * Update instagram_pic.py Co-authored-by: Christian Clauss --- web_programming/instagram_pic.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 web_programming/instagram_pic.py diff --git a/web_programming/instagram_pic.py b/web_programming/instagram_pic.py new file mode 100644 index 000000000000..8521da674d7d --- /dev/null +++ b/web_programming/instagram_pic.py @@ -0,0 +1,16 @@ +from datetime import datetime + +import requests +from bs4 import BeautifulSoup + +if __name__ == "__main__": + url = input("Enter image url: ").strip() + print(f"Downloading image from {url} ...") + soup = BeautifulSoup(requests.get(url).content, "html.parser") + # The image URL is in the content field of the first meta tag with property og:image + image_url = soup.find("meta", {"property": "og:image"})["content"] + image_data = requests.get(image_url).content + file_name = f"{datetime.now():%Y-%m-%d_%H:%M:%S}.jpg" + with open(file_name, "wb") as fp: + fp.write(image_data) + print(f"Done. Image saved to disk as {file_name}.") From d485986644d1ea06d5f226193d8e6d7004a2951c Mon Sep 17 00:00:00 2001 From: Cho Yin Yong Date: Tue, 24 Nov 2020 19:30:15 -0500 Subject: [PATCH 096/195] Add a divide and conquer method in finding the maximum difference pair (#3692) * A divide and conquer method in finding the maximum difference pair * fix formatting issues * fix formatting issues * add doctest runner --- divide_and_conquer/max_difference_pair.py | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 divide_and_conquer/max_difference_pair.py diff --git a/divide_and_conquer/max_difference_pair.py b/divide_and_conquer/max_difference_pair.py new file mode 100644 index 000000000000..b976aca43137 --- /dev/null +++ b/divide_and_conquer/max_difference_pair.py @@ -0,0 +1,47 @@ +from typing import List + + +def max_difference(a: List[int]) -> (int, int): + """ + We are given an array A[1..n] of integers, n >= 1. We want to + find a pair of indices (i, j) such that + 1 <= i <= j <= n and A[j] - A[i] is as large as possible. + + Explanation: + https://www.geeksforgeeks.org/maximum-difference-between-two-elements/ + + >>> max_difference([5, 11, 2, 1, 7, 9, 0, 7]) + (1, 9) + """ + # base case + if len(a) == 1: + return a[0], a[0] + else: + # split A into half. + first = a[: len(a) // 2] + second = a[len(a) // 2 :] + + # 2 sub problems, 1/2 of original size. + small1, big1 = max_difference(first) + small2, big2 = max_difference(second) + + # get min of first and max of second + # linear time + min_first = min(first) + max_second = max(second) + + # 3 cases, either (small1, big1), + # (min_first, max_second), (small2, big2) + # constant comparisons + if big2 - small2 > max_second - min_first and big2 - small2 > big1 - small1: + return small2, big2 + elif big1 - small1 > max_second - min_first: + return small1, big1 + else: + return min_first, max_second + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4669f64c0e56cc4b83cf6937ae6aa7b194b1c019 Mon Sep 17 00:00:00 2001 From: lawric1 <67882089+lawric1@users.noreply.github.com> Date: Wed, 25 Nov 2020 02:13:14 -0300 Subject: [PATCH 097/195] Fixes: #3944 Authentication error; use tokens instead (#3949) * fixes #3944 authentication error * Fixes: #3944 authentication error * Fixed docstring failure in pre-commit, Fixed request.get params to GitHub REST API standards * run black formatter * Add USER_TOKEN constant and checks if empty, removes deprecated docstring * Add descriptive dict type hint, change headers format to f-string * Add Accept header * Fix pre-commit error * Fix pre-commit error * Add test for fetch_github_info * Remove test function from main file * Create test_fetch_github_info.py * Update test_fetch_github_info.py * Update test_fetch_github_info.py * No need to cover __name__ == __main__ block Co-authored-by: Dhruv Manilawala --- web_programming/fetch_github_info.py | 50 +++++++++++++++++------ web_programming/test_fetch_github_info.py | 27 ++++++++++++ 2 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 web_programming/test_fetch_github_info.py diff --git a/web_programming/fetch_github_info.py b/web_programming/fetch_github_info.py index 227598bb20ab..c9198460f211 100644 --- a/web_programming/fetch_github_info.py +++ b/web_programming/fetch_github_info.py @@ -1,26 +1,50 @@ #!/usr/bin/env python3 - """ Created by sarathkaul on 14/11/19 +Updated by lawric1 on 24/11/20 -Basic authentication using an API password is deprecated and will soon no longer work. -Visit https://developer.github.com/changes/2020-02-14-deprecating-password-auth -for more information around suggested workarounds and removal dates. -""" +Authentication will be made via access token. +To generate your personal access token visit https://github.com/settings/tokens. + +NOTE: +Never hardcode any credential information in the code. Always use an environment +file to store the private information and use the `os` module to get the information +during runtime. +Create a ".env" file in the root directory and write these two lines in that file +with your token:: + +#!/usr/bin/env bash +export USER_TOKEN="" +""" +import os +from typing import Any, Dict import requests -_GITHUB_API = "https://api.github.com/user" +BASE_URL = "https://api.github.com/" +# https://docs.github.com/en/free-pro-team@latest/rest/reference/users#get-the-authenticated-user +AUTHENTICATED_USER_ENDPOINT = BASE_URL + "/user" -def fetch_github_info(auth_user: str, auth_pass: str) -> dict: +# https://github.com/settings/tokens +USER_TOKEN = os.environ.get("USER_TOKEN", "") + + +def fetch_github_info(auth_token: str) -> Dict[Any, Any]: """ Fetch GitHub info of a user using the requests module """ - return requests.get(_GITHUB_API, auth=(auth_user, auth_pass)).json() - - -if __name__ == "__main__": - for key, value in fetch_github_info("", "").items(): - print(f"{key}: {value}") + headers = { + "Authorization": f"token {auth_token}", + "Accept": "application/vnd.github.v3+json", + } + return requests.get(AUTHENTICATED_USER_ENDPOINT, headers=headers).json() + + +if __name__ == "__main__": # pragma: no cover + if USER_TOKEN: + for key, value in fetch_github_info(USER_TOKEN).items(): + print(f"{key}: {value}") + else: + raise ValueError("'USER_TOKEN' field cannot be empty.") diff --git a/web_programming/test_fetch_github_info.py b/web_programming/test_fetch_github_info.py new file mode 100644 index 000000000000..2da97c782df7 --- /dev/null +++ b/web_programming/test_fetch_github_info.py @@ -0,0 +1,27 @@ +import json + +import requests + +from .fetch_github_info import AUTHENTICATED_USER_ENDPOINT, fetch_github_info + + +def test_fetch_github_info(monkeypatch): + class FakeResponse: + def __init__(self, content) -> None: + assert isinstance(content, (bytes, str)) + self.content = content + + def json(self): + return json.loads(self.content) + + def mock_response(*args, **kwargs): + assert args[0] == AUTHENTICATED_USER_ENDPOINT + assert "Authorization" in kwargs["headers"] + assert kwargs["headers"]["Authorization"].startswith("token ") + assert "Accept" in kwargs["headers"] + return FakeResponse(b'{"login":"test","id":1}') + + monkeypatch.setattr(requests, "get", mock_response) + result = fetch_github_info("token") + assert result["login"] == "test" + assert result["id"] == 1 From c0429236352c02f5212246e768cf23f036f51918 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Wed, 25 Nov 2020 13:23:49 +0530 Subject: [PATCH 098/195] Update stalebot to take 5 actions per hour (#3922) * Update stalebot to take 5 actions per hour * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index ba6fd155d7a3..36ca56266b26 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -27,7 +27,7 @@ exemptAssignees: false staleLabel: stale # Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 30 +limitPerRun: 5 # Comment to post when removing the stale label. # unmarkComment: > From 86b9f18ef5d73a7dfba63c878a44e16219535f10 Mon Sep 17 00:00:00 2001 From: YeonJeongLee00 <67946956+YeonJeongLee00@users.noreply.github.com> Date: Wed, 25 Nov 2020 17:54:31 +0900 Subject: [PATCH 099/195] Create intro_sort.py (#3877) * Create intro_sort.py * modified intro_sort.py * add doctest * modified code black intro_sort.py * add more test * Update intro_sort.py added doctest, modified code * black intro_sort.py * add type hint * modified code --- sorts/intro_sort.py | 173 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 sorts/intro_sort.py diff --git a/sorts/intro_sort.py b/sorts/intro_sort.py new file mode 100644 index 000000000000..f0e3645adbb7 --- /dev/null +++ b/sorts/intro_sort.py @@ -0,0 +1,173 @@ +""" +Introspective Sort is hybrid sort (Quick Sort + Heap Sort + Insertion Sort) +if the size of the list is under 16, use insertion sort +https://en.wikipedia.org/wiki/Introsort +""" +import math + + +def insertion_sort(array: list, start: int = 0, end: int = 0) -> list: + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> insertion_sort(array, 0, len(array)) + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + """ + end = end or len(array) + for i in range(start, end): + temp_index = i + temp_index_value = array[i] + while temp_index != start and temp_index_value < array[temp_index - 1]: + array[temp_index] = array[temp_index - 1] + temp_index -= 1 + array[temp_index] = temp_index_value + return array + + +def heapify(array: list, index: int, heap_size: int) -> None: # Max Heap + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> heapify(array, len(array) // 2 ,len(array)) + """ + largest = index + left_index = 2 * index + 1 # Left Node + right_index = 2 * index + 2 # Right Node + + if left_index < heap_size and array[largest] < array[left_index]: + largest = left_index + + if right_index < heap_size and array[largest] < array[right_index]: + largest = right_index + + if largest != index: + array[index], array[largest] = array[largest], array[index] + heapify(array, largest, heap_size) + + +def heap_sort(array: list) -> list: + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> heap_sort(array) + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + """ + n = len(array) + + for i in range(n // 2, -1, -1): + heapify(array, i, n) + + for i in range(n - 1, 0, -1): + array[i], array[0] = array[0], array[i] + heapify(array, 0, i) + + return array + + +def median_of_3( + array: list, first_index: int, middle_index: int, last_index: int +) -> int: + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> median_of_3(array, 0, 0 + ((len(array) - 0) // 2) + 1, len(array) - 1) + 12 + """ + if (array[first_index] > array[middle_index]) != ( + array[first_index] > array[last_index] + ): + return array[first_index] + elif (array[middle_index] > array[first_index]) != ( + array[middle_index] > array[last_index] + ): + return array[middle_index] + else: + return array[last_index] + + +def partition(array: list, low: int, high: int, pivot: int) -> int: + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> partition(array, 0, len(array), 12) + 8 + """ + i = low + j = high + while True: + while array[i] < pivot: + i += 1 + j -= 1 + while pivot < array[j]: + j -= 1 + if i >= j: + return i + array[i], array[j] = array[j], array[i] + i += 1 + + +def sort(array: list) -> list: + """ + :param collection: some mutable ordered collection with heterogeneous + comparable items inside + :return: the same collection ordered by ascending + + Examples: + >>> sort([4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12]) + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + + >>> sort([-1, -5, -3, -13, -44]) + [-44, -13, -5, -3, -1] + + >>> sort([]) + [] + + >>> sort([5]) + [5] + + >>> sort([-3, 0, -7, 6, 23, -34]) + [-34, -7, -3, 0, 6, 23] + + >>> sort([1.7, 1.0, 3.3, 2.1, 0.3 ]) + [0.3, 1.0, 1.7, 2.1, 3.3] + + >>> sort(['d', 'a', 'b', 'e', 'c']) + ['a', 'b', 'c', 'd', 'e'] + """ + if len(array) == 0: + return array + max_depth = 2 * math.ceil(math.log2(len(array))) + size_threshold = 16 + return intro_sort(array, 0, len(array), size_threshold, max_depth) + + +def intro_sort( + array: list, start: int, end: int, size_threshold: int, max_depth: int +) -> list: + """ + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] + + >>> max_depth = 2 * math.ceil(math.log2(len(array))) + + >>> intro_sort(array, 0, len(array), 16, max_depth) + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] + """ + while end - start > size_threshold: + if max_depth == 0: + return heap_sort(array) + max_depth -= 1 + pivot = median_of_3(array, start, start + ((end - start) // 2) + 1, end - 1) + p = partition(array, start, end, pivot) + intro_sort(array, p, end, size_threshold, max_depth) + end = p + return insertion_sort(array, start, end) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + user_input = input("Enter numbers separated by a comma : ").strip() + unsorted = [float(item) for item in user_input.split(",")] + print(sort(unsorted)) From c4dfb2cda4c57398c1f427bcf54c9f54d77188d5 Mon Sep 17 00:00:00 2001 From: Erdum Date: Wed, 25 Nov 2020 16:01:49 +0500 Subject: [PATCH 100/195] Ohm's Law algorithm added (#3934) * New algorithm added * Errors resolvedc * New Algorithm * New algorithm added * Added new algorithm * work * New algorithm added * Hope this is final * Update electronics/ohms_law.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> * update decimal value & negative value test * update as cclauss suggest * Update electronics/ohms_law.py Co-authored-by: Christian Clauss * updated as suggested by cclauss * update as suggested by cclauss * Update as suggested by cclauss * Update ohms_law.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> Co-authored-by: Christian Clauss --- electronics/ohms_law.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 electronics/ohms_law.py diff --git a/electronics/ohms_law.py b/electronics/ohms_law.py new file mode 100644 index 000000000000..a7b37b635397 --- /dev/null +++ b/electronics/ohms_law.py @@ -0,0 +1,39 @@ +# https://en.wikipedia.org/wiki/Ohm%27s_law + + +def ohms_law(voltage: float, current: float, resistance: float) -> float: + """ + Apply Ohm's Law, on any two given electrical values, which can be voltage, current, + and resistance, and then in a Python dict return name/value pair of the zero value. + + >>> ohms_law(voltage=10, resistance=5, current=0) + {'current': 2.0} + >>> ohms_law(voltage=0, current=0, resistance=10) + Traceback (most recent call last): + ... + ValueError: One and only one argument must be 0 + >>> ohms_law(voltage=0, current=1, resistance=-2) + Traceback (most recent call last): + ... + ValueError: Resistance cannot be negative + >>> ohms_law(resistance=0, voltage=-10, current=1) + {'resistance': -10.0} + >>> ohms_law(voltage=0, current=-1.5, resistance=2) + {'voltage': -3.0} + """ + if (voltage, current, resistance).count(0) != 1: + raise ValueError("One and only one argument must be 0") + if resistance < 0: + raise ValueError("Resistance cannot be negative") + if voltage == 0: + return {"voltage": float(current * resistance)} + elif current == 0: + return {"current": voltage / resistance} + elif resistance == 0: + return {"resistance": voltage / current} + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0f0af0cf14591369b6e5232037822f3b571641f7 Mon Sep 17 00:00:00 2001 From: Hafidh <32499116+hfz1337@users.noreply.github.com> Date: Wed, 25 Nov 2020 13:38:02 +0100 Subject: [PATCH 101/195] Replace base64_cipher.py with an easy to understand version (#3925) * rename base64_cipher.py to base64_encoding.py * edit base64_encoding.py * import necessary modules inside doctests * make it behave like the official implementation * replace format with f-string where possible * replace format with f-string Co-authored-by: Christian Clauss * fix: syntax error due to closing parenthese * reformat code Co-authored-by: Christian Clauss --- ciphers/base64_cipher.py | 89 ----------------------- ciphers/base64_encoding.py | 142 +++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 89 deletions(-) delete mode 100644 ciphers/base64_cipher.py create mode 100644 ciphers/base64_encoding.py diff --git a/ciphers/base64_cipher.py b/ciphers/base64_cipher.py deleted file mode 100644 index 1dbe74a20fe7..000000000000 --- a/ciphers/base64_cipher.py +++ /dev/null @@ -1,89 +0,0 @@ -def encode_base64(text: str) -> str: - r""" - >>> encode_base64('WELCOME to base64 encoding 😁') - 'V0VMQ09NRSB0byBiYXNlNjQgZW5jb2Rpbmcg8J+YgQ==' - >>> encode_base64('AÅᐃ𐀏🤓') - 'QcOF4ZCD8JCAj/CfpJM=' - >>> encode_base64('A'*60) - 'QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB' - """ - base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - - byte_text = bytes(text, "utf-8") # put text in bytes for unicode support - r = "" # the result - c = -len(byte_text) % 3 # the length of padding - p = "=" * c # the padding - s = byte_text + b"\x00" * c # the text to encode - - i = 0 - while i < len(s): - if i > 0 and ((i / 3 * 4) % 76) == 0: - r = r + "\r\n" # for unix newline, put "\n" - - n = (s[i] << 16) + (s[i + 1] << 8) + s[i + 2] - - n1 = (n >> 18) & 63 - n2 = (n >> 12) & 63 - n3 = (n >> 6) & 63 - n4 = n & 63 - - r += base64_chars[n1] + base64_chars[n2] + base64_chars[n3] + base64_chars[n4] - i += 3 - - return r[0 : len(r) - len(p)] + p - - -def decode_base64(text: str) -> str: - r""" - >>> decode_base64('V0VMQ09NRSB0byBiYXNlNjQgZW5jb2Rpbmcg8J+YgQ==') - 'WELCOME to base64 encoding 😁' - >>> decode_base64('QcOF4ZCD8JCAj/CfpJM=') - 'AÅᐃ𐀏🤓' - >>> decode_base64("QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF" - ... "BQUFBQUFBQUFB\r\nQUFB") - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - """ - base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - s = "" - - for i in text: - if i in base64_chars: - s += i - c = "" - else: - if i == "=": - c += "=" - - p = "" - if c == "=": - p = "A" - else: - if c == "==": - p = "AA" - - r = b"" - s = s + p - - i = 0 - while i < len(s): - n = ( - (base64_chars.index(s[i]) << 18) - + (base64_chars.index(s[i + 1]) << 12) - + (base64_chars.index(s[i + 2]) << 6) - + base64_chars.index(s[i + 3]) - ) - - r += bytes([(n >> 16) & 255]) + bytes([(n >> 8) & 255]) + bytes([n & 255]) - - i += 4 - - return str(r[0 : len(r) - len(p)], "utf-8") - - -def main(): - print(encode_base64("WELCOME to base64 encoding 😁")) - print(decode_base64(encode_base64("WELCOME to base64 encoding 😁"))) - - -if __name__ == "__main__": - main() diff --git a/ciphers/base64_encoding.py b/ciphers/base64_encoding.py new file mode 100644 index 000000000000..634afcb89873 --- /dev/null +++ b/ciphers/base64_encoding.py @@ -0,0 +1,142 @@ +B64_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + + +def base64_encode(data: bytes) -> bytes: + """Encodes data according to RFC4648. + + The data is first transformed to binary and appended with binary digits so that its + length becomes a multiple of 6, then each 6 binary digits will match a character in + the B64_CHARSET string. The number of appended binary digits would later determine + how many "=" sign should be added, the padding. + For every 2 binary digits added, a "=" sign is added in the output. + We can add any binary digits to make it a multiple of 6, for instance, consider the + following example: + "AA" -> 0010100100101001 -> 001010 010010 1001 + As can be seen above, 2 more binary digits should be added, so there's 4 + possibilities here: 00, 01, 10 or 11. + That being said, Base64 encoding can be used in Steganography to hide data in these + appended digits. + + >>> from base64 import b64encode + >>> a = b"This pull request is part of Hacktoberfest20!" + >>> b = b"https://tools.ietf.org/html/rfc4648" + >>> c = b"A" + >>> base64_encode(a) == b64encode(a) + True + >>> base64_encode(b) == b64encode(b) + True + >>> base64_encode(c) == b64encode(c) + True + >>> base64_encode("abc") + Traceback (most recent call last): + ... + TypeError: a bytes-like object is required, not 'str' + """ + # Make sure the supplied data is a bytes-like object + if not isinstance(data, bytes): + raise TypeError( + f"a bytes-like object is required, not '{data.__class__.__name__}'" + ) + + binary_stream = "".join(bin(byte)[2:].zfill(8) for byte in data) + + padding_needed = len(binary_stream) % 6 != 0 + + if padding_needed: + # The padding that will be added later + padding = b"=" * ((6 - len(binary_stream) % 6) // 2) + + # Append binary_stream with arbitrary binary digits (0's by default) to make its + # length a multiple of 6. + binary_stream += "0" * (6 - len(binary_stream) % 6) + else: + padding = b"" + + # Encode every 6 binary digits to their corresponding Base64 character + return ( + "".join( + B64_CHARSET[int(binary_stream[index : index + 6], 2)] + for index in range(0, len(binary_stream), 6) + ).encode() + + padding + ) + + +def base64_decode(encoded_data: str) -> bytes: + """Decodes data according to RFC4648. + + This does the reverse operation of base64_encode. + We first transform the encoded data back to a binary stream, take off the + previously appended binary digits according to the padding, at this point we + would have a binary stream whose length is multiple of 8, the last step is + to convert every 8 bits to a byte. + + >>> from base64 import b64decode + >>> a = "VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh" + >>> b = "aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=" + >>> c = "QQ==" + >>> base64_decode(a) == b64decode(a) + True + >>> base64_decode(b) == b64decode(b) + True + >>> base64_decode(c) == b64decode(c) + True + >>> base64_decode("abc") + Traceback (most recent call last): + ... + AssertionError: Incorrect padding + """ + # Make sure encoded_data is either a string or a bytes-like object + if not isinstance(encoded_data, bytes) and not isinstance(encoded_data, str): + raise TypeError( + "argument should be a bytes-like object or ASCII string, not " + f"'{encoded_data.__class__.__name__}'" + ) + + # In case encoded_data is a bytes-like object, make sure it contains only + # ASCII characters so we convert it to a string object + if isinstance(encoded_data, bytes): + try: + encoded_data = encoded_data.decode("utf-8") + except UnicodeDecodeError: + raise ValueError("base64 encoded data should only contain ASCII characters") + + padding = encoded_data.count("=") + + # Check if the encoded string contains non base64 characters + if padding: + assert all( + char in B64_CHARSET for char in encoded_data[:-padding] + ), "Invalid base64 character(s) found." + else: + assert all( + char in B64_CHARSET for char in encoded_data + ), "Invalid base64 character(s) found." + + # Check the padding + assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding" + + if padding: + # Remove padding if there is one + encoded_data = encoded_data[:-padding] + + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + )[: -padding * 2] + else: + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + ) + + data = [ + int(binary_stream[index : index + 8], 2) + for index in range(0, len(binary_stream), 8) + ] + + return bytes(data) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 7eaa3c6aa312dd1dd4a52a560c67c237940e24fe Mon Sep 17 00:00:00 2001 From: Vivek Date: Thu, 26 Nov 2020 06:57:00 +0530 Subject: [PATCH 102/195] added binary_count_trailing_zeros.py (#2557) * added binary_count_trailing_zeros.py * updated binary_count_trailing_zeros.py file * changed file name to count_trailing_zeros.py * updated count_trailing_zeros.py * resolved flake8 error * renamed to binary_count_trailing_zeros.py * added required changes * resolved pre-commit error * added count_setbits.py * resolved errors * changed name to binary_count_setbits.py * updated file * reformated file --- bit_manipulation/binary_count_setbits.py | 41 +++++++++++++++++ .../binary_count_trailing_zeros.py | 44 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 bit_manipulation/binary_count_setbits.py create mode 100644 bit_manipulation/binary_count_trailing_zeros.py diff --git a/bit_manipulation/binary_count_setbits.py b/bit_manipulation/binary_count_setbits.py new file mode 100644 index 000000000000..3c92694533aa --- /dev/null +++ b/bit_manipulation/binary_count_setbits.py @@ -0,0 +1,41 @@ +def binary_count_setbits(a: int) -> int: + """ + Take in 1 integer, return a number that is + the number of 1's in binary representation of that number. + + >>> binary_count_setbits(25) + 3 + >>> binary_count_setbits(36) + 2 + >>> binary_count_setbits(16) + 1 + >>> binary_count_setbits(58) + 4 + >>> binary_count_setbits(4294967295) + 32 + >>> binary_count_setbits(0) + 0 + >>> binary_count_setbits(-10) + Traceback (most recent call last): + ... + ValueError: Input value must be a positive integer + >>> binary_count_setbits(0.8) + Traceback (most recent call last): + ... + TypeError: Input value must be a 'int' type + >>> binary_count_setbits("0") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0: + raise ValueError("Input value must be a positive integer") + elif isinstance(a, float): + raise TypeError("Input value must be a 'int' type") + return bin(a).count("1") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/bit_manipulation/binary_count_trailing_zeros.py b/bit_manipulation/binary_count_trailing_zeros.py new file mode 100644 index 000000000000..f401c4ab9266 --- /dev/null +++ b/bit_manipulation/binary_count_trailing_zeros.py @@ -0,0 +1,44 @@ +from math import log2 + + +def binary_count_trailing_zeros(a: int) -> int: + """ + Take in 1 integer, return a number that is + the number of trailing zeros in binary representation of that number. + + >>> binary_count_trailing_zeros(25) + 0 + >>> binary_count_trailing_zeros(36) + 2 + >>> binary_count_trailing_zeros(16) + 4 + >>> binary_count_trailing_zeros(58) + 1 + >>> binary_count_trailing_zeros(4294967296) + 32 + >>> binary_count_trailing_zeros(0) + 0 + >>> binary_count_trailing_zeros(-10) + Traceback (most recent call last): + ... + ValueError: Input value must be a positive integer + >>> binary_count_trailing_zeros(0.8) + Traceback (most recent call last): + ... + TypeError: Input value must be a 'int' type + >>> binary_count_trailing_zeros("0") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0: + raise ValueError("Input value must be a positive integer") + elif isinstance(a, float): + raise TypeError("Input value must be a 'int' type") + return 0 if (a == 0) else int(log2(a & -a)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From dc831f157039b9c9d3c5e7497e511ba52a7c3953 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Fri, 27 Nov 2020 15:27:12 +0530 Subject: [PATCH 103/195] Cleaned up knapsack and images directory (#3972) --- images/Travis_CI_fail_1.png | Bin 80257 -> 0 bytes images/Travis_CI_fail_2.png | Bin 45660 -> 0 bytes images/__init__.py | 0 {greedy_method => knapsack}/greedy_knapsack.py | 0 {greedy_method => knapsack/tests}/__init__.py | 0 .../tests/test_greedy_knapsack.py | 2 +- knapsack/{ => tests}/test_knapsack.py | 0 7 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 images/Travis_CI_fail_1.png delete mode 100644 images/Travis_CI_fail_2.png delete mode 100644 images/__init__.py rename {greedy_method => knapsack}/greedy_knapsack.py (100%) rename {greedy_method => knapsack/tests}/__init__.py (100%) rename greedy_method/test_knapsack.py => knapsack/tests/test_greedy_knapsack.py (98%) rename knapsack/{ => tests}/test_knapsack.py (100%) diff --git a/images/Travis_CI_fail_1.png b/images/Travis_CI_fail_1.png deleted file mode 100644 index 451e54e4844a0b9f7d501c3d7682869d9acd97fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80257 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 diff --git a/images/Travis_CI_fail_2.png b/images/Travis_CI_fail_2.png deleted file mode 100644 index caa406099da12215efb783b13ae780c4c052fe7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45660 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 Date: Fri, 27 Nov 2020 09:48:20 -0500 Subject: [PATCH 104/195] Create euclidean_distance.py (#3350) * Create distance_formula.py * Remove whitespace * Update distance_formula.py * Update distance_formula.py * Update distance_formula.py * Generalize * Grammar mistake * Rename distance_formula.py to euclidean_distance.py * Update euclidean_distance.py * v1 - > v2 * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update maths/euclidean_distance.py Co-authored-by: Christian Clauss * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update euclidean_distance.py * Update maths/euclidean_distance.py Co-authored-by: Christian Clauss Co-authored-by: Christian Clauss --- maths/euclidean_distance.py | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 maths/euclidean_distance.py diff --git a/maths/euclidean_distance.py b/maths/euclidean_distance.py new file mode 100644 index 000000000000..6e0da6370219 --- /dev/null +++ b/maths/euclidean_distance.py @@ -0,0 +1,62 @@ +from typing import Iterable, Union + +import numpy as np + +Vector = Union[Iterable[float], Iterable[int], np.ndarray] +VectorOut = Union[np.float64, int, float] + + +def euclidean_distance(vector_1: Vector, vector_2: Vector) -> VectorOut: + """ + Calculate the distance between the two endpoints of two vectors. + A vector is defined as a list, tuple, or numpy 1D array. + >>> euclidean_distance((0, 0), (2, 2)) + 2.8284271247461903 + >>> euclidean_distance(np.array([0, 0, 0]), np.array([2, 2, 2])) + 3.4641016151377544 + >>> euclidean_distance(np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8])) + 8.0 + >>> euclidean_distance([1, 2, 3, 4], [5, 6, 7, 8]) + 8.0 + """ + return np.sqrt(np.sum((np.asarray(vector_1) - np.asarray(vector_2)) ** 2)) + + +def euclidean_distance_no_np(vector_1: Vector, vector_2: Vector) -> VectorOut: + """ + Calculate the distance between the two endpoints of two vectors without numpy. + A vector is defined as a list, tuple, or numpy 1D array. + >>> euclidean_distance_no_np((0, 0), (2, 2)) + 2.8284271247461903 + >>> euclidean_distance_no_np([1, 2, 3, 4], [5, 6, 7, 8]) + 8.0 + """ + return sum((v1 - v2) ** 2 for v1, v2 in zip(vector_1, vector_2)) ** (1 / 2) + + +if __name__ == "__main__": + + def benchmark() -> None: + """ + Benchmarks + """ + from timeit import timeit + + print("Without Numpy") + print( + timeit( + "euclidean_distance_no_np([1, 2, 3], [4, 5, 6])", + number=10000, + globals=globals(), + ) + ) + print("With Numpy") + print( + timeit( + "euclidean_distance([1, 2, 3], [4, 5, 6])", + number=10000, + globals=globals(), + ) + ) + + benchmark() From b594928ba6154f427cdae8e2f47c4c2c512e0e30 Mon Sep 17 00:00:00 2001 From: Anubhav Solanki <55892162+AnubhavSolanki@users.noreply.github.com> Date: Fri, 27 Nov 2020 21:33:17 +0530 Subject: [PATCH 105/195] Create weight_conversion.py (#3964) * Create weight_conversion.py * Update weight_conversion.py Co-authored-by: Dhruv Manilawala --- conversions/weight_conversion.py | 287 +++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 conversions/weight_conversion.py diff --git a/conversions/weight_conversion.py b/conversions/weight_conversion.py new file mode 100644 index 000000000000..85515f2f6f88 --- /dev/null +++ b/conversions/weight_conversion.py @@ -0,0 +1,287 @@ +""" +Conversion of weight units. + +__author__ = "Anubhav Solanki" +__license__ = "MIT" +__version__ = "1.0.0" +__maintainer__ = "Anubhav Solanki" +__email__ = "anubhavsolanki0@gmail.com" + +USAGE : +-> Import this file into their respective project. +-> Use the function weight_conversion() for conversion of weight units. +-> Parameters : + -> from_type : From which type you want to convert + -> to_type : To which type you want to convert + -> value : the value which you want to convert + +REFERENCES : + +-> Wikipedia reference: https://en.wikipedia.org/wiki/Kilogram +-> Wikipedia reference: https://en.wikipedia.org/wiki/Gram +-> Wikipedia reference: https://en.wikipedia.org/wiki/Millimetre +-> Wikipedia reference: https://en.wikipedia.org/wiki/Tonne +-> Wikipedia reference: https://en.wikipedia.org/wiki/Long_ton +-> Wikipedia reference: https://en.wikipedia.org/wiki/Short_ton +-> Wikipedia reference: https://en.wikipedia.org/wiki/Pound +-> Wikipedia reference: https://en.wikipedia.org/wiki/Ounce +-> Wikipedia reference: https://en.wikipedia.org/wiki/Fineness#Karat +-> Wikipedia reference: https://en.wikipedia.org/wiki/Dalton_(unit) +""" + +KILOGRAM_CHART = { + "kilogram": 1, + "gram": pow(10, 3), + "milligram": pow(10, 6), + "metric-ton": pow(10, -3), + "long-ton": 0.0009842073, + "short-ton": 0.0011023122, + "pound": 2.2046244202, + "ounce": 35.273990723, + "carrat": 5000, + "atomic-mass-unit": 6.022136652e26, +} + +WEIGHT_TYPE_CHART = { + "kilogram": 1, + "gram": pow(10, -3), + "milligram": pow(10, -6), + "metric-ton": pow(10, 3), + "long-ton": 1016.04608, + "short-ton": 907.184, + "pound": 0.453592, + "ounce": 0.0283495, + "carrat": 0.0002, + "atomic-mass-unit": 1.660540199e-27, +} + + +def weight_conversion(from_type: str, to_type: str, value: float) -> float: + """ + Conversion of weight unit with the help of KILOGRAM_CHART + + "kilogram" : 1, + "gram" : pow(10, 3), + "milligram" : pow(10, 6), + "metric-ton" : pow(10, -3), + "long-ton" : 0.0009842073, + "short-ton" : 0.0011023122, + "pound" : 2.2046244202, + "ounce" : 35.273990723, + "carrat" : 5000, + "atomic-mass-unit" : 6.022136652E+26 + + >>> weight_conversion("kilogram","kilogram",4) + 4 + >>> weight_conversion("kilogram","gram",1) + 1000 + >>> weight_conversion("kilogram","milligram",4) + 4000000 + >>> weight_conversion("kilogram","metric-ton",4) + 0.004 + >>> weight_conversion("kilogram","long-ton",3) + 0.0029526219 + >>> weight_conversion("kilogram","short-ton",1) + 0.0011023122 + >>> weight_conversion("kilogram","pound",4) + 8.8184976808 + >>> weight_conversion("kilogram","ounce",4) + 141.095962892 + >>> weight_conversion("kilogram","carrat",3) + 15000 + >>> weight_conversion("kilogram","atomic-mass-unit",1) + 6.022136652e+26 + >>> weight_conversion("gram","kilogram",1) + 0.001 + >>> weight_conversion("gram","gram",3) + 3.0 + >>> weight_conversion("gram","milligram",2) + 2000.0 + >>> weight_conversion("gram","metric-ton",4) + 4e-06 + >>> weight_conversion("gram","long-ton",3) + 2.9526219e-06 + >>> weight_conversion("gram","short-ton",3) + 3.3069366000000003e-06 + >>> weight_conversion("gram","pound",3) + 0.0066138732606 + >>> weight_conversion("gram","ounce",1) + 0.035273990723 + >>> weight_conversion("gram","carrat",2) + 10.0 + >>> weight_conversion("gram","atomic-mass-unit",1) + 6.022136652e+23 + >>> weight_conversion("milligram","kilogram",1) + 1e-06 + >>> weight_conversion("milligram","gram",2) + 0.002 + >>> weight_conversion("milligram","milligram",3) + 3.0 + >>> weight_conversion("milligram","metric-ton",3) + 3e-09 + >>> weight_conversion("milligram","long-ton",3) + 2.9526219e-09 + >>> weight_conversion("milligram","short-ton",1) + 1.1023122e-09 + >>> weight_conversion("milligram","pound",3) + 6.6138732605999995e-06 + >>> weight_conversion("milligram","ounce",2) + 7.054798144599999e-05 + >>> weight_conversion("milligram","carrat",1) + 0.005 + >>> weight_conversion("milligram","atomic-mass-unit",1) + 6.022136652e+20 + >>> weight_conversion("metric-ton","kilogram",2) + 2000 + >>> weight_conversion("metric-ton","gram",2) + 2000000 + >>> weight_conversion("metric-ton","milligram",3) + 3000000000 + >>> weight_conversion("metric-ton","metric-ton",2) + 2.0 + >>> weight_conversion("metric-ton","long-ton",3) + 2.9526219 + >>> weight_conversion("metric-ton","short-ton",2) + 2.2046244 + >>> weight_conversion("metric-ton","pound",3) + 6613.8732606 + >>> weight_conversion("metric-ton","ounce",4) + 141095.96289199998 + >>> weight_conversion("metric-ton","carrat",4) + 20000000 + >>> weight_conversion("metric-ton","atomic-mass-unit",1) + 6.022136652e+29 + >>> weight_conversion("long-ton","kilogram",4) + 4064.18432 + >>> weight_conversion("long-ton","gram",4) + 4064184.32 + >>> weight_conversion("long-ton","milligram",3) + 3048138240.0 + >>> weight_conversion("long-ton","metric-ton",4) + 4.06418432 + >>> weight_conversion("long-ton","long-ton",3) + 2.999999907217152 + >>> weight_conversion("long-ton","short-ton",1) + 1.119999989746176 + >>> weight_conversion("long-ton","pound",3) + 6720.000000049448 + >>> weight_conversion("long-ton","ounce",1) + 35840.000000060514 + >>> weight_conversion("long-ton","carrat",4) + 20320921.599999998 + >>> weight_conversion("long-ton","atomic-mass-unit",4) + 2.4475073353955697e+30 + >>> weight_conversion("short-ton","kilogram",3) + 2721.5519999999997 + >>> weight_conversion("short-ton","gram",3) + 2721552.0 + >>> weight_conversion("short-ton","milligram",1) + 907184000.0 + >>> weight_conversion("short-ton","metric-ton",4) + 3.628736 + >>> weight_conversion("short-ton","long-ton",3) + 2.6785713457296 + >>> weight_conversion("short-ton","short-ton",3) + 2.9999999725344 + >>> weight_conversion("short-ton","pound",2) + 4000.0000000294335 + >>> weight_conversion("short-ton","ounce",4) + 128000.00000021611 + >>> weight_conversion("short-ton","carrat",4) + 18143680.0 + >>> weight_conversion("short-ton","atomic-mass-unit",1) + 5.463186016507968e+29 + >>> weight_conversion("pound","kilogram",4) + 1.814368 + >>> weight_conversion("pound","gram",2) + 907.184 + >>> weight_conversion("pound","milligram",3) + 1360776.0 + >>> weight_conversion("pound","metric-ton",3) + 0.001360776 + >>> weight_conversion("pound","long-ton",2) + 0.0008928571152432 + >>> weight_conversion("pound","short-ton",1) + 0.0004999999954224 + >>> weight_conversion("pound","pound",3) + 3.0000000000220752 + >>> weight_conversion("pound","ounce",1) + 16.000000000027015 + >>> weight_conversion("pound","carrat",1) + 2267.96 + >>> weight_conversion("pound","atomic-mass-unit",4) + 1.0926372033015936e+27 + >>> weight_conversion("ounce","kilogram",3) + 0.0850485 + >>> weight_conversion("ounce","gram",3) + 85.0485 + >>> weight_conversion("ounce","milligram",4) + 113398.0 + >>> weight_conversion("ounce","metric-ton",4) + 0.000113398 + >>> weight_conversion("ounce","long-ton",4) + 0.0001116071394054 + >>> weight_conversion("ounce","short-ton",4) + 0.0001249999988556 + >>> weight_conversion("ounce","pound",1) + 0.0625000000004599 + >>> weight_conversion("ounce","ounce",2) + 2.000000000003377 + >>> weight_conversion("ounce","carrat",1) + 141.7475 + >>> weight_conversion("ounce","atomic-mass-unit",1) + 1.70724563015874e+25 + >>> weight_conversion("carrat","kilogram",1) + 0.0002 + >>> weight_conversion("carrat","gram",4) + 0.8 + >>> weight_conversion("carrat","milligram",2) + 400.0 + >>> weight_conversion("carrat","metric-ton",2) + 4.0000000000000003e-07 + >>> weight_conversion("carrat","long-ton",3) + 5.9052438e-07 + >>> weight_conversion("carrat","short-ton",4) + 8.818497600000002e-07 + >>> weight_conversion("carrat","pound",1) + 0.00044092488404000004 + >>> weight_conversion("carrat","ounce",2) + 0.0141095962892 + >>> weight_conversion("carrat","carrat",4) + 4.0 + >>> weight_conversion("carrat","atomic-mass-unit",4) + 4.8177093216e+23 + >>> weight_conversion("atomic-mass-unit","kilogram",4) + 6.642160796e-27 + >>> weight_conversion("atomic-mass-unit","gram",2) + 3.321080398e-24 + >>> weight_conversion("atomic-mass-unit","milligram",2) + 3.3210803980000002e-21 + >>> weight_conversion("atomic-mass-unit","metric-ton",3) + 4.9816205970000004e-30 + >>> weight_conversion("atomic-mass-unit","long-ton",3) + 4.9029473573977584e-30 + >>> weight_conversion("atomic-mass-unit","short-ton",1) + 1.830433719948128e-30 + >>> weight_conversion("atomic-mass-unit","pound",3) + 1.0982602420317504e-26 + >>> weight_conversion("atomic-mass-unit","ounce",2) + 1.1714775914938915e-25 + >>> weight_conversion("atomic-mass-unit","carrat",2) + 1.660540199e-23 + >>> weight_conversion("atomic-mass-unit","atomic-mass-unit",2) + 1.999999998903455 + """ + if to_type not in KILOGRAM_CHART or from_type not in WEIGHT_TYPE_CHART: + raise ValueError( + f"Invalid 'from_type' or 'to_type' value: {from_type!r}, {to_type!r}\n" + f"Supported values are: {', '.join(WEIGHT_TYPE_CHART)}" + ) + return value * KILOGRAM_CHART[to_type] * WEIGHT_TYPE_CHART[from_type] + + +if __name__ == "__main__": + + import doctest + + doctest.testmod() From b77be290851df9521c701f52aef0399acad85044 Mon Sep 17 00:00:00 2001 From: fpringle Date: Fri, 27 Nov 2020 17:08:14 +0100 Subject: [PATCH 106/195] Added solution for Project Euler problem 77 (#3132) * Added solution for Project Euler problem 77. * Update docstrings, doctest, type annotations and 0-padding in directory name. Reference: #3256 * Implemented lru_cache, better type hints, more doctests for problem 77 * updating DIRECTORY.md * updating DIRECTORY.md * Added solution for Project Euler problem 77. Fixes: 2695 * Update docstrings, doctest, type annotations and 0-padding in directory name. Reference: #3256 * Implemented lru_cache, better type hints, more doctests for problem 77 * better variable names Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_077/__init__.py | 0 project_euler/problem_077/sol1.py | 81 +++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 project_euler/problem_077/__init__.py create mode 100644 project_euler/problem_077/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index e1e57307d593..7fe7c63a2571 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -709,6 +709,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_075/sol1.py) * Problem 076 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_076/sol1.py) + * Problem 077 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_077/sol1.py) * Problem 080 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) * Problem 081 diff --git a/project_euler/problem_077/__init__.py b/project_euler/problem_077/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_077/sol1.py b/project_euler/problem_077/sol1.py new file mode 100644 index 000000000000..e92992a90ab3 --- /dev/null +++ b/project_euler/problem_077/sol1.py @@ -0,0 +1,81 @@ +""" +Project Euler Problem 77: https://projecteuler.net/problem=77 + +It is possible to write ten as the sum of primes in exactly five different ways: + +7 + 3 +5 + 5 +5 + 3 + 2 +3 + 3 + 2 + 2 +2 + 2 + 2 + 2 + 2 + +What is the first value which can be written as the sum of primes in over +five thousand different ways? +""" + +from functools import lru_cache +from math import ceil +from typing import Optional, Set + +NUM_PRIMES = 100 + +primes = set(range(3, NUM_PRIMES, 2)) +primes.add(2) +prime: int + +for prime in range(3, ceil(NUM_PRIMES ** 0.5), 2): + if prime not in primes: + continue + primes.difference_update(set(range(prime * prime, NUM_PRIMES, prime))) + + +@lru_cache(maxsize=100) +def partition(number_to_partition: int) -> Set[int]: + """ + Return a set of integers corresponding to unique prime partitions of n. + The unique prime partitions can be represented as unique prime decompositions, + e.g. (7+3) <-> 7*3 = 12, (3+3+2+2) = 3*3*2*2 = 36 + >>> partition(10) + {32, 36, 21, 25, 30} + >>> partition(15) + {192, 160, 105, 44, 112, 243, 180, 150, 216, 26, 125, 126} + >>> len(partition(20)) + 26 + """ + if number_to_partition < 0: + return set() + elif number_to_partition == 0: + return {1} + + ret: Set[int] = set() + prime: int + sub: int + + for prime in primes: + if prime > number_to_partition: + continue + for sub in partition(number_to_partition - prime): + ret.add(sub * prime) + + return ret + + +def solution(number_unique_partitions: int = 5000) -> Optional[int]: + """ + Return the smallest integer that can be written as the sum of primes in over + m unique ways. + >>> solution(4) + 10 + >>> solution(500) + 45 + >>> solution(1000) + 53 + """ + for number_to_partition in range(1, NUM_PRIMES): + if len(partition(number_to_partition)) > number_unique_partitions: + return number_to_partition + return None + + +if __name__ == "__main__": + print(f"{solution() = }") From 70451cd0dfde75e25a6a9742253487545482d7e9 Mon Sep 17 00:00:00 2001 From: arif599 <59244462+arif599@users.noreply.github.com> Date: Sat, 28 Nov 2020 05:50:18 +0000 Subject: [PATCH 107/195] data_structures/linked_list: Add __str__() function (#3961) * Adding __str__() function * Removing white space * Update data_structures/linked_list/__init__.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> * Adding type hints * Update __init__.py * Update __init__.py * Adding the changes requested * Updating to fix pre-commit * Updating __init__.py * Updating __init__.py Co-authored-by: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> --- data_structures/linked_list/__init__.py | 46 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/data_structures/linked_list/__init__.py b/data_structures/linked_list/__init__.py index 3ddfea5c5abf..a5f5537b1d96 100644 --- a/data_structures/linked_list/__init__.py +++ b/data_structures/linked_list/__init__.py @@ -1,19 +1,30 @@ +""" +Linked Lists consists of Nodes. +Nodes contain data and also may link to other nodes: + - Head Node: First node, the address of the + head node gives us access of the complete list + - Last node: points to null +""" + +from typing import Any + + class Node: - def __init__(self, item, next): + def __init__(self, item: Any, next: Any) -> None: self.item = item self.next = next class LinkedList: - def __init__(self): + def __init__(self) -> None: self.head = None self.size = 0 - def add(self, item): + def add(self, item: Any) -> None: self.head = Node(item, self.head) self.size += 1 - def remove(self): + def remove(self) -> Any: if self.is_empty(): return None else: @@ -22,10 +33,33 @@ def remove(self): self.size -= 1 return item - def is_empty(self): + def is_empty(self) -> bool: return self.head is None - def __len__(self): + def __str__(self) -> str: + """ + >>> linked_list = LinkedList() + >>> linked_list.add(23) + >>> linked_list.add(14) + >>> linked_list.add(9) + >>> print(linked_list) + 9 --> 14 --> 23 + """ + if not self.is_empty: + return "" + else: + iterate = self.head + item_str = "" + item_list = [] + while iterate: + item_list.append(str(iterate.item)) + iterate = iterate.next + + item_str = " --> ".join(item_list) + + return item_str + + def __len__(self) -> int: """ >>> linked_list = LinkedList() >>> len(linked_list) From 54882af2da0f9c077a81f76721bdccae689a0489 Mon Sep 17 00:00:00 2001 From: xcodz-dot <71920621+xcodz-dot@users.noreply.github.com> Date: Sat, 28 Nov 2020 19:09:27 +0530 Subject: [PATCH 108/195] Update CONTRIBUTING.md with pre-commit plugin instructions (#3979) * Update CONTRIBUTING.md * Update CONTRIBUTING.md * Update CONTRIBUTING.md * Update CONTRIBUTING.md Co-authored-by: Christian Clauss * Update CONTRIBUTING.md Co-authored-by: Christian Clauss * Update CONTRIBUTING.md Co-authored-by: Dhruv Manilawala Co-authored-by: Christian Clauss --- CONTRIBUTING.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eedcb0250169..e4c81a5ecd98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,19 @@ Algorithms should: Algorithms in this repo should not be how-to examples for existing Python packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing Python packages but each algorithm in this repo should add unique value. +#### Pre-commit plugin +Use [pre-commit](https://pre-commit.com/#installation) to automatically format your code to match our coding style: + +```bash +python3 -m pip install pre-commit # required only once +pre-commit install +``` +That's it! The plugin will run every time you commit any changes. If there are any errors found during the run, fix them and commit those changes. You can even run the plugin manually on all files: + +```bash +pre-commit run --all-files --show-diff-on-failure +``` + #### Coding Style We want your work to be readable by others; therefore, we encourage you to note the following: @@ -64,14 +77,14 @@ We want your work to be readable by others; therefore, we encourage you to note - Please consider running [__psf/black__](https://github.com/python/black) on your Python file(s) before submitting your pull request. This is not yet a requirement but it does make your code more readable and automatically aligns it with much of [PEP 8](https://www.python.org/dev/peps/pep-0008/). There are other code formatters (autopep8, yapf) but the __black__ formatter is now hosted by the Python Software Foundation. To use it, ```bash - pip3 install black # only required the first time + python3 -m pip install black # only required the first time black . ``` - All submissions will need to pass the test __flake8 . --ignore=E203,W503 --max-line-length=88__ before they will be accepted so if possible, try this test locally on your Python file(s) before submitting your pull request. ```bash - pip3 install flake8 # only required the first time + python3 -m pip install flake8 # only required the first time flake8 . --ignore=E203,W503 --max-line-length=88 --show-source ``` From c14d5e31c5ea6717cc78aa75dd8b15b5836bcace Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Sat, 28 Nov 2020 22:42:30 +0800 Subject: [PATCH 109/195] Fixed LGTM and typehint (#3970) * fixed LGTM fixed typehint * updating DIRECTORY.md * Update lucas_series.py * Update lucas_series.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: John Law --- DIRECTORY.md | 12 +++++++++++- maths/lucas_series.py | 27 ++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 7fe7c63a2571..079dae1884bc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -26,6 +26,8 @@ ## Bit Manipulation * [Binary And Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_and_operator.py) + * [Binary Count Setbits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_count_setbits.py) + * [Binary Count Trailing Zeros](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_count_trailing_zeros.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) * [Single Bit Manipulation Operations](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/single_bit_manipulation_operations.py) @@ -47,7 +49,7 @@ * [Atbash](https://github.com/TheAlgorithms/Python/blob/master/ciphers/atbash.py) * [Base16](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base16.py) * [Base32](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base32.py) - * [Base64 Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_cipher.py) + * [Base64 Encoding](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_encoding.py) * [Base85](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base85.py) * [Beaufort Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/beaufort_cipher.py) * [Brute Force Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/brute_force_caesar_cipher.py) @@ -101,6 +103,7 @@ * [Decimal To Octal](https://github.com/TheAlgorithms/Python/blob/master/conversions/decimal_to_octal.py) * [Hexadecimal To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/hexadecimal_to_decimal.py) * [Molecular Chemistry](https://github.com/TheAlgorithms/Python/blob/master/conversions/molecular_chemistry.py) + * [Octal To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/octal_to_decimal.py) * [Prefix Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/prefix_conversions.py) * [Roman To Integer](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_to_integer.py) * [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py) @@ -207,6 +210,7 @@ * [Heaps Algorithm Iterative](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/heaps_algorithm_iterative.py) * [Inversions](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/inversions.py) * [Kth Order Statistic](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/kth_order_statistic.py) + * [Max Difference Pair](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/max_difference_pair.py) * [Max Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/max_subarray_sum.py) * [Mergesort](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/mergesort.py) * [Peak](https://github.com/TheAlgorithms/Python/blob/master/divide_and_conquer/peak.py) @@ -243,6 +247,9 @@ * [Subset Generation](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/subset_generation.py) * [Sum Of Subset](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/sum_of_subset.py) +## Electronics + * [Ohms Law](https://github.com/TheAlgorithms/Python/blob/master/electronics/ohms_law.py) + ## File Transfer * [Receive File](https://github.com/TheAlgorithms/Python/blob/master/file_transfer/receive_file.py) * [Send File](https://github.com/TheAlgorithms/Python/blob/master/file_transfer/send_file.py) @@ -804,6 +811,7 @@ * [Gnome Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/gnome_sort.py) * [Heap Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/heap_sort.py) * [Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/insertion_sort.py) + * [Intro Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/intro_sort.py) * [Iterative Merge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/iterative_merge_sort.py) * [Merge Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_insertion_sort.py) * [Merge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_sort.py) @@ -877,6 +885,8 @@ * [Get Imdb Top 250 Movies Csv](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_imdb_top_250_movies_csv.py) * [Get Imdbtop](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_imdbtop.py) * [Instagram Crawler](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_crawler.py) + * [Instagram Pic](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_pic.py) * [Recaptcha Verification](https://github.com/TheAlgorithms/Python/blob/master/web_programming/recaptcha_verification.py) * [Slack Message](https://github.com/TheAlgorithms/Python/blob/master/web_programming/slack_message.py) + * [Test Fetch Github Info](https://github.com/TheAlgorithms/Python/blob/master/web_programming/test_fetch_github_info.py) * [World Covid19 Stats](https://github.com/TheAlgorithms/Python/blob/master/web_programming/world_covid19_stats.py) diff --git a/maths/lucas_series.py b/maths/lucas_series.py index 02eae8d8c658..6b32c2022e13 100644 --- a/maths/lucas_series.py +++ b/maths/lucas_series.py @@ -3,7 +3,7 @@ """ -def recursive_lucas_number(n): +def recursive_lucas_number(n_th_number: int) -> int: """ Returns the nth lucas number >>> recursive_lucas_number(1) @@ -19,17 +19,19 @@ def recursive_lucas_number(n): ... TypeError: recursive_lucas_number accepts only integer arguments. """ - if n == 1: - return n - if n == 0: - return 2 - if not isinstance(n, int): + if not isinstance(n_th_number, int): raise TypeError("recursive_lucas_number accepts only integer arguments.") + if n_th_number == 0: + return 2 + if n_th_number == 1: + return 1 - return recursive_lucas_number(n - 1) + recursive_lucas_number(n - 2) + return recursive_lucas_number(n_th_number - 1) + recursive_lucas_number( + n_th_number - 2 + ) -def dynamic_lucas_number(n: int) -> int: +def dynamic_lucas_number(n_th_number: int) -> int: """ Returns the nth lucas number >>> dynamic_lucas_number(1) @@ -45,14 +47,10 @@ def dynamic_lucas_number(n: int) -> int: ... TypeError: dynamic_lucas_number accepts only integer arguments. """ - if not isinstance(n, int): + if not isinstance(n_th_number, int): raise TypeError("dynamic_lucas_number accepts only integer arguments.") - if n == 0: - return 2 - if n == 1: - return 1 a, b = 2, 1 - for i in range(n): + for i in range(n_th_number): a, b = b, a + b return a @@ -62,7 +60,6 @@ def dynamic_lucas_number(n: int) -> int: testmod() n = int(input("Enter the number of terms in lucas series:\n").strip()) - n = int(input("Enter the number of terms in lucas series:\n").strip()) print("Using recursive function to calculate lucas series:") print(" ".join(str(recursive_lucas_number(i)) for i in range(n))) print("\nUsing dynamic function to calculate lucas series:") From af9b201f494caed0d3968a7676cbe2bdbaa59724 Mon Sep 17 00:00:00 2001 From: Sullivan <38718448+Epic-R-R@users.noreply.github.com> Date: Sun, 29 Nov 2020 15:44:18 +0330 Subject: [PATCH 110/195] Instagram Video and IGTV downloader (#3981) * Instagram Video and IGTV downloader Download Video and IGTV from Instagram * Update * Update * Some Change * Update * Update instagram_video.py * Update instagram_video.py * Update instagram_video.py * Update instagram_video.py Co-authored-by: Christian Clauss Co-authored-by: Dhruv Manilawala --- web_programming/instagram_video.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 web_programming/instagram_video.py diff --git a/web_programming/instagram_video.py b/web_programming/instagram_video.py new file mode 100644 index 000000000000..243cece1a50e --- /dev/null +++ b/web_programming/instagram_video.py @@ -0,0 +1,17 @@ +from datetime import datetime + +import requests + + +def download_video(url: str) -> bytes: + base_url = "https://downloadgram.net/wp-json/wppress/video-downloader/video?url=" + video_url = requests.get(base_url + url).json()[0]["urls"][0]["src"] + return requests.get(video_url).content + + +if __name__ == "__main__": + url = input("Enter Video/IGTV url: ").strip() + file_name = f"{datetime.now():%Y-%m-%d_%H:%M:%S}.mp4" + with open(file_name, "wb") as fp: + fp.write(download_video(url)) + print(f"Done. Video saved to disk as {file_name}.") From 5aff89b9b4b119cafbff828accbbc8170c9528b5 Mon Sep 17 00:00:00 2001 From: jenra Date: Sun, 29 Nov 2020 11:09:33 -0500 Subject: [PATCH 111/195] Hacktoberfest 2020: Conway's Game of Life (#3070) * Created conways_game_of_life.py * Added new_generation(list[int[int]]) -> list[list[int]] * Added glider example * Added comments and shortened glider example * Fixed index out of bounds error * Added test * Added blinker example * Added ability to generate images * Moved image generating code into a separate function * Added comments * Comment * Reformatted file * Formatting * Removed glider test * Update cellular_automata/conways_game_of_life.py Co-authored-by: John Law * Update conways_game_of_life.py * Update conways_game_of_life.py Co-authored-by: John Law --- cellular_automata/conways_game_of_life.py | 100 ++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 cellular_automata/conways_game_of_life.py diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py new file mode 100644 index 000000000000..321baa3a3794 --- /dev/null +++ b/cellular_automata/conways_game_of_life.py @@ -0,0 +1,100 @@ +""" +Conway's Game of Life implemented in Python. +https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life +""" + +from __future__ import annotations + +from typing import List + +from PIL import Image + +# Define glider example +GLIDER = [ + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0], +] + +# Define blinker example +BLINKER = [[0, 1, 0], [0, 1, 0], [0, 1, 0]] + + +def new_generation(cells: List[List[int]]) -> List[List[int]]: + """ + Generates the next generation for a given state of Conway's Game of Life. + >>> new_generation(BLINKER) + [[0, 0, 0], [1, 1, 1], [0, 0, 0]] + """ + next_generation = [] + for i in range(len(cells)): + next_generation_row = [] + for j in range(len(cells[i])): + # Get the number of live neighbours + neighbour_count = 0 + if i > 0 and j > 0: + neighbour_count += cells[i - 1][j - 1] + if i > 0: + neighbour_count += cells[i - 1][j] + if i > 0 and j < len(cells[i]) - 1: + neighbour_count += cells[i - 1][j + 1] + if j > 0: + neighbour_count += cells[i][j - 1] + if j < len(cells[i]) - 1: + neighbour_count += cells[i][j + 1] + if i < len(cells) - 1 and j > 0: + neighbour_count += cells[i + 1][j - 1] + if i < len(cells) - 1: + neighbour_count += cells[i + 1][j] + if i < len(cells) - 1 and j < len(cells[i]) - 1: + neighbour_count += cells[i + 1][j + 1] + + # Rules of the game of life (excerpt from Wikipedia): + # 1. Any live cell with two or three live neighbours survives. + # 2. Any dead cell with three live neighbours becomes a live cell. + # 3. All other live cells die in the next generation. + # Similarly, all other dead cells stay dead. + alive = cells[i][j] == 1 + if ( + (alive and 2 <= neighbour_count <= 3) + or not alive + and neighbour_count == 3 + ): + next_generation_row.append(1) + else: + next_generation_row.append(0) + + next_generation.append(next_generation_row) + return next_generation + + +def generate_images(cells: list[list[int]], frames) -> list[Image.Image]: + """ + Generates a list of images of subsequent Game of Life states. + """ + images = [] + for _ in range(frames): + # Create output image + img = Image.new("RGB", (len(cells[0]), len(cells))) + pixels = img.load() + + # Save cells to image + for x in range(len(cells)): + for y in range(len(cells[0])): + colour = 255 - cells[y][x] * 255 + pixels[x, y] = (colour, colour, colour) + + # Save image + images.append(img) + cells = new_generation(cells) + return images + + +if __name__ == "__main__": + images = generate_images(GLIDER, 16) + images[0].save("out.gif", save_all=True, append_images=images[1:]) From 3fa9e199e4472df906053d0bf05a30844585eebb Mon Sep 17 00:00:00 2001 From: Jenia Dysin Date: Sun, 29 Nov 2020 18:19:50 +0200 Subject: [PATCH 112/195] Add static typing to backtracking algorithms (#2684) * Added static typing to backtracking algorithms * Ran psf/black to fix some minor issues. * updating DIRECTORY.md * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: John Law --- DIRECTORY.md | 13 ++++++++----- backtracking/all_combinations.py | 10 ++++++++-- backtracking/all_permutations.py | 6 ++++-- backtracking/n_queens.py | 6 +++--- backtracking/rat_in_maze.py | 4 ++-- backtracking/sum_of_subsets.py | 11 +++++++++-- 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 079dae1884bc..00da7922d54d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -41,6 +41,7 @@ * [Quine Mc Cluskey](https://github.com/TheAlgorithms/Python/blob/master/boolean_algebra/quine_mc_cluskey.py) ## Cellular Automata + * [Conways Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/conways_game_of_life.py) * [One Dimensional](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/one_dimensional.py) ## Ciphers @@ -107,6 +108,7 @@ * [Prefix Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/prefix_conversions.py) * [Roman To Integer](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_to_integer.py) * [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py) + * [Weight Conversion](https://github.com/TheAlgorithms/Python/blob/master/conversions/weight_conversion.py) ## Data Structures * Binary Tree @@ -321,10 +323,6 @@ * [Test Min Spanning Tree Kruskal](https://github.com/TheAlgorithms/Python/blob/master/graphs/tests/test_min_spanning_tree_kruskal.py) * [Test Min Spanning Tree Prim](https://github.com/TheAlgorithms/Python/blob/master/graphs/tests/test_min_spanning_tree_prim.py) -## Greedy Method - * [Greedy Knapsack](https://github.com/TheAlgorithms/Python/blob/master/greedy_method/greedy_knapsack.py) - * [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/greedy_method/test_knapsack.py) - ## Hashes * [Adler32](https://github.com/TheAlgorithms/Python/blob/master/hashes/adler32.py) * [Chaos Machine](https://github.com/TheAlgorithms/Python/blob/master/hashes/chaos_machine.py) @@ -336,8 +334,11 @@ * [Sha1](https://github.com/TheAlgorithms/Python/blob/master/hashes/sha1.py) ## Knapsack + * [Greedy Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/greedy_knapsack.py) * [Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/knapsack.py) - * [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/test_knapsack.py) + * Tests + * [Test Greedy Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/tests/test_greedy_knapsack.py) + * [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/tests/test_knapsack.py) ## Linear Algebra * Src @@ -400,6 +401,7 @@ * [Combinations](https://github.com/TheAlgorithms/Python/blob/master/maths/combinations.py) * [Decimal Isolate](https://github.com/TheAlgorithms/Python/blob/master/maths/decimal_isolate.py) * [Entropy](https://github.com/TheAlgorithms/Python/blob/master/maths/entropy.py) + * [Euclidean Distance](https://github.com/TheAlgorithms/Python/blob/master/maths/euclidean_distance.py) * [Eulers Totient](https://github.com/TheAlgorithms/Python/blob/master/maths/eulers_totient.py) * [Explicit Euler](https://github.com/TheAlgorithms/Python/blob/master/maths/explicit_euler.py) * [Extended Euclidean Algorithm](https://github.com/TheAlgorithms/Python/blob/master/maths/extended_euclidean_algorithm.py) @@ -886,6 +888,7 @@ * [Get Imdbtop](https://github.com/TheAlgorithms/Python/blob/master/web_programming/get_imdbtop.py) * [Instagram Crawler](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_crawler.py) * [Instagram Pic](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_pic.py) + * [Instagram Video](https://github.com/TheAlgorithms/Python/blob/master/web_programming/instagram_video.py) * [Recaptcha Verification](https://github.com/TheAlgorithms/Python/blob/master/web_programming/recaptcha_verification.py) * [Slack Message](https://github.com/TheAlgorithms/Python/blob/master/web_programming/slack_message.py) * [Test Fetch Github Info](https://github.com/TheAlgorithms/Python/blob/master/web_programming/test_fetch_github_info.py) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index 854dc5198422..0444ed093449 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -16,7 +16,13 @@ def generate_all_combinations(n: int, k: int) -> [[int]]: return result -def create_all_state(increment, total_number, level, current_list, total_list): +def create_all_state( + increment: int, + total_number: int, + level: int, + current_list: [int], + total_list: [int], +) -> None: if level == 0: total_list.append(current_list[:]) return @@ -27,7 +33,7 @@ def create_all_state(increment, total_number, level, current_list, total_list): current_list.pop() -def print_all_state(total_list): +def print_all_state(total_list: [int]) -> None: for i in total_list: print(*i) diff --git a/backtracking/all_permutations.py b/backtracking/all_permutations.py index 5244fef97f93..59c7b7bbf41e 100644 --- a/backtracking/all_permutations.py +++ b/backtracking/all_permutations.py @@ -7,11 +7,13 @@ """ -def generate_all_permutations(sequence): +def generate_all_permutations(sequence: [int]) -> None: create_state_space_tree(sequence, [], 0, [0 for i in range(len(sequence))]) -def create_state_space_tree(sequence, current_sequence, index, index_used): +def create_state_space_tree( + sequence: [int], current_sequence: [int], index: int, index_used: int +) -> None: """ Creates a state space tree to iterate through each branch using DFS. We know that each state has exactly len(sequence) - index children. diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index ca7beb830bba..31696b4a84d3 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -10,7 +10,7 @@ solution = [] -def isSafe(board, row, column): +def isSafe(board: [[int]], row: int, column: int) -> bool: """ This function returns a boolean value True if it is safe to place a queen there considering the current state of the board. @@ -38,7 +38,7 @@ def isSafe(board, row, column): return True -def solve(board, row): +def solve(board: [[int]], row: int) -> bool: """ It creates a state space tree and calls the safe function until it receives a False Boolean and terminates that branch and backtracks to the next @@ -68,7 +68,7 @@ def solve(board, row): return False -def printboard(board): +def printboard(board: [[int]]) -> None: """ Prints the boards that have a successful combination. """ diff --git a/backtracking/rat_in_maze.py b/backtracking/rat_in_maze.py index 788aeac13c09..8dc484c3f92d 100644 --- a/backtracking/rat_in_maze.py +++ b/backtracking/rat_in_maze.py @@ -1,4 +1,4 @@ -def solve_maze(maze: list) -> bool: +def solve_maze(maze: [[int]]) -> bool: """ This method solves the "rat in maze" problem. In this problem we have some n by n matrix, a start point and an end point. @@ -67,7 +67,7 @@ def solve_maze(maze: list) -> bool: return solved -def run_maze(maze, i, j, solutions): +def run_maze(maze: [[int]], i: int, j: int, solutions: [[int]]) -> bool: """ This method is recursive starting from (i, j) and going in one of four directions: up, down, left, right. diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index 425ddcff927e..b71edc2eefb5 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -8,7 +8,7 @@ """ -def generate_sum_of_subsets_soln(nums, max_sum): +def generate_sum_of_subsets_soln(nums: [int], max_sum: [int]) -> [int]: result = [] path = [] num_index = 0 @@ -17,7 +17,14 @@ def generate_sum_of_subsets_soln(nums, max_sum): return result -def create_state_space_tree(nums, max_sum, num_index, path, result, remaining_nums_sum): +def create_state_space_tree( + nums: [int], + max_sum: int, + num_index: int, + path: [int], + result: [int], + remaining_nums_sum: int, +) -> None: """ Creates a state space tree to iterate through each branch using DFS. It terminates the branching of a node when any of the two conditions From ff579b2c2f22a90f443eb41fa65e2a1d8ffc962c Mon Sep 17 00:00:00 2001 From: Jenia Dysin Date: Sun, 29 Nov 2020 18:20:54 +0200 Subject: [PATCH 113/195] Add typehints to blockchain (#3149) * updating DIRECTORY.md * updating DIRECTORY.md * Added type hints to blockchain algorithms * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- blockchain/chinese_remainder_theorem.py | 8 ++++---- blockchain/diophantine_equation.py | 8 ++++---- blockchain/modular_division.py | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/blockchain/chinese_remainder_theorem.py b/blockchain/chinese_remainder_theorem.py index b6a486f0b1ed..3e4b2b7b4f10 100644 --- a/blockchain/chinese_remainder_theorem.py +++ b/blockchain/chinese_remainder_theorem.py @@ -12,7 +12,7 @@ # Extended Euclid -def extended_euclid(a, b): +def extended_euclid(a: int, b: int) -> (int, int): """ >>> extended_euclid(10, 6) (-1, 2) @@ -29,7 +29,7 @@ def extended_euclid(a, b): # Uses ExtendedEuclid to find inverses -def chinese_remainder_theorem(n1, r1, n2, r2): +def chinese_remainder_theorem(n1: int, r1: int, n2: int, r2: int) -> int: """ >>> chinese_remainder_theorem(5,1,7,3) 31 @@ -51,7 +51,7 @@ def chinese_remainder_theorem(n1, r1, n2, r2): # ----------SAME SOLUTION USING InvertModulo instead ExtendedEuclid---------------- # This function find the inverses of a i.e., a^(-1) -def invert_modulo(a, n): +def invert_modulo(a: int, n: int) -> int: """ >>> invert_modulo(2, 5) 3 @@ -67,7 +67,7 @@ def invert_modulo(a, n): # Same a above using InvertingModulo -def chinese_remainder_theorem2(n1, r1, n2, r2): +def chinese_remainder_theorem2(n1: int, r1: int, n2: int, r2: int) -> int: """ >>> chinese_remainder_theorem2(5,1,7,3) 31 diff --git a/blockchain/diophantine_equation.py b/blockchain/diophantine_equation.py index 751b0efb7227..a92c2a13cfd5 100644 --- a/blockchain/diophantine_equation.py +++ b/blockchain/diophantine_equation.py @@ -5,7 +5,7 @@ # GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) -def diophantine(a, b, c): +def diophantine(a: int, b: int, c: int) -> (int, int): """ >>> diophantine(10,6,14) (-7.0, 14.0) @@ -37,7 +37,7 @@ def diophantine(a, b, c): # n is the number of solution you want, n = 2 by default -def diophantine_all_soln(a, b, c, n=2): +def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: """ >>> diophantine_all_soln(10, 6, 14) -7.0 14.0 @@ -72,7 +72,7 @@ def diophantine_all_soln(a, b, c, n=2): # Euclid's Algorithm -def greatest_common_divisor(a, b): +def greatest_common_divisor(a: int, b: int) -> int: """ >>> greatest_common_divisor(7,5) 1 @@ -98,7 +98,7 @@ def greatest_common_divisor(a, b): # x and y, then d = gcd(a,b) -def extended_gcd(a, b): +def extended_gcd(a: int, b: int) -> (int, int, int): """ >>> extended_gcd(10, 6) (2, -1, 2) diff --git a/blockchain/modular_division.py b/blockchain/modular_division.py index 8fcf6e37cbed..e012db28fab8 100644 --- a/blockchain/modular_division.py +++ b/blockchain/modular_division.py @@ -14,7 +14,7 @@ # Uses ExtendedEuclid to find the inverse of a -def modular_division(a, b, n): +def modular_division(a: int, b: int, n: int) -> int: """ >>> modular_division(4,8,5) 2 @@ -33,7 +33,7 @@ def modular_division(a, b, n): # This function find the inverses of a i.e., a^(-1) -def invert_modulo(a, n): +def invert_modulo(a: int, n: int) -> int: """ >>> invert_modulo(2, 5) 3 @@ -51,7 +51,7 @@ def invert_modulo(a, n): # ------------------ Finding Modular division using invert_modulo ------------------- # This function used the above inversion of a to find x = (b*a^(-1))mod n -def modular_division2(a, b, n): +def modular_division2(a: int, b: int, n: int) -> int: """ >>> modular_division2(4,8,5) 2 @@ -72,7 +72,7 @@ def modular_division2(a, b, n): # and y, then d = gcd(a,b) -def extended_gcd(a, b): +def extended_gcd(a: int, b: int) -> (int, int, int): """ >>> extended_gcd(10, 6) (2, -1, 2) @@ -99,7 +99,7 @@ def extended_gcd(a, b): # Extended Euclid -def extended_euclid(a, b): +def extended_euclid(a: int, b: int) -> (int, int): """ >>> extended_euclid(10, 6) (-1, 2) @@ -119,7 +119,7 @@ def extended_euclid(a, b): # Euclid's Algorithm -def greatest_common_divisor(a, b): +def greatest_common_divisor(a: int, b: int) -> int: """ >>> greatest_common_divisor(7,5) 1 From 6afee53b89e8325b9a60823d4ad93558a0055b8b Mon Sep 17 00:00:00 2001 From: John Law Date: Mon, 30 Nov 2020 01:30:31 +0800 Subject: [PATCH 114/195] Fix mypy in #2684 (#3987) * Fix mypy in #2684 * fix pre-commit --- backtracking/all_combinations.py | 11 ++++++----- backtracking/all_permutations.py | 14 +++++++++----- backtracking/n_queens.py | 10 ++++++---- backtracking/rat_in_maze.py | 8 ++++++-- backtracking/sum_of_subsets.py | 13 +++++++------ 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/backtracking/all_combinations.py b/backtracking/all_combinations.py index 0444ed093449..76462837ce35 100644 --- a/backtracking/all_combinations.py +++ b/backtracking/all_combinations.py @@ -3,15 +3,16 @@ numbers out of 1 ... n. We use backtracking to solve this problem. Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!))) """ +from typing import List -def generate_all_combinations(n: int, k: int) -> [[int]]: +def generate_all_combinations(n: int, k: int) -> List[List[int]]: """ >>> generate_all_combinations(n=4, k=2) [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] """ - result = [] + result: List[List[int]] = [] create_all_state(1, n, k, [], result) return result @@ -20,8 +21,8 @@ def create_all_state( increment: int, total_number: int, level: int, - current_list: [int], - total_list: [int], + current_list: List[int], + total_list: List[List[int]], ) -> None: if level == 0: total_list.append(current_list[:]) @@ -33,7 +34,7 @@ def create_all_state( current_list.pop() -def print_all_state(total_list: [int]) -> None: +def print_all_state(total_list: List[List[int]]) -> None: for i in total_list: print(*i) diff --git a/backtracking/all_permutations.py b/backtracking/all_permutations.py index 59c7b7bbf41e..a0032c5ca814 100644 --- a/backtracking/all_permutations.py +++ b/backtracking/all_permutations.py @@ -5,14 +5,18 @@ Time complexity: O(n! * n), where n denotes the length of the given sequence. """ +from typing import List, Union -def generate_all_permutations(sequence: [int]) -> None: +def generate_all_permutations(sequence: List[Union[int, str]]) -> None: create_state_space_tree(sequence, [], 0, [0 for i in range(len(sequence))]) def create_state_space_tree( - sequence: [int], current_sequence: [int], index: int, index_used: int + sequence: List[Union[int, str]], + current_sequence: List[Union[int, str]], + index: int, + index_used: List[int], ) -> None: """ Creates a state space tree to iterate through each branch using DFS. @@ -40,8 +44,8 @@ def create_state_space_tree( sequence = list(map(int, input().split())) """ -sequence = [3, 1, 2, 4] +sequence: List[Union[int, str]] = [3, 1, 2, 4] generate_all_permutations(sequence) -sequence = ["A", "B", "C"] -generate_all_permutations(sequence) +sequence_2: List[Union[int, str]] = ["A", "B", "C"] +generate_all_permutations(sequence_2) diff --git a/backtracking/n_queens.py b/backtracking/n_queens.py index 31696b4a84d3..29b8d819acf3 100644 --- a/backtracking/n_queens.py +++ b/backtracking/n_queens.py @@ -7,10 +7,12 @@ diagonal lines. """ +from typing import List + solution = [] -def isSafe(board: [[int]], row: int, column: int) -> bool: +def isSafe(board: List[List[int]], row: int, column: int) -> bool: """ This function returns a boolean value True if it is safe to place a queen there considering the current state of the board. @@ -38,7 +40,7 @@ def isSafe(board: [[int]], row: int, column: int) -> bool: return True -def solve(board: [[int]], row: int) -> bool: +def solve(board: List[List[int]], row: int) -> bool: """ It creates a state space tree and calls the safe function until it receives a False Boolean and terminates that branch and backtracks to the next @@ -53,7 +55,7 @@ def solve(board: [[int]], row: int) -> bool: solution.append(board) printboard(board) print() - return + return True for i in range(len(board)): """ For every row it iterates through each column to check if it is feasible to @@ -68,7 +70,7 @@ def solve(board: [[int]], row: int) -> bool: return False -def printboard(board: [[int]]) -> None: +def printboard(board: List[List[int]]) -> None: """ Prints the boards that have a successful combination. """ diff --git a/backtracking/rat_in_maze.py b/backtracking/rat_in_maze.py index 8dc484c3f92d..cd2a8f41daa8 100644 --- a/backtracking/rat_in_maze.py +++ b/backtracking/rat_in_maze.py @@ -1,4 +1,7 @@ -def solve_maze(maze: [[int]]) -> bool: +from typing import List + + +def solve_maze(maze: List[List[int]]) -> bool: """ This method solves the "rat in maze" problem. In this problem we have some n by n matrix, a start point and an end point. @@ -67,7 +70,7 @@ def solve_maze(maze: [[int]]) -> bool: return solved -def run_maze(maze: [[int]], i: int, j: int, solutions: [[int]]) -> bool: +def run_maze(maze: List[List[int]], i: int, j: int, solutions: List[List[int]]) -> bool: """ This method is recursive starting from (i, j) and going in one of four directions: up, down, left, right. @@ -106,6 +109,7 @@ def run_maze(maze: [[int]], i: int, j: int, solutions: [[int]]) -> bool: solutions[i][j] = 0 return False + return False if __name__ == "__main__": diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index b71edc2eefb5..f695b8f7a80e 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -6,11 +6,12 @@ Summation of the chosen numbers must be equal to given number M and one number can be used only once. """ +from typing import List -def generate_sum_of_subsets_soln(nums: [int], max_sum: [int]) -> [int]: - result = [] - path = [] +def generate_sum_of_subsets_soln(nums: List[int], max_sum: int) -> List[List[int]]: + result: List[List[int]] = [] + path: List[int] = [] num_index = 0 remaining_nums_sum = sum(nums) create_state_space_tree(nums, max_sum, num_index, path, result, remaining_nums_sum) @@ -18,11 +19,11 @@ def generate_sum_of_subsets_soln(nums: [int], max_sum: [int]) -> [int]: def create_state_space_tree( - nums: [int], + nums: List[int], max_sum: int, num_index: int, - path: [int], - result: [int], + path: List[int], + result: List[List[int]], remaining_nums_sum: int, ) -> None: """ From 014bb7d03a67bac34f97ebef10f4c874df017057 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sun, 29 Nov 2020 23:11:09 +0530 Subject: [PATCH 115/195] Validate only submitted Project Euler solution (#3977) * Update validate solution script to fetch only submitted solution * Update workflow file with the updated PE script * Fix: do not fetch `validate_solutions.py` script * Update script to use the requests package for API calls * Fix: install requests module * Pytest ignore scripts/ directory --- .github/workflows/build.yml | 2 +- .github/workflows/project_euler.yml | 18 +++++++----- scripts/validate_solutions.py | 45 +++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae9b4e36b1ce..9e15d18ade8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,6 @@ jobs: python -m pip install --upgrade pip setuptools six wheel python -m pip install pytest-cov -r requirements.txt - name: Run tests - run: pytest --doctest-modules --ignore=project_euler/ --cov-report=term-missing:skip-covered --cov=. . + run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml index e8b011af20a6..995295fcaa9a 100644 --- a/.github/workflows/project_euler.yml +++ b/.github/workflows/project_euler.yml @@ -1,12 +1,14 @@ on: pull_request: - # only check if a file is changed within the project_euler directory and related files + # Run only if a file is changed within the project_euler directory and related files paths: - - 'project_euler/**' - - '.github/workflows/project_euler.yml' - - 'scripts/validate_solutions.py' + - "project_euler/**" + - ".github/workflows/project_euler.yml" + - "scripts/validate_solutions.py" + schedule: + - cron: "0 0 * * *" # Run everyday -name: 'Project Euler' +name: "Project Euler" jobs: project-euler: @@ -24,8 +26,10 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - name: Install pytest + - name: Install pytest and requests run: | python -m pip install --upgrade pip - python -m pip install --upgrade pytest + python -m pip install --upgrade pytest requests - run: pytest scripts/validate_solutions.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py index e1f68ff843bb..fd804ea5aa31 100755 --- a/scripts/validate_solutions.py +++ b/scripts/validate_solutions.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 import importlib.util import json +import os import pathlib from types import ModuleType from typing import Dict, List import pytest +import requests PROJECT_EULER_DIR_PATH = pathlib.Path.cwd().joinpath("project_euler") PROJECT_EULER_ANSWERS_PATH = pathlib.Path.cwd().joinpath( @@ -24,7 +26,7 @@ def convert_path_to_module(file_path: pathlib.Path) -> ModuleType: return module -def collect_solution_file_paths() -> List[pathlib.Path]: +def all_solution_file_paths() -> List[pathlib.Path]: """Collects all the solution file path in the Project Euler directory""" solution_file_paths = [] for problem_dir_path in PROJECT_EULER_DIR_PATH.iterdir(): @@ -37,12 +39,51 @@ def collect_solution_file_paths() -> List[pathlib.Path]: return solution_file_paths +def get_files_url() -> str: + """Return the pull request number which triggered this action.""" + with open(os.environ["GITHUB_EVENT_PATH"]) as file: + event = json.load(file) + return event["pull_request"]["url"] + "/files" + + +def added_solution_file_path() -> List[pathlib.Path]: + """Collects only the solution file path which got added in the current + pull request. + + This will only be triggered if the script is ran from GitHub Actions. + """ + solution_file_paths = [] + headers = { + "Accept": "application/vnd.github.v3+json", + "Authorization": "token " + os.environ["GITHUB_TOKEN"], + } + files = requests.get(get_files_url(), headers=headers).json() + for file in files: + filepath = pathlib.Path.cwd().joinpath(file["filename"]) + if ( + filepath.suffix != ".py" + or filepath.name.startswith(("_", "test")) + or not filepath.name.startswith("sol") + ): + continue + solution_file_paths.append(filepath) + return solution_file_paths + + +def collect_solution_file_paths() -> List[pathlib.Path]: + if os.environ.get("CI") and os.environ.get("GITHUB_EVENT_NAME") == "pull_request": + # Return only if there are any, otherwise default to all solutions + if filepaths := added_solution_file_path(): + return filepaths + return all_solution_file_paths() + + @pytest.mark.parametrize( "solution_path", collect_solution_file_paths(), ids=lambda path: f"{path.parent.name}/{path.name}", ) -def test_project_euler(solution_path: pathlib.Path): +def test_project_euler(solution_path: pathlib.Path) -> None: """Testing for all Project Euler solutions""" # problem_[extract this part] and pad it with zeroes for width 3 problem_number: str = solution_path.parent.name[8:].zfill(3) From 4a0dd03ee4ccdd8d3b9016c672a875f063910045 Mon Sep 17 00:00:00 2001 From: John Law Date: Mon, 30 Nov 2020 01:46:26 +0800 Subject: [PATCH 116/195] Fix mypy in #3149 (#3988) * Fix mypy in #3149 * Fix pre-commit --- blockchain/chinese_remainder_theorem.py | 21 +++++---- blockchain/diophantine_equation.py | 50 ++++++++++----------- blockchain/modular_division.py | 58 +++++++++++++------------ 3 files changed, 67 insertions(+), 62 deletions(-) diff --git a/blockchain/chinese_remainder_theorem.py b/blockchain/chinese_remainder_theorem.py index 3e4b2b7b4f10..b50147ac1215 100644 --- a/blockchain/chinese_remainder_theorem.py +++ b/blockchain/chinese_remainder_theorem.py @@ -1,18 +1,21 @@ -# Chinese Remainder Theorem: -# GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) +""" +Chinese Remainder Theorem: +GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) -# If GCD(a,b) = 1, then for any remainder ra modulo a and any remainder rb modulo b -# there exists integer n, such that n = ra (mod a) and n = ra(mod b). If n1 and n2 are -# two such integers, then n1=n2(mod ab) +If GCD(a,b) = 1, then for any remainder ra modulo a and any remainder rb modulo b +there exists integer n, such that n = ra (mod a) and n = ra(mod b). If n1 and n2 are +two such integers, then n1=n2(mod ab) -# Algorithm : +Algorithm : -# 1. Use extended euclid algorithm to find x,y such that a*x + b*y = 1 -# 2. Take n = ra*by + rb*ax +1. Use extended euclid algorithm to find x,y such that a*x + b*y = 1 +2. Take n = ra*by + rb*ax +""" +from typing import Tuple # Extended Euclid -def extended_euclid(a: int, b: int) -> (int, int): +def extended_euclid(a: int, b: int) -> Tuple[int, int]: """ >>> extended_euclid(10, 6) (-1, 2) diff --git a/blockchain/diophantine_equation.py b/blockchain/diophantine_equation.py index a92c2a13cfd5..7df674cb1438 100644 --- a/blockchain/diophantine_equation.py +++ b/blockchain/diophantine_equation.py @@ -1,12 +1,14 @@ -# Diophantine Equation : Given integers a,b,c ( at least one of a and b != 0), the -# diophantine equation a*x + b*y = c has a solution (where x and y are integers) -# iff gcd(a,b) divides c. +from typing import Tuple -# GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) - -def diophantine(a: int, b: int, c: int) -> (int, int): +def diophantine(a: int, b: int, c: int) -> Tuple[float, float]: """ + Diophantine Equation : Given integers a,b,c ( at least one of a and b != 0), the + diophantine equation a*x + b*y = c has a solution (where x and y are integers) + iff gcd(a,b) divides c. + + GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) + >>> diophantine(10,6,14) (-7.0, 14.0) @@ -26,19 +28,19 @@ def diophantine(a: int, b: int, c: int) -> (int, int): return (r * x, r * y) -# Lemma : if n|ab and gcd(a,n) = 1, then n|b. - -# Finding All solutions of Diophantine Equations: +def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: + """ + Lemma : if n|ab and gcd(a,n) = 1, then n|b. -# Theorem : Let gcd(a,b) = d, a = d*p, b = d*q. If (x0,y0) is a solution of Diophantine -# Equation a*x + b*y = c. a*x0 + b*y0 = c, then all the solutions have the form -# a(x0 + t*q) + b(y0 - t*p) = c, where t is an arbitrary integer. + Finding All solutions of Diophantine Equations: -# n is the number of solution you want, n = 2 by default + Theorem : Let gcd(a,b) = d, a = d*p, b = d*q. If (x0,y0) is a solution of + Diophantine Equation a*x + b*y = c. a*x0 + b*y0 = c, then all the + solutions have the form a(x0 + t*q) + b(y0 - t*p) = c, + where t is an arbitrary integer. + n is the number of solution you want, n = 2 by default -def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: - """ >>> diophantine_all_soln(10, 6, 14) -7.0 14.0 -4.0 9.0 @@ -67,13 +69,12 @@ def diophantine_all_soln(a: int, b: int, c: int, n: int = 2) -> None: print(x, y) -# Euclid's Lemma : d divides a and b, if and only if d divides a-b and b - -# Euclid's Algorithm - - def greatest_common_divisor(a: int, b: int) -> int: """ + Euclid's Lemma : d divides a and b, if and only if d divides a-b and b + + Euclid's Algorithm + >>> greatest_common_divisor(7,5) 1 @@ -94,12 +95,11 @@ def greatest_common_divisor(a: int, b: int) -> int: return b -# Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers -# x and y, then d = gcd(a,b) - - -def extended_gcd(a: int, b: int) -> (int, int, int): +def extended_gcd(a: int, b: int) -> Tuple[int, int, int]: """ + Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers + x and y, then d = gcd(a,b) + >>> extended_gcd(10, 6) (2, -1, 2) diff --git a/blockchain/modular_division.py b/blockchain/modular_division.py index e012db28fab8..4f7f50a92ad0 100644 --- a/blockchain/modular_division.py +++ b/blockchain/modular_division.py @@ -1,21 +1,23 @@ -# Modular Division : -# An efficient algorithm for dividing b by a modulo n. +from typing import Tuple -# GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) -# Given three integers a, b, and n, such that gcd(a,n)=1 and n>1, the algorithm should -# return an integer x such that 0≤x≤n−1, and b/a=x(modn) (that is, b=ax(modn)). +def modular_division(a: int, b: int, n: int) -> int: + """ + Modular Division : + An efficient algorithm for dividing b by a modulo n. -# Theorem: -# a has a multiplicative inverse modulo n iff gcd(a,n) = 1 + GCD ( Greatest Common Divisor ) or HCF ( Highest Common Factor ) + Given three integers a, b, and n, such that gcd(a,n)=1 and n>1, the algorithm should + return an integer x such that 0≤x≤n−1, and b/a=x(modn) (that is, b=ax(modn)). -# This find x = b*a^(-1) mod n -# Uses ExtendedEuclid to find the inverse of a + Theorem: + a has a multiplicative inverse modulo n iff gcd(a,n) = 1 -def modular_division(a: int, b: int, n: int) -> int: - """ + This find x = b*a^(-1) mod n + Uses ExtendedEuclid to find the inverse of a + >>> modular_division(4,8,5) 2 @@ -32,9 +34,10 @@ def modular_division(a: int, b: int, n: int) -> int: return x -# This function find the inverses of a i.e., a^(-1) def invert_modulo(a: int, n: int) -> int: """ + This function find the inverses of a i.e., a^(-1) + >>> invert_modulo(2, 5) 3 @@ -50,9 +53,11 @@ def invert_modulo(a: int, n: int) -> int: # ------------------ Finding Modular division using invert_modulo ------------------- -# This function used the above inversion of a to find x = (b*a^(-1))mod n + def modular_division2(a: int, b: int, n: int) -> int: """ + This function used the above inversion of a to find x = (b*a^(-1))mod n + >>> modular_division2(4,8,5) 2 @@ -68,17 +73,15 @@ def modular_division2(a: int, b: int, n: int) -> int: return x -# Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers x -# and y, then d = gcd(a,b) - - -def extended_gcd(a: int, b: int) -> (int, int, int): +def extended_gcd(a: int, b: int) -> Tuple[int, int, int]: """ - >>> extended_gcd(10, 6) - (2, -1, 2) + Extended Euclid's Algorithm : If d divides a and b and d = a*x + b*y for integers x + and y, then d = gcd(a,b) + >>> extended_gcd(10, 6) + (2, -1, 2) - >>> extended_gcd(7, 5) - (1, -2, 3) + >>> extended_gcd(7, 5) + (1, -2, 3) ** extended_gcd function is used when d = gcd(a,b) is required in output @@ -98,9 +101,9 @@ def extended_gcd(a: int, b: int) -> (int, int, int): return (d, x, y) -# Extended Euclid -def extended_euclid(a: int, b: int) -> (int, int): +def extended_euclid(a: int, b: int) -> Tuple[int, int]: """ + Extended Euclid >>> extended_euclid(10, 6) (-1, 2) @@ -115,12 +118,11 @@ def extended_euclid(a: int, b: int) -> (int, int): return (y, x - k * y) -# Euclid's Lemma : d divides a and b, if and only if d divides a-b and b -# Euclid's Algorithm - - def greatest_common_divisor(a: int, b: int) -> int: """ + Euclid's Lemma : d divides a and b, if and only if d divides a-b and b + Euclid's Algorithm + >>> greatest_common_divisor(7,5) 1 From 08228ecc3492a61e62b592ce8cb3c8076fc4be77 Mon Sep 17 00:00:00 2001 From: Erdum Date: Mon, 30 Nov 2020 03:07:10 +0500 Subject: [PATCH 117/195] Electric power (#3976) * Electric power * updated as suggested by cclauss * updated as suggested by cclauss * decimal value error * All done --- electronics/electric_power.py | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 electronics/electric_power.py diff --git a/electronics/electric_power.py b/electronics/electric_power.py new file mode 100644 index 000000000000..768c3d5c7232 --- /dev/null +++ b/electronics/electric_power.py @@ -0,0 +1,49 @@ +# https://en.m.wikipedia.org/wiki/Electric_power +from collections import namedtuple + + +def electric_power(voltage: float, current: float, power: float) -> float: + """ + This function can calculate any one of the three (voltage, current, power), + fundamental value of electrical system. + examples are below: + >>> electric_power(voltage=0, current=2, power=5) + result(name='voltage', value=2.5) + >>> electric_power(voltage=2, current=2, power=0) + result(name='power', value=4.0) + >>> electric_power(voltage=-2, current=3, power=0) + result(name='power', value=6.0) + >>> electric_power(voltage=2, current=4, power=2) + Traceback (most recent call last): + File "", line 15, in + ValueError: Only one argument must be 0 + >>> electric_power(voltage=0, current=0, power=2) + Traceback (most recent call last): + File "", line 19, in + ValueError: Only one argument must be 0 + >>> electric_power(voltage=0, current=2, power=-4) + Traceback (most recent call last): + File "", line 23, in >> electric_power(voltage=2.2, current=2.2, power=0) + result(name='power', value=4.84) + """ + result = namedtuple("result", "name value") + if (voltage, current, power).count(0) != 1: + raise ValueError("Only one argument must be 0") + elif power < 0: + raise ValueError( + "Power cannot be negative in any electrical/electronics system" + ) + elif voltage == 0: + return result("voltage", power / current) + elif current == 0: + return result("current", power / voltage) + elif power == 0: + return result("power", float(round(abs(voltage * current), 2))) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 2263909dea3b8b31b4b140d6a09022ceaa681359 Mon Sep 17 00:00:00 2001 From: wuyudi Date: Mon, 30 Nov 2020 22:59:23 +0800 Subject: [PATCH 118/195] Update pigeon_sort.py (#2359) * Update pigeon_sort.py * Update pigeon_sort.py * Update pigeon_sort.py --- sorts/pigeon_sort.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/sorts/pigeon_sort.py b/sorts/pigeon_sort.py index cc6205f804dc..3126e47c719e 100644 --- a/sorts/pigeon_sort.py +++ b/sorts/pigeon_sort.py @@ -26,29 +26,17 @@ def pigeon_sort(array): if len(array) == 0: return array - # Manually finds the minimum and maximum of the array. - min = array[0] - max = array[0] - - for i in range(len(array)): - if array[i] < min: - min = array[i] - elif array[i] > max: - max = array[i] + _min, _max = min(array), max(array) # Compute the variables - holes_range = max - min + 1 - holes = [0 for _ in range(holes_range)] - holes_repeat = [0 for _ in range(holes_range)] + holes_range = _max - _min + 1 + holes, holes_repeat = [0] * holes_range, [0] * holes_range # Make the sorting. - for i in range(len(array)): - index = array[i] - min - if holes[index] != array[i]: - holes[index] = array[i] - holes_repeat[index] += 1 - else: - holes_repeat[index] += 1 + for i in array: + index = i - _min + holes[index] = i + holes_repeat[index] += 1 # Makes the array back by replacing the numbers. index = 0 @@ -63,6 +51,8 @@ def pigeon_sort(array): if __name__ == "__main__": + import doctest + doctest.testmod() user_input = input("Enter numbers separated by comma:\n") unsorted = [int(x) for x in user_input.split(",")] print(pigeon_sort(unsorted)) From 40e6b1363d7a6df30e6661683fd2d4eaa74b762d Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Mon, 30 Nov 2020 21:03:29 +0530 Subject: [PATCH 119/195] Fix pre-commit error on master (#3992) * Update pigeon_sort.py * updating DIRECTORY.md * Add type hints and return annotation Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 1 + sorts/pigeon_sort.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 00da7922d54d..2307685f1330 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -250,6 +250,7 @@ * [Sum Of Subset](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/sum_of_subset.py) ## Electronics + * [Electric Power](https://github.com/TheAlgorithms/Python/blob/master/electronics/electric_power.py) * [Ohms Law](https://github.com/TheAlgorithms/Python/blob/master/electronics/ohms_law.py) ## File Transfer diff --git a/sorts/pigeon_sort.py b/sorts/pigeon_sort.py index 3126e47c719e..3d81f0643865 100644 --- a/sorts/pigeon_sort.py +++ b/sorts/pigeon_sort.py @@ -9,9 +9,10 @@ For manual testing run: python pigeon_sort.py """ +from typing import List -def pigeon_sort(array): +def pigeon_sort(array: List[int]) -> List[int]: """ Implementation of pigeon hole sort algorithm :param array: Collection of comparable items @@ -52,6 +53,7 @@ def pigeon_sort(array): if __name__ == "__main__": import doctest + doctest.testmod() user_input = input("Enter numbers separated by comma:\n") unsorted = [int(x) for x in user_input.split(",")] From fe39c27d750e93c49caa3b8baaf4075d3ed977d7 Mon Sep 17 00:00:00 2001 From: Maliha Date: Thu, 3 Dec 2020 07:02:48 -0800 Subject: [PATCH 120/195] Create merge_two_lists.py that implements merging of two sorted linked lists (#3874) * Create merge_two_lists.py that implements merging of two sorted linked lists * Update merge_two_lists.py Fixed formatting errors * Fixed trailing whitespace * Change name of function to def __str__() * updating DIRECTORY.md * Imported classes from singly_linked_list.py * Update merge_two_lists.py * Update merge_two_lists.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + .../linked_list/merge_two_lists.py | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 data_structures/linked_list/merge_two_lists.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 2307685f1330..c9c3a09eb599 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -153,6 +153,7 @@ * [From Sequence](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/from_sequence.py) * [Has Loop](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/has_loop.py) * [Is Palindrome](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/is_palindrome.py) + * [Merge Two Lists](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/merge_two_lists.py) * [Middle Element Of Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/middle_element_of_linked_list.py) * [Print Reverse](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/print_reverse.py) * [Singly Linked List](https://github.com/TheAlgorithms/Python/blob/master/data_structures/linked_list/singly_linked_list.py) diff --git a/data_structures/linked_list/merge_two_lists.py b/data_structures/linked_list/merge_two_lists.py new file mode 100644 index 000000000000..96ec6b8abc85 --- /dev/null +++ b/data_structures/linked_list/merge_two_lists.py @@ -0,0 +1,83 @@ +""" +Algorithm that merges two sorted linked lists into one sorted linked list. +""" +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from dataclasses import dataclass +from typing import Optional + +test_data_odd = (3, 9, -11, 0, 7, 5, 1, -1) +test_data_even = (4, 6, 2, 0, 8, 10, 3, -2) + + +@dataclass +class Node: + data: int + next: Optional[Node] + + +class SortedLinkedList: + def __init__(self, ints: Iterable[int]) -> None: + self.head: Optional[Node] = None + for i in reversed(sorted(ints)): + self.head = Node(i, self.head) + + def __iter__(self) -> Iterator[int]: + """ + >>> tuple(SortedLinkedList(test_data_odd)) == tuple(sorted(test_data_odd)) + True + >>> tuple(SortedLinkedList(test_data_even)) == tuple(sorted(test_data_even)) + True + """ + node = self.head + while node: + yield node.data + node = node.next + + def __len__(self) -> int: + """ + >>> for i in range(3): + ... len(SortedLinkedList(range(i))) == i + True + True + True + >>> len(SortedLinkedList(test_data_odd)) + 8 + """ + return len(tuple(iter(self))) + + def __str__(self) -> str: + """ + >>> str(SortedLinkedList([])) + '' + >>> str(SortedLinkedList(test_data_odd)) + '-11 -> -1 -> 0 -> 1 -> 3 -> 5 -> 7 -> 9' + >>> str(SortedLinkedList(test_data_even)) + '-2 -> 0 -> 2 -> 3 -> 4 -> 6 -> 8 -> 10' + """ + return " -> ".join([str(node) for node in self]) + + +def merge_lists( + sll_one: SortedLinkedList, sll_two: SortedLinkedList +) -> SortedLinkedList: + """ + >>> SSL = SortedLinkedList + >>> merged = merge_lists(SSL(test_data_odd), SSL(test_data_even)) + >>> len(merged) + 16 + >>> str(merged) + '-11 -> -2 -> -1 -> 0 -> 0 -> 1 -> 2 -> 3 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10' + >>> list(merged) == list(sorted(test_data_odd + test_data_even)) + True + """ + return SortedLinkedList(list(sll_one) + list(sll_two)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + SSL = SortedLinkedList + print(merge_lists(SSL(test_data_odd), SSL(test_data_even))) From b00384e2aab3135e15cd8a9fef95fa6d5409998e Mon Sep 17 00:00:00 2001 From: Jogendra Singh <58473917+Joe-Sin7h@users.noreply.github.com> Date: Wed, 9 Dec 2020 11:38:49 +0530 Subject: [PATCH 121/195] Update bitonic_sort with type hints, doctest, snake_case names (#4016) * Updated input * Fix pre-commit error * Add type hints, doctests, black, snake_case Co-authored-by: Dhruv Manilawala --- sorts/bitonic_sort.py | 144 ++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/sorts/bitonic_sort.py b/sorts/bitonic_sort.py index be3499de13cd..c718973e5ecb 100644 --- a/sorts/bitonic_sort.py +++ b/sorts/bitonic_sort.py @@ -1,58 +1,96 @@ -# Python program for Bitonic Sort. Note that this program -# works only when size of input is a power of 2. - - -# The parameter dir indicates the sorting direction, ASCENDING -# or DESCENDING; if (a[i] > a[j]) agrees with the direction, -# then a[i] and a[j] are interchanged. -def compAndSwap(a, i, j, dire): - if (dire == 1 and a[i] > a[j]) or (dire == 0 and a[i] < a[j]): - a[i], a[j] = a[j], a[i] - - # It recursively sorts a bitonic sequence in ascending order, - - -# if dir = 1, and in descending order otherwise (means dir=0). -# The sequence to be sorted starts at index position low, -# the parameter cnt is the number of elements to be sorted. -def bitonic_merge(a, low, cnt, dire): - if cnt > 1: - k = int(cnt / 2) - for i in range(low, low + k): - compAndSwap(a, i, i + k, dire) - bitonic_merge(a, low, k, dire) - bitonic_merge(a, low + k, k, dire) - - # This function first produces a bitonic sequence by recursively - - -# sorting its two halves in opposite sorting orders, and then -# calls bitonic_merge to make them in the same order -def bitonic_sort(a, low, cnt, dire): - if cnt > 1: - k = int(cnt / 2) - bitonic_sort(a, low, k, 1) - bitonic_sort(a, low + k, k, 0) - bitonic_merge(a, low, cnt, dire) - - # Caller of bitonic_sort for sorting the entire array of length N - - -# in ASCENDING order -def sort(a, N, up): - bitonic_sort(a, 0, N, up) +""" +Python program for Bitonic Sort. + +Note that this program works only when size of input is a power of 2. +""" +from typing import List + + +def comp_and_swap(array: List[int], index1: int, index2: int, direction: int) -> None: + """Compare the value at given index1 and index2 of the array and swap them as per + the given direction. + + The parameter direction indicates the sorting direction, ASCENDING(1) or + DESCENDING(0); if (a[i] > a[j]) agrees with the direction, then a[i] and a[j] are + interchanged. + + >>> arr = [12, 42, -21, 1] + >>> comp_and_swap(arr, 1, 2, 1) + >>> print(arr) + [12, -21, 42, 1] + + >>> comp_and_swap(arr, 1, 2, 0) + >>> print(arr) + [12, 42, -21, 1] + + >>> comp_and_swap(arr, 0, 3, 1) + >>> print(arr) + [1, 42, -21, 12] + + >>> comp_and_swap(arr, 0, 3, 0) + >>> print(arr) + [12, 42, -21, 1] + """ + if (direction == 1 and array[index1] > array[index2]) or ( + direction == 0 and array[index1] < array[index2] + ): + array[index1], array[index2] = array[index2], array[index1] + + +def bitonic_merge(array: List[int], low: int, length: int, direction: int) -> None: + """ + It recursively sorts a bitonic sequence in ascending order, if direction = 1, and in + descending if direction = 0. + The sequence to be sorted starts at index position low, the parameter length is the + number of elements to be sorted. + + >>> arr = [12, 42, -21, 1] + >>> bitonic_merge(arr, 0, 4, 1) + >>> print(arr) + [-21, 1, 12, 42] + + >>> bitonic_merge(arr, 0, 4, 0) + >>> print(arr) + [42, 12, 1, -21] + """ + if length > 1: + middle = int(length / 2) + for i in range(low, low + middle): + comp_and_swap(array, i, i + middle, direction) + bitonic_merge(array, low, middle, direction) + bitonic_merge(array, low + middle, middle, direction) + + +def bitonic_sort(array: List[int], low: int, length: int, direction: int) -> None: + """ + This function first produces a bitonic sequence by recursively sorting its two + halves in opposite sorting orders, and then calls bitonic_merge to make them in the + same order. + + >>> arr = [12, 34, 92, -23, 0, -121, -167, 145] + >>> bitonic_sort(arr, 0, 8, 1) + >>> arr + [-167, -121, -23, 0, 12, 34, 92, 145] + + >>> bitonic_sort(arr, 0, 8, 0) + >>> arr + [145, 92, 34, 12, 0, -23, -121, -167] + """ + if length > 1: + middle = int(length / 2) + bitonic_sort(array, low, middle, 1) + bitonic_sort(array, low + middle, middle, 0) + bitonic_merge(array, low, length, direction) if __name__ == "__main__": + user_input = input("Enter numbers separated by a comma:\n").strip() + unsorted = [int(item.strip()) for item in user_input.split(",")] - a = [] - - n = int(input().strip()) - for i in range(n): - a.append(int(input().strip())) - up = 1 + bitonic_sort(unsorted, 0, len(unsorted), 1) + print("\nSorted array in ascending order is: ", end="") + print(*unsorted, sep=", ") - sort(a, n, up) - print("\n\nSorted array is") - for i in range(n): - print("%d" % a[i]) + bitonic_merge(unsorted, 0, len(unsorted), 0) + print("Sorted array in descending order is: ", end="") + print(*unsorted, sep=", ") From bb2925df39e2fc68bc92195e6014b3a89928e3d0 Mon Sep 17 00:00:00 2001 From: Lewis Tian Date: Wed, 9 Dec 2020 17:21:46 +0800 Subject: [PATCH 122/195] update graphs/breadth_first_search.py (#3908) * update graphs/breadth_first_search.py - update naming style to snake_case - add type hints * add doctests --- graphs/breadth_first_search.py | 74 ++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/graphs/breadth_first_search.py b/graphs/breadth_first_search.py index e40ec9d1d06d..ee9855bd0c2d 100644 --- a/graphs/breadth_first_search.py +++ b/graphs/breadth_first_search.py @@ -2,24 +2,52 @@ """ Author: OMKAR PATHAK """ +from typing import Set + class Graph: - def __init__(self): + def __init__(self) -> None: self.vertices = {} - def printGraph(self): - """prints adjacency list representation of graaph""" - for i in self.vertices.keys(): + def print_graph(self) -> None: + """ + prints adjacency list representation of graaph + >>> g = Graph() + >>> g.print_graph() + >>> g.add_edge(0, 1) + >>> g.print_graph() + 0 : 1 + """ + for i in self.vertices: print(i, " : ", " -> ".join([str(j) for j in self.vertices[i]])) - def addEdge(self, fromVertex, toVertex): - """adding the edge between two vertices""" - if fromVertex in self.vertices.keys(): - self.vertices[fromVertex].append(toVertex) + def add_edge(self, from_vertex: int, to_vertex: int) -> None: + """ + adding the edge between two vertices + >>> g = Graph() + >>> g.print_graph() + >>> g.add_edge(0, 1) + >>> g.print_graph() + 0 : 1 + """ + if from_vertex in self.vertices: + self.vertices[from_vertex].append(to_vertex) else: - self.vertices[fromVertex] = [toVertex] + self.vertices[from_vertex] = [to_vertex] - def BFS(self, startVertex): + def bfs(self, start_vertex: int) -> Set[int]: + """ + >>> g = Graph() + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 1) + >>> g.add_edge(0, 2) + >>> g.add_edge(1, 2) + >>> g.add_edge(2, 0) + >>> g.add_edge(2, 3) + >>> g.add_edge(3, 3) + >>> sorted(g.bfs(2)) + [0, 1, 2, 3] + """ # initialize set for storing already visited vertices visited = set() @@ -27,8 +55,8 @@ def BFS(self, startVertex): queue = [] # mark the source node as visited and enqueue it - visited.add(startVertex) - queue.append(startVertex) + visited.add(start_vertex) + queue.append(start_vertex) while queue: vertex = queue.pop(0) @@ -42,18 +70,22 @@ def BFS(self, startVertex): if __name__ == "__main__": + from doctest import testmod + + testmod(verbose=True) + g = Graph() - g.addEdge(0, 1) - g.addEdge(0, 2) - g.addEdge(1, 2) - g.addEdge(2, 0) - g.addEdge(2, 3) - g.addEdge(3, 3) - - g.printGraph() + g.add_edge(0, 1) + g.add_edge(0, 2) + g.add_edge(1, 2) + g.add_edge(2, 0) + g.add_edge(2, 3) + g.add_edge(3, 3) + + g.print_graph() # 0 : 1 -> 2 # 1 : 2 # 2 : 0 -> 3 # 3 : 3 - assert sorted(g.BFS(2)) == [0, 1, 2, 3] + assert sorted(g.bfs(2)) == [0, 1, 2, 3] From c45eb3880b01f775dcc0baf6c2b29c9bf06e464d Mon Sep 17 00:00:00 2001 From: Alex Joslin Date: Wed, 9 Dec 2020 01:22:07 -0800 Subject: [PATCH 123/195] Implemented minimum steps to one using tabulation. (#3911) * Implemented minimum steps to one using tabulation. * Update minimum_steps_to_one.py Made the parameter "n" more descriptive. Changed it to number * `n` to `number` Co-authored-by: John Law --- dynamic_programming/minimum_steps_to_one.py | 65 +++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 dynamic_programming/minimum_steps_to_one.py diff --git a/dynamic_programming/minimum_steps_to_one.py b/dynamic_programming/minimum_steps_to_one.py new file mode 100644 index 000000000000..f4eb7033dd20 --- /dev/null +++ b/dynamic_programming/minimum_steps_to_one.py @@ -0,0 +1,65 @@ +""" +YouTube Explanation: https://www.youtube.com/watch?v=f2xi3c1S95M + +Given an integer n, return the minimum steps to 1 + +AVAILABLE STEPS: + * Decrement by 1 + * if n is divisible by 2, divide by 2 + * if n is divisible by 3, divide by 3 + + +Example 1: n = 10 +10 -> 9 -> 3 -> 1 +Result: 3 steps + +Example 2: n = 15 +15 -> 5 -> 4 -> 2 -> 1 +Result: 4 steps + +Example 3: n = 6 +6 -> 2 -> 1 +Result: 2 step +""" + +from __future__ import annotations + +__author__ = "Alexander Joslin" + + +def min_steps_to_one(number: int) -> int: + """ + Minimum steps to 1 implemented using tabulation. + >>> min_steps_to_one(10) + 3 + >>> min_steps_to_one(15) + 4 + >>> min_steps_to_one(6) + 2 + + :param number: + :return int: + """ + + if number <= 0: + raise ValueError(f"n must be greater than 0. Got n = {number}") + + table = [number + 1] * (number + 1) + + # starting position + table[1] = 0 + for i in range(1, number): + table[i + 1] = min(table[i + 1], table[i] + 1) + # check if out of bounds + if i * 2 <= number: + table[i * 2] = min(table[i * 2], table[i] + 1) + # check if out of bounds + if i * 3 <= number: + table[i * 3] = min(table[i * 3], table[i] + 1) + return table[number] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 9a5689d330e45ff8649152065c82408919f529a7 Mon Sep 17 00:00:00 2001 From: fpringle Date: Wed, 9 Dec 2020 12:14:51 +0100 Subject: [PATCH 124/195] Add Project Euler Problem 180 (#4017) * Added solution for Project Euler problem 180 * Fixed minor details in Project Euler problem 180 * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 + project_euler/problem_180/__init__.py | 0 project_euler/problem_180/sol1.py | 174 ++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 project_euler/problem_180/__init__.py create mode 100644 project_euler/problem_180/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index c9c3a09eb599..10523a85c48e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -245,6 +245,7 @@ * [Max Sum Contiguous Subsequence](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/max_sum_contiguous_subsequence.py) * [Minimum Cost Path](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_cost_path.py) * [Minimum Partition](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_partition.py) + * [Minimum Steps To One](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_steps_to_one.py) * [Optimal Binary Search Tree](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/optimal_binary_search_tree.py) * [Rod Cutting](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/rod_cutting.py) * [Subset Generation](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/subset_generation.py) @@ -754,6 +755,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_173/sol1.py) * Problem 174 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_174/sol1.py) + * Problem 180 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_180/sol1.py) * Problem 188 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_188/sol1.py) * Problem 191 diff --git a/project_euler/problem_180/__init__.py b/project_euler/problem_180/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_180/sol1.py b/project_euler/problem_180/sol1.py new file mode 100644 index 000000000000..6112db2ea370 --- /dev/null +++ b/project_euler/problem_180/sol1.py @@ -0,0 +1,174 @@ +""" +Project Euler Problem 234: https://projecteuler.net/problem=234 + +For any integer n, consider the three functions + +f1,n(x,y,z) = x^(n+1) + y^(n+1) - z^(n+1) +f2,n(x,y,z) = (xy + yz + zx)*(x^(n-1) + y^(n-1) - z^(n-1)) +f3,n(x,y,z) = xyz*(xn-2 + yn-2 - zn-2) + +and their combination + +fn(x,y,z) = f1,n(x,y,z) + f2,n(x,y,z) - f3,n(x,y,z) + +We call (x,y,z) a golden triple of order k if x, y, and z are all rational numbers +of the form a / b with 0 < a < b ≤ k and there is (at least) one integer n, +so that fn(x,y,z) = 0. + +Let s(x,y,z) = x + y + z. +Let t = u / v be the sum of all distinct s(x,y,z) for all golden triples +(x,y,z) of order 35. +All the s(x,y,z) and t must be in reduced form. + +Find u + v. + + +Solution: + +By expanding the brackets it is easy to show that +fn(x, y, z) = (x + y + z) * (x^n + y^n - z^n). + +Since x,y,z are positive, the requirement fn(x, y, z) = 0 is fulfilled if and +only if x^n + y^n = z^n. + +By Fermat's Last Theorem, this means that the absolute value of n can not +exceed 2, i.e. n is in {-2, -1, 0, 1, 2}. We can eliminate n = 0 since then the +equation would reduce to 1 + 1 = 1, for which there are no solutions. + +So all we have to do is iterate through the possible numerators and denominators +of x and y, calculate the corresponding z, and check if the corresponding numerator and +denominator are integer and satisfy 0 < z_num < z_den <= 0. We use a set "uniquq_s" +to make sure there are no duplicates, and the fractions.Fraction class to make sure +we get the right numerator and denominator. + +Reference: +https://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem +""" + + +from fractions import Fraction +from math import gcd, sqrt +from typing import Tuple + + +def is_sq(number: int) -> bool: + """ + Check if number is a perfect square. + + >>> is_sq(1) + True + >>> is_sq(1000001) + False + >>> is_sq(1000000) + True + """ + sq: int = int(number ** 0.5) + return number == sq * sq + + +def add_three( + x_num: int, x_den: int, y_num: int, y_den: int, z_num: int, z_den: int +) -> Tuple[int, int]: + """ + Given the numerators and denominators of three fractions, return the + numerator and denominator of their sum in lowest form. + >>> add_three(1, 3, 1, 3, 1, 3) + (1, 1) + >>> add_three(2, 5, 4, 11, 12, 3) + (262, 55) + """ + top: int = x_num * y_den * z_den + y_num * x_den * z_den + z_num * x_den * y_den + bottom: int = x_den * y_den * z_den + hcf: int = gcd(top, bottom) + top //= hcf + bottom //= hcf + return top, bottom + + +def solution(order: int = 35) -> int: + """ + Find the sum of the numerator and denominator of the sum of all s(x,y,z) for + golden triples (x,y,z) of the given order. + + >>> solution(5) + 296 + >>> solution(10) + 12519 + >>> solution(20) + 19408891927 + """ + unique_s: set = set() + hcf: int + total: Fraction = Fraction(0) + fraction_sum: Tuple[int, int] + + for x_num in range(1, order + 1): + for x_den in range(x_num + 1, order + 1): + for y_num in range(1, order + 1): + for y_den in range(y_num + 1, order + 1): + # n=1 + z_num = x_num * y_den + x_den * y_num + z_den = x_den * y_den + hcf = gcd(z_num, z_den) + z_num //= hcf + z_den //= hcf + if 0 < z_num < z_den <= order: + fraction_sum = add_three( + x_num, x_den, y_num, y_den, z_num, z_den + ) + unique_s.add(fraction_sum) + + # n=2 + z_num = ( + x_num * x_num * y_den * y_den + x_den * x_den * y_num * y_num + ) + z_den = x_den * x_den * y_den * y_den + if is_sq(z_num) and is_sq(z_den): + z_num = int(sqrt(z_num)) + z_den = int(sqrt(z_den)) + hcf = gcd(z_num, z_den) + z_num //= hcf + z_den //= hcf + if 0 < z_num < z_den <= order: + fraction_sum = add_three( + x_num, x_den, y_num, y_den, z_num, z_den + ) + unique_s.add(fraction_sum) + + # n=-1 + z_num = x_num * y_num + z_den = x_den * y_num + x_num * y_den + hcf = gcd(z_num, z_den) + z_num //= hcf + z_den //= hcf + if 0 < z_num < z_den <= order: + fraction_sum = add_three( + x_num, x_den, y_num, y_den, z_num, z_den + ) + unique_s.add(fraction_sum) + + # n=2 + z_num = x_num * x_num * y_num * y_num + z_den = ( + x_den * x_den * y_num * y_num + x_num * x_num * y_den * y_den + ) + if is_sq(z_num) and is_sq(z_den): + z_num = int(sqrt(z_num)) + z_den = int(sqrt(z_den)) + hcf = gcd(z_num, z_den) + z_num //= hcf + z_den //= hcf + if 0 < z_num < z_den <= order: + fraction_sum = add_three( + x_num, x_den, y_num, y_den, z_num, z_den + ) + unique_s.add(fraction_sum) + + for num, den in unique_s: + total += Fraction(num, den) + + return total.denominator + total.numerator + + +if __name__ == "__main__": + print(f"{solution() = }") From 25a5e9e4d3322cd8a7fadb2e3de07ef009a1bda9 Mon Sep 17 00:00:00 2001 From: Umair Kamran Date: Wed, 9 Dec 2020 19:01:58 +0500 Subject: [PATCH 125/195] Chore: Added type hints to searches/binary_search.py (#2682) * Chore: Added type hints to searches/binary_search.py * Use -1 as the sentinal value * Wrap long lines * Update binary_search.py * Update binary_search.py Co-authored-by: Christian Clauss --- searches/binary_search.py | 74 +++++++++++++++------------------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/searches/binary_search.py b/searches/binary_search.py index d0f6296168fa..35e0dd0596d2 100644 --- a/searches/binary_search.py +++ b/searches/binary_search.py @@ -1,18 +1,21 @@ +#!/usr/bin/env python3 + """ This is pure Python implementation of binary search algorithms For doctests run following command: -python -m doctest -v binary_search.py -or python3 -m doctest -v binary_search.py For manual testing run: -python binary_search.py +python3 binary_search.py """ import bisect +from typing import List, Optional -def bisect_left(sorted_collection, item, lo=0, hi=None): +def bisect_left( + sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 +) -> int: """ Locates the first element in a sorted array that is larger or equal to a given value. @@ -43,7 +46,7 @@ def bisect_left(sorted_collection, item, lo=0, hi=None): >>> bisect_left([0, 5, 7, 10, 15], 6, 2) 2 """ - if hi is None: + if hi < 0: hi = len(sorted_collection) while lo < hi: @@ -56,7 +59,9 @@ def bisect_left(sorted_collection, item, lo=0, hi=None): return lo -def bisect_right(sorted_collection, item, lo=0, hi=None): +def bisect_right( + sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 +) -> int: """ Locates the first element in a sorted array that is larger than a given value. @@ -86,7 +91,7 @@ def bisect_right(sorted_collection, item, lo=0, hi=None): >>> bisect_right([0, 5, 7, 10, 15], 6, 2) 2 """ - if hi is None: + if hi < 0: hi = len(sorted_collection) while lo < hi: @@ -99,7 +104,9 @@ def bisect_right(sorted_collection, item, lo=0, hi=None): return lo -def insort_left(sorted_collection, item, lo=0, hi=None): +def insort_left( + sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 +) -> None: """ Inserts a given value into a sorted array before other values with the same value. @@ -140,7 +147,9 @@ def insort_left(sorted_collection, item, lo=0, hi=None): sorted_collection.insert(bisect_left(sorted_collection, item, lo, hi), item) -def insort_right(sorted_collection, item, lo=0, hi=None): +def insort_right( + sorted_collection: List[int], item: int, lo: int = 0, hi: int = -1 +) -> None: """ Inserts a given value into a sorted array after other values with the same value. @@ -181,7 +190,7 @@ def insort_right(sorted_collection, item, lo=0, hi=None): sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item) -def binary_search(sorted_collection, item): +def binary_search(sorted_collection: List[int], item: int) -> Optional[int]: """Pure implementation of binary search algorithm in Python Be careful collection must be ascending sorted, otherwise result will be @@ -219,7 +228,7 @@ def binary_search(sorted_collection, item): return None -def binary_search_std_lib(sorted_collection, item): +def binary_search_std_lib(sorted_collection: List[int], item: int) -> Optional[int]: """Pure implementation of binary search algorithm in Python using stdlib Be careful collection must be ascending sorted, otherwise result will be @@ -248,7 +257,9 @@ def binary_search_std_lib(sorted_collection, item): return None -def binary_search_by_recursion(sorted_collection, item, left, right): +def binary_search_by_recursion( + sorted_collection: List[int], item: int, left: int, right: int +) -> Optional[int]: """Pure implementation of binary search algorithm in Python by recursion @@ -286,41 +297,12 @@ def binary_search_by_recursion(sorted_collection, item, left, right): return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right) -def __assert_sorted(collection): - """Check if collection is ascending sorted, if not - raises :py:class:`ValueError` - - :param collection: collection - :return: True if collection is ascending sorted - :raise: :py:class:`ValueError` if collection is not ascending sorted - - Examples: - >>> __assert_sorted([0, 1, 2, 4]) - True - - >>> __assert_sorted([10, -1, 5]) - Traceback (most recent call last): - ... - ValueError: Collection must be ascending sorted - """ - if collection != sorted(collection): - raise ValueError("Collection must be ascending sorted") - return True - - if __name__ == "__main__": - import sys - user_input = input("Enter numbers separated by comma:\n").strip() - collection = [int(item) for item in user_input.split(",")] - try: - __assert_sorted(collection) - except ValueError: - sys.exit("Sequence must be ascending sorted to apply binary search") - - target_input = input("Enter a single number to be found in the list:\n") - target = int(target_input) + collection = sorted(int(item) for item in user_input.split(",")) + target = int(input("Enter a single number to be found in the list:\n")) result = binary_search(collection, target) - if result is not None: - print(f"{target} found at positions: {result}") + if result is None: + print(f"{target} was not found in {collection}.") else: - print("Not found") + print(f"{target} was found at position {result} in {collection}.") From fa33c988ce428400f0b3b229308036e87da616c0 Mon Sep 17 00:00:00 2001 From: fpringle Date: Thu, 10 Dec 2020 14:18:17 +0100 Subject: [PATCH 126/195] Add solution for Project Euler problem 085 (#4024) * Added solution for Project Euler problem 085. * updating DIRECTORY.md * Minor tweaks to Project Euler problem 85 * Variable comments for project euler problem 85 Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_085/__init__.py | 0 project_euler/problem_085/sol1.py | 108 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 project_euler/problem_085/__init__.py create mode 100644 project_euler/problem_085/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 10523a85c48e..cb582e793ade 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -727,6 +727,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) * Problem 081 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) + * Problem 085 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_085/sol1.py) * Problem 087 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_087/sol1.py) * Problem 089 diff --git a/project_euler/problem_085/__init__.py b/project_euler/problem_085/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_085/sol1.py b/project_euler/problem_085/sol1.py new file mode 100644 index 000000000000..74e36b1301a4 --- /dev/null +++ b/project_euler/problem_085/sol1.py @@ -0,0 +1,108 @@ +""" +Project Euler Problem 85: https://projecteuler.net/problem=85 + +By counting carefully it can be seen that a rectangular grid measuring 3 by 2 +contains eighteen rectangles. + +Although there exists no rectangular grid that contains exactly two million +rectangles, find the area of the grid with the nearest solution. + +Solution: + + For a grid with side-lengths a and b, the number of rectangles contained in the grid + is [a*(a+1)/2] * [b*(b+1)/2)], which happens to be the product of the a-th and b-th + triangle numbers. So to find the solution grid (a,b), we need to find the two + triangle numbers whose product is closest to two million. + + Denote these two triangle numbers Ta and Tb. We want their product Ta*Tb to be + as close as possible to 2m. Assuming that the best solution is fairly close to 2m, + We can assume that both Ta and Tb are roughly bounded by 2m. Since Ta = a(a+1)/2, + we can assume that a (and similarly b) are roughly bounded by sqrt(2 * 2m) = 2000. + Since this is a rough bound, to be on the safe side we add 10%. Therefore we start + by generating all the triangle numbers Ta for 1 <= a <= 2200. This can be done + iteratively since the ith triangle number is the sum of 1,2, ... ,i, and so + T(i) = T(i-1) + i. + + We then search this list of triangle numbers for the two that give a product + closest to our target of two million. Rather than testing every combination of 2 + elements of the list, which would find the result in quadratic time, we can find + the best pair in linear time. + + We iterate through the list of triangle numbers using enumerate() so we have a + and Ta. Since we want Ta * Tb to be as close as possible to 2m, we know that Tb + needs to be roughly 2m / Ta. Using the formula Tb = b*(b+1)/2 as well as the + quadratic formula, we can solve for b: + b is roughly (-1 + sqrt(1 + 8 * 2m / Ta)) / 2. + + Since the closest integers to this estimate will give product closest to 2m, + we only need to consider the integers above and below. It's then a simple matter + to get the triangle numbers corresponding to those integers, calculate the product + Ta * Tb, compare that product to our target 2m, and keep track of the (a,b) pair + that comes the closest. + + +Reference: https://en.wikipedia.org/wiki/Triangular_number + https://en.wikipedia.org/wiki/Quadratic_formula +""" + + +from math import ceil, floor, sqrt +from typing import List + + +def solution(target: int = 2000000) -> int: + """ + Find the area of the grid which contains as close to two million rectangles + as possible. + >>> solution(20) + 6 + >>> solution(2000) + 72 + >>> solution(2000000000) + 86595 + """ + triangle_numbers: List[int] = [0] + idx: int + + for idx in range(1, ceil(sqrt(target * 2) * 1.1)): + triangle_numbers.append(triangle_numbers[-1] + idx) + + # we want this to be as close as possible to target + best_product: int = 0 + # the area corresponding to the grid that gives the product closest to target + area: int = 0 + # an estimate of b, using the quadratic formula + b_estimate: float + # the largest integer less than b_estimate + b_floor: int + # the largest integer less than b_estimate + b_ceil: int + # the triangle number corresponding to b_floor + triangle_b_first_guess: int + # the triangle number corresponding to b_ceil + triangle_b_second_guess: int + + for idx_a, triangle_a in enumerate(triangle_numbers[1:], 1): + b_estimate = (-1 + sqrt(1 + 8 * target / triangle_a)) / 2 + b_floor = floor(b_estimate) + b_ceil = ceil(b_estimate) + triangle_b_first_guess = triangle_numbers[b_floor] + triangle_b_second_guess = triangle_numbers[b_ceil] + + if abs(target - triangle_b_first_guess * triangle_a) < abs( + target - best_product + ): + best_product = triangle_b_first_guess * triangle_a + area = idx_a * b_floor + + if abs(target - triangle_b_second_guess * triangle_a) < abs( + target - best_product + ): + best_product = triangle_b_second_guess * triangle_a + area = idx_a * b_ceil + + return area + + +if __name__ == "__main__": + print(f"{solution() = }") From fa75ee4bc4d94eeeee9ea5a5dcac6a9cc85631ef Mon Sep 17 00:00:00 2001 From: Abdeldjaouad Nusayr Medakene <31663979+MrGeek1337@users.noreply.github.com> Date: Thu, 10 Dec 2020 18:25:57 +0100 Subject: [PATCH 127/195] Update ciphers/caesar_cipher.py with type hints (#3860) * Update caesar_cipher.py improved for conciseness and readability * Add type hints Co-authored-by: Dhruv Manilawala --- ciphers/caesar_cipher.py | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 4038919e5dde..4b2f76c7d873 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -1,7 +1,8 @@ from string import ascii_letters +from typing import Dict, Optional -def encrypt(input_string: str, key: int, alphabet=None) -> str: +def encrypt(input_string: str, key: int, alphabet: Optional[str] = None) -> str: """ encrypt ======= @@ -79,7 +80,7 @@ def encrypt(input_string: str, key: int, alphabet=None) -> str: return result -def decrypt(input_string: str, key: int, alphabet=None) -> str: +def decrypt(input_string: str, key: int, alphabet: Optional[str] = None) -> str: """ decrypt ======= @@ -144,7 +145,7 @@ def decrypt(input_string: str, key: int, alphabet=None) -> str: return encrypt(input_string, key, alphabet) -def brute_force(input_string: str, alphabet=None) -> dict: +def brute_force(input_string: str, alphabet: Optional[str] = None) -> Dict[int, str]: """ brute_force =========== @@ -193,31 +194,18 @@ def brute_force(input_string: str, alphabet=None) -> dict: # Set default alphabet to lower and upper case english chars alpha = alphabet or ascii_letters - # The key during testing (will increase) - key = 1 - - # The encoded result - result = "" - # To store data on all the combinations brute_force_data = {} # Cycle through each combination - while key <= len(alpha): - # Decrypt the message - result = decrypt(input_string, key, alpha) - - # Update the data - brute_force_data[key] = result - - # Reset result and increase the key - result = "" - key += 1 + for key in range(1, len(alpha) + 1): + # Decrypt the message and store the result in the data + brute_force_data[key] = decrypt(input_string, key, alpha) return brute_force_data -def main(): +if __name__ == "__main__": while True: print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") @@ -248,7 +236,3 @@ def main(): elif choice == "4": print("Goodbye.") break - - -if __name__ == "__main__": - main() From 997fa96f273a8f752d3ffb1b7d75642577df962f Mon Sep 17 00:00:00 2001 From: zakademic <67771932+zakademic@users.noreply.github.com> Date: Fri, 11 Dec 2020 20:40:23 -0800 Subject: [PATCH 128/195] Add conjugate gradient method algorithm (#2486) * Initial commit of the conjugate gradient method * Update linear_algebra/src/conjugate_gradient.py * Added documentation links, changed variable names to lower case and more descriptive naming, added check for symmetry in _is_matrix_spd * Made changes to some variable naming to be more clear * Update conjugate_gradient.py Co-authored-by: Zeyad Zaky Co-authored-by: Christian Clauss Co-authored-by: Dhruv Manilawala --- linear_algebra/src/conjugate_gradient.py | 173 +++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 linear_algebra/src/conjugate_gradient.py diff --git a/linear_algebra/src/conjugate_gradient.py b/linear_algebra/src/conjugate_gradient.py new file mode 100644 index 000000000000..1a65b8ccf019 --- /dev/null +++ b/linear_algebra/src/conjugate_gradient.py @@ -0,0 +1,173 @@ +""" +Resources: +- https://en.wikipedia.org/wiki/Conjugate_gradient_method +- https://en.wikipedia.org/wiki/Definite_symmetric_matrix +""" +import numpy as np + + +def _is_matrix_spd(matrix: np.array) -> bool: + """ + Returns True if input matrix is symmetric positive definite. + Returns False otherwise. + + For a matrix to be SPD, all eigenvalues must be positive. + + >>> import numpy as np + >>> matrix = np.array([ + ... [4.12401784, -5.01453636, -0.63865857], + ... [-5.01453636, 12.33347422, -3.40493586], + ... [-0.63865857, -3.40493586, 5.78591885]]) + >>> _is_matrix_spd(matrix) + True + >>> matrix = np.array([ + ... [0.34634879, 1.96165514, 2.18277744], + ... [0.74074469, -1.19648894, -1.34223498], + ... [-0.7687067 , 0.06018373, -1.16315631]]) + >>> _is_matrix_spd(matrix) + False + """ + # Ensure matrix is square. + assert np.shape(matrix)[0] == np.shape(matrix)[1] + + # If matrix not symmetric, exit right away. + if np.allclose(matrix, matrix.T) is False: + return False + + # Get eigenvalues and eignevectors for a symmetric matrix. + eigen_values, _ = np.linalg.eigh(matrix) + + # Check sign of all eigenvalues. + return np.all(eigen_values > 0) + + +def _create_spd_matrix(dimension: np.int64) -> np.array: + """ + Returns a symmetric positive definite matrix given a dimension. + + Input: + dimension gives the square matrix dimension. + + Output: + spd_matrix is an diminesion x dimensions symmetric positive definite (SPD) matrix. + + >>> import numpy as np + >>> dimension = 3 + >>> spd_matrix = _create_spd_matrix(dimension) + >>> _is_matrix_spd(spd_matrix) + True + """ + random_matrix = np.random.randn(dimension, dimension) + spd_matrix = np.dot(random_matrix, random_matrix.T) + assert _is_matrix_spd(spd_matrix) + return spd_matrix + + +def conjugate_gradient( + spd_matrix: np.array, + load_vector: np.array, + max_iterations: int = 1000, + tol: float = 1e-8, +) -> np.array: + """ + Returns solution to the linear system np.dot(spd_matrix, x) = b. + + Input: + spd_matrix is an NxN Symmetric Positive Definite (SPD) matrix. + load_vector is an Nx1 vector. + + Output: + x is an Nx1 vector that is the solution vector. + + >>> import numpy as np + >>> spd_matrix = np.array([ + ... [8.73256573, -5.02034289, -2.68709226], + ... [-5.02034289, 3.78188322, 0.91980451], + ... [-2.68709226, 0.91980451, 1.94746467]]) + >>> b = np.array([ + ... [-5.80872761], + ... [ 3.23807431], + ... [ 1.95381422]]) + >>> conjugate_gradient(spd_matrix, b) + array([[-0.63114139], + [-0.01561498], + [ 0.13979294]]) + """ + # Ensure proper dimensionality. + assert np.shape(spd_matrix)[0] == np.shape(spd_matrix)[1] + assert np.shape(load_vector)[0] == np.shape(spd_matrix)[0] + assert _is_matrix_spd(spd_matrix) + + # Initialize solution guess, residual, search direction. + x0 = np.zeros((np.shape(load_vector)[0], 1)) + r0 = np.copy(load_vector) + p0 = np.copy(r0) + + # Set initial errors in solution guess and residual. + error_residual = 1e9 + error_x_solution = 1e9 + error = 1e9 + + # Set iteration counter to threshold number of iterations. + iterations = 0 + + while error > tol: + + # Save this value so we only calculate the matrix-vector product once. + w = np.dot(spd_matrix, p0) + + # The main algorithm. + + # Update search direction magnitude. + alpha = np.dot(r0.T, r0) / np.dot(p0.T, w) + # Update solution guess. + x = x0 + alpha * p0 + # Calculate new residual. + r = r0 - alpha * w + # Calculate new Krylov subspace scale. + beta = np.dot(r.T, r) / np.dot(r0.T, r0) + # Calculate new A conjuage search direction. + p = r + beta * p0 + + # Calculate errors. + error_residual = np.linalg.norm(r - r0) + error_x_solution = np.linalg.norm(x - x0) + error = np.maximum(error_residual, error_x_solution) + + # Update variables. + x0 = np.copy(x) + r0 = np.copy(r) + p0 = np.copy(p) + + # Update number of iterations. + iterations += 1 + + return x + + +def test_conjugate_gradient() -> None: + """ + >>> test_conjugate_gradient() # self running tests + """ + # Create linear system with SPD matrix and known solution x_true. + dimension = 3 + spd_matrix = _create_spd_matrix(dimension) + x_true = np.random.randn(dimension, 1) + b = np.dot(spd_matrix, x_true) + + # Numpy solution. + x_numpy = np.linalg.solve(spd_matrix, b) + + # Our implementation. + x_conjugate_gradient = conjugate_gradient(spd_matrix, b) + + # Ensure both solutions are close to x_true (and therefore one another). + assert np.linalg.norm(x_numpy - x_true) <= 1e-6 + assert np.linalg.norm(x_conjugate_gradient - x_true) <= 1e-6 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + test_conjugate_gradient() From e51546fbcfc92fd216749c40b780e4be58029689 Mon Sep 17 00:00:00 2001 From: fpringle Date: Sat, 12 Dec 2020 06:19:35 +0100 Subject: [PATCH 129/195] Add solution for Project Euler problem 86 (#4025) * Added solution for Project Euler problem 86 * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_086/__init__.py | 0 project_euler/problem_086/sol1.py | 105 ++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 project_euler/problem_086/__init__.py create mode 100644 project_euler/problem_086/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index cb582e793ade..7eec7e0811dd 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -729,6 +729,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) * Problem 085 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_085/sol1.py) + * Problem 086 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_086/sol1.py) * Problem 087 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_087/sol1.py) * Problem 089 diff --git a/project_euler/problem_086/__init__.py b/project_euler/problem_086/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_086/sol1.py b/project_euler/problem_086/sol1.py new file mode 100644 index 000000000000..0bf66e6b5a31 --- /dev/null +++ b/project_euler/problem_086/sol1.py @@ -0,0 +1,105 @@ +""" +Project Euler Problem 86: https://projecteuler.net/problem=86 + +A spider, S, sits in one corner of a cuboid room, measuring 6 by 5 by 3, and a fly, F, +sits in the opposite corner. By travelling on the surfaces of the room the shortest +"straight line" distance from S to F is 10 and the path is shown on the diagram. + +However, there are up to three "shortest" path candidates for any given cuboid and the +shortest route doesn't always have integer length. + +It can be shown that there are exactly 2060 distinct cuboids, ignoring rotations, with +integer dimensions, up to a maximum size of M by M by M, for which the shortest route +has integer length when M = 100. This is the least value of M for which the number of +solutions first exceeds two thousand; the number of solutions when M = 99 is 1975. + +Find the least value of M such that the number of solutions first exceeds one million. + +Solution: + Label the 3 side-lengths of the cuboid a,b,c such that 1 <= a <= b <= c <= M. + By conceptually "opening up" the cuboid and laying out its faces on a plane, + it can be seen that the shortest distance between 2 opposite corners is + sqrt((a+b)^2 + c^2). This distance is an integer if and only if (a+b),c make up + the first 2 sides of a pythagorean triplet. + + The second useful insight is rather than calculate the number of cuboids + with integral shortest distance for each maximum cuboid side-length M, + we can calculate this number iteratively each time we increase M, as follows. + The set of cuboids satisfying this property with maximum side-length M-1 is a + subset of the cuboids satisfying the property with maximum side-length M + (since any cuboids with side lengths <= M-1 are also <= M). To calculate the + number of cuboids in the larger set (corresponding to M) we need only consider + the cuboids which have at least one side of length M. Since we have ordered the + side lengths a <= b <= c, we can assume that c = M. Then we just need to count + the number of pairs a,b satisfying the conditions: + sqrt((a+b)^2 + M^2) is integer + 1 <= a <= b <= M + + To count the number of pairs (a,b) satisfying these conditions, write d = a+b. + Now we have: + 1 <= a <= b <= M => 2 <= d <= 2*M + we can actually make the second equality strict, + since d = 2*M => d^2 + M^2 = 5M^2 + => shortest distance = M * sqrt(5) + => not integral. + a + b = d => b = d - a + and a <= b + => a <= d/2 + also a <= M + => a <= min(M, d//2) + + a + b = d => a = d - b + and b <= M + => a >= d - M + also a >= 1 + => a >= max(1, d - M) + + So a is in range(max(1, d - M), min(M, d // 2) + 1) + + For a given d, the number of cuboids satisfying the required property with c = M + and a + b = d is the length of this range, which is + min(M, d // 2) + 1 - max(1, d - M). + + In the code below, d is sum_shortest_sides + and M is max_cuboid_size. + + +""" + + +from math import sqrt + + +def solution(limit: int = 1000000) -> int: + """ + Return the least value of M such that there are more than one million cuboids + of side lengths 1 <= a,b,c <= M such that the shortest distance between two + opposite vertices of the cuboid is integral. + >>> solution(100) + 24 + >>> solution(1000) + 72 + >>> solution(2000) + 100 + >>> solution(20000) + 288 + """ + num_cuboids: int = 0 + max_cuboid_size: int = 0 + sum_shortest_sides: int + + while num_cuboids <= limit: + max_cuboid_size += 1 + for sum_shortest_sides in range(2, 2 * max_cuboid_size + 1): + if sqrt(sum_shortest_sides ** 2 + max_cuboid_size ** 2).is_integer(): + num_cuboids += ( + min(max_cuboid_size, sum_shortest_sides // 2) + - max(1, sum_shortest_sides - max_cuboid_size) + + 1 + ) + + return max_cuboid_size + + +if __name__ == "__main__": + print(f"{solution() = }") From 4e7df3dfae85bcfb3348b819576d2913087ce932 Mon Sep 17 00:00:00 2001 From: fpringle Date: Sun, 13 Dec 2020 12:09:52 +0100 Subject: [PATCH 130/195] Add solution for Project Euler problem 59 (#4031) * Added solution for Project Euler problem 59 * updating DIRECTORY.md * Formatting, type hints, no more evil map functions * Doctests * Added doctests for Project Euler problem 59 Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 + project_euler/problem_059/__init__.py | 0 project_euler/problem_059/p059_cipher.txt | 1 + project_euler/problem_059/sol1.py | 128 ++++++++++++++++++++++ project_euler/problem_059/test_cipher.txt | 1 + 5 files changed, 133 insertions(+) create mode 100644 project_euler/problem_059/__init__.py create mode 100644 project_euler/problem_059/p059_cipher.txt create mode 100644 project_euler/problem_059/sol1.py create mode 100644 project_euler/problem_059/test_cipher.txt diff --git a/DIRECTORY.md b/DIRECTORY.md index 7eec7e0811dd..929a986b0f3b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -345,6 +345,7 @@ ## Linear Algebra * Src + * [Conjugate Gradient](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/conjugate_gradient.py) * [Lib](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/lib.py) * [Polynom For Points](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/polynom_for_points.py) * [Power Iteration](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/power_iteration.py) @@ -695,6 +696,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_057/sol1.py) * Problem 058 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_058/sol1.py) + * Problem 059 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_059/sol1.py) * Problem 062 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_062/sol1.py) * Problem 063 diff --git a/project_euler/problem_059/__init__.py b/project_euler/problem_059/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_059/p059_cipher.txt b/project_euler/problem_059/p059_cipher.txt new file mode 100644 index 000000000000..b3b3247298d1 --- /dev/null +++ b/project_euler/problem_059/p059_cipher.txt @@ -0,0 +1 @@ +36,22,80,0,0,4,23,25,19,17,88,4,4,19,21,11,88,22,23,23,29,69,12,24,0,88,25,11,12,2,10,28,5,6,12,25,10,22,80,10,30,80,10,22,21,69,23,22,69,61,5,9,29,2,66,11,80,8,23,3,17,88,19,0,20,21,7,10,17,17,29,20,69,8,17,21,29,2,22,84,80,71,60,21,69,11,5,8,21,25,22,88,3,0,10,25,0,10,5,8,88,2,0,27,25,21,10,31,6,25,2,16,21,82,69,35,63,11,88,4,13,29,80,22,13,29,22,88,31,3,88,3,0,10,25,0,11,80,10,30,80,23,29,19,12,8,2,10,27,17,9,11,45,95,88,57,69,16,17,19,29,80,23,29,19,0,22,4,9,1,80,3,23,5,11,28,92,69,9,5,12,12,21,69,13,30,0,0,0,0,27,4,0,28,28,28,84,80,4,22,80,0,20,21,2,25,30,17,88,21,29,8,2,0,11,3,12,23,30,69,30,31,23,88,4,13,29,80,0,22,4,12,10,21,69,11,5,8,88,31,3,88,4,13,17,3,69,11,21,23,17,21,22,88,65,69,83,80,84,87,68,69,83,80,84,87,73,69,83,80,84,87,65,83,88,91,69,29,4,6,86,92,69,15,24,12,27,24,69,28,21,21,29,30,1,11,80,10,22,80,17,16,21,69,9,5,4,28,2,4,12,5,23,29,80,10,30,80,17,16,21,69,27,25,23,27,28,0,84,80,22,23,80,17,16,17,17,88,25,3,88,4,13,29,80,17,10,5,0,88,3,16,21,80,10,30,80,17,16,25,22,88,3,0,10,25,0,11,80,12,11,80,10,26,4,4,17,30,0,28,92,69,30,2,10,21,80,12,12,80,4,12,80,10,22,19,0,88,4,13,29,80,20,13,17,1,10,17,17,13,2,0,88,31,3,88,4,13,29,80,6,17,2,6,20,21,69,30,31,9,20,31,18,11,94,69,54,17,8,29,28,28,84,80,44,88,24,4,14,21,69,30,31,16,22,20,69,12,24,4,12,80,17,16,21,69,11,5,8,88,31,3,88,4,13,17,3,69,11,21,23,17,21,22,88,25,22,88,17,69,11,25,29,12,24,69,8,17,23,12,80,10,30,80,17,16,21,69,11,1,16,25,2,0,88,31,3,88,4,13,29,80,21,29,2,12,21,21,17,29,2,69,23,22,69,12,24,0,88,19,12,10,19,9,29,80,18,16,31,22,29,80,1,17,17,8,29,4,0,10,80,12,11,80,84,67,80,10,10,80,7,1,80,21,13,4,17,17,30,2,88,4,13,29,80,22,13,29,69,23,22,69,12,24,12,11,80,22,29,2,12,29,3,69,29,1,16,25,28,69,12,31,69,11,92,69,17,4,69,16,17,22,88,4,13,29,80,23,25,4,12,23,80,22,9,2,17,80,70,76,88,29,16,20,4,12,8,28,12,29,20,69,26,9,69,11,80,17,23,80,84,88,31,3,88,4,13,29,80,21,29,2,12,21,21,17,29,2,69,12,31,69,12,24,0,88,20,12,25,29,0,12,21,23,86,80,44,88,7,12,20,28,69,11,31,10,22,80,22,16,31,18,88,4,13,25,4,69,12,24,0,88,3,16,21,80,10,30,80,17,16,25,22,88,3,0,10,25,0,11,80,17,23,80,7,29,80,4,8,0,23,23,8,12,21,17,17,29,28,28,88,65,75,78,68,81,65,67,81,72,70,83,64,68,87,74,70,81,75,70,81,67,80,4,22,20,69,30,2,10,21,80,8,13,28,17,17,0,9,1,25,11,31,80,17,16,25,22,88,30,16,21,18,0,10,80,7,1,80,22,17,8,73,88,17,11,28,80,17,16,21,11,88,4,4,19,25,11,31,80,17,16,21,69,11,1,16,25,2,0,88,2,10,23,4,73,88,4,13,29,80,11,13,29,7,29,2,69,75,94,84,76,65,80,65,66,83,77,67,80,64,73,82,65,67,87,75,72,69,17,3,69,17,30,1,29,21,1,88,0,23,23,20,16,27,21,1,84,80,18,16,25,6,16,80,0,0,0,23,29,3,22,29,3,69,12,24,0,88,0,0,10,25,8,29,4,0,10,80,10,30,80,4,88,19,12,10,19,9,29,80,18,16,31,22,29,80,1,17,17,8,29,4,0,10,80,12,11,80,84,86,80,35,23,28,9,23,7,12,22,23,69,25,23,4,17,30,69,12,24,0,88,3,4,21,21,69,11,4,0,8,3,69,26,9,69,15,24,12,27,24,69,49,80,13,25,20,69,25,2,23,17,6,0,28,80,4,12,80,17,16,25,22,88,3,16,21,92,69,49,80,13,25,6,0,88,20,12,11,19,10,14,21,23,29,20,69,12,24,4,12,80,17,16,21,69,11,5,8,88,31,3,88,4,13,29,80,22,29,2,12,29,3,69,73,80,78,88,65,74,73,70,69,83,80,84,87,72,84,88,91,69,73,95,87,77,70,69,83,80,84,87,70,87,77,80,78,88,21,17,27,94,69,25,28,22,23,80,1,29,0,0,22,20,22,88,31,11,88,4,13,29,80,20,13,17,1,10,17,17,13,2,0,88,31,3,88,4,13,29,80,6,17,2,6,20,21,75,88,62,4,21,21,9,1,92,69,12,24,0,88,3,16,21,80,10,30,80,17,16,25,22,88,29,16,20,4,12,8,28,12,29,20,69,26,9,69,65,64,69,31,25,19,29,3,69,12,24,0,88,18,12,9,5,4,28,2,4,12,21,69,80,22,10,13,2,17,16,80,21,23,7,0,10,89,69,23,22,69,12,24,0,88,19,12,10,19,16,21,22,0,10,21,11,27,21,69,23,22,69,12,24,0,88,0,0,10,25,8,29,4,0,10,80,10,30,80,4,88,19,12,10,19,9,29,80,18,16,31,22,29,80,1,17,17,8,29,4,0,10,80,12,11,80,84,86,80,36,22,20,69,26,9,69,11,25,8,17,28,4,10,80,23,29,17,22,23,30,12,22,23,69,49,80,13,25,6,0,88,28,12,19,21,18,17,3,0,88,18,0,29,30,69,25,18,9,29,80,17,23,80,1,29,4,0,10,29,12,22,21,69,12,24,0,88,3,16,21,3,69,23,22,69,12,24,0,88,3,16,26,3,0,9,5,0,22,4,69,11,21,23,17,21,22,88,25,11,88,7,13,17,19,13,88,4,13,29,80,0,0,0,10,22,21,11,12,3,69,25,2,0,88,21,19,29,30,69,22,5,8,26,21,23,11,94 \ No newline at end of file diff --git a/project_euler/problem_059/sol1.py b/project_euler/problem_059/sol1.py new file mode 100644 index 000000000000..1f55029b2613 --- /dev/null +++ b/project_euler/problem_059/sol1.py @@ -0,0 +1,128 @@ +""" +Each character on a computer is assigned a unique code and the preferred standard is +ASCII (American Standard Code for Information Interchange). +For example, uppercase A = 65, asterisk (*) = 42, and lowercase k = 107. + +A modern encryption method is to take a text file, convert the bytes to ASCII, then +XOR each byte with a given value, taken from a secret key. The advantage with the +XOR function is that using the same encryption key on the cipher text, restores +the plain text; for example, 65 XOR 42 = 107, then 107 XOR 42 = 65. + +For unbreakable encryption, the key is the same length as the plain text message, and +the key is made up of random bytes. The user would keep the encrypted message and the +encryption key in different locations, and without both "halves", it is impossible to +decrypt the message. + +Unfortunately, this method is impractical for most users, so the modified method is +to use a password as a key. If the password is shorter than the message, which is +likely, the key is repeated cyclically throughout the message. The balance for this +method is using a sufficiently long password key for security, but short enough to +be memorable. + +Your task has been made easy, as the encryption key consists of three lower case +characters. Using p059_cipher.txt (right click and 'Save Link/Target As...'), a +file containing the encrypted ASCII codes, and the knowledge that the plain text +must contain common English words, decrypt the message and find the sum of the ASCII +values in the original text. +""" + + +import string +from itertools import cycle, product +from pathlib import Path +from typing import List, Optional, Set, Tuple + +VALID_CHARS: str = ( + string.ascii_letters + string.digits + string.punctuation + string.whitespace +) +LOWERCASE_INTS: List[int] = [ord(letter) for letter in string.ascii_lowercase] +VALID_INTS: Set[int] = {ord(char) for char in VALID_CHARS} + +COMMON_WORDS: List[str] = ["the", "be", "to", "of", "and", "in", "that", "have"] + + +def try_key(ciphertext: List[int], key: Tuple[int, ...]) -> Optional[str]: + """ + Given an encrypted message and a possible 3-character key, decrypt the message. + If the decrypted message contains a invalid character, i.e. not an ASCII letter, + a digit, punctuation or whitespace, then we know the key is incorrect, so return + None. + >>> try_key([0, 17, 20, 4, 27], (104, 116, 120)) + 'hello' + >>> try_key([68, 10, 300, 4, 27], (104, 116, 120)) is None + True + """ + decoded: str = "" + keychar: int + cipherchar: int + decodedchar: int + + for keychar, cipherchar in zip(cycle(key), ciphertext): + decodedchar = cipherchar ^ keychar + if decodedchar not in VALID_INTS: + return None + decoded += chr(decodedchar) + + return decoded + + +def filter_valid_chars(ciphertext: List[int]) -> List[str]: + """ + Given an encrypted message, test all 3-character strings to try and find the + key. Return a list of the possible decrypted messages. + >>> from itertools import cycle + >>> text = "The enemy's gate is down" + >>> key = "end" + >>> encoded = [ord(k) ^ ord(c) for k,c in zip(cycle(key), text)] + >>> text in filter_valid_chars(encoded) + True + """ + possibles: List[str] = [] + for key in product(LOWERCASE_INTS, repeat=3): + encoded = try_key(ciphertext, key) + if encoded is not None: + possibles.append(encoded) + return possibles + + +def filter_common_word(possibles: List[str], common_word: str) -> List[str]: + """ + Given a list of possible decoded messages, narrow down the possibilities + for checking for the presence of a specified common word. Only decoded messages + containing common_word will be returned. + >>> filter_common_word(['asfla adf', 'I am here', ' !?! #a'], 'am') + ['I am here'] + >>> filter_common_word(['athla amf', 'I am here', ' !?! #a'], 'am') + ['athla amf', 'I am here'] + """ + return [possible for possible in possibles if common_word in possible.lower()] + + +def solution(filename: str = "p059_cipher.txt") -> int: + """ + Test the ciphertext against all possible 3-character keys, then narrow down the + possibilities by filtering using common words until there's only one possible + decoded message. + >>> solution("test_cipher.txt") + 3000 + """ + ciphertext: List[int] + possibles: List[str] + common_word: str + decoded_text: str + data: str = Path(__file__).parent.joinpath(filename).read_text(encoding="utf-8") + + ciphertext = [int(number) for number in data.strip().split(",")] + + possibles = filter_valid_chars(ciphertext) + for common_word in COMMON_WORDS: + possibles = filter_common_word(possibles, common_word) + if len(possibles) == 1: + break + + decoded_text = possibles[0] + return sum([ord(char) for char in decoded_text]) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_059/test_cipher.txt b/project_euler/problem_059/test_cipher.txt new file mode 100644 index 000000000000..27c53740cc1a --- /dev/null +++ b/project_euler/problem_059/test_cipher.txt @@ -0,0 +1 @@ +63,13,28,75,0,23,14,8,0,76,22,89,12,4,13,14,69,16,24,69,29,4,18,23,69,69,59,14,69,11,14,4,29,18 From 97ef2fe2413191fd0cdf5bb0b8c4bbb0550d2417 Mon Sep 17 00:00:00 2001 From: Du Yuanchao Date: Fri, 18 Dec 2020 17:39:51 +0800 Subject: [PATCH 131/195] Optimization for shell sort (#4038) * fixed shell sort * udpate code style * Update sorts/shell_sort.py Co-authored-by: John Law Co-authored-by: John Law --- sorts/shell_sort.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/sorts/shell_sort.py b/sorts/shell_sort.py index bf3c2c7f9cc6..2e749e43d056 100644 --- a/sorts/shell_sort.py +++ b/sorts/shell_sort.py @@ -1,13 +1,5 @@ """ -This is a pure Python implementation of the shell sort algorithm - -For doctests run following command: -python -m doctest -v shell_sort.py -or -python3 -m doctest -v shell_sort.py - -For manual testing run: -python shell_sort.py +https://en.wikipedia.org/wiki/Shellsort#Pseudocode """ @@ -19,26 +11,29 @@ def shell_sort(collection): >>> shell_sort([0, 5, 3, 2, 2]) [0, 2, 2, 3, 5] - >>> shell_sort([]) [] - >>> shell_sort([-2, -5, -45]) [-45, -5, -2] """ # Marcin Ciura's gap sequence - gaps = [701, 301, 132, 57, 23, 10, 4, 1] + gaps = [701, 301, 132, 57, 23, 10, 4, 1] for gap in gaps: for i in range(gap, len(collection)): + insert_value = collection[i] j = i - while j >= gap and collection[j] < collection[j - gap]: - collection[j], collection[j - gap] = collection[j - gap], collection[j] + while j >= gap and collection[j - gap] > insert_value: + collection[j] = collection[j - gap] j -= gap + collection[j] = insert_value return collection if __name__ == "__main__": + from doctest import testmod + + testmod() user_input = input("Enter numbers separated by a comma:\n").strip() unsorted = [int(item) for item in user_input.split(",")] print(shell_sort(unsorted)) From e6a812b37f227fb1441080897c328626d1dc6fc9 Mon Sep 17 00:00:00 2001 From: sharmapulkit04 <39304055+sharmapulkit04@users.noreply.github.com> Date: Sat, 19 Dec 2020 11:46:15 +0530 Subject: [PATCH 132/195] Add solution for Project Euler problem 135 (#4035) --- project_euler/problem_135/__init__.py | 0 project_euler/problem_135/sol1.py | 61 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 project_euler/problem_135/__init__.py create mode 100644 project_euler/problem_135/sol1.py diff --git a/project_euler/problem_135/__init__.py b/project_euler/problem_135/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_135/sol1.py b/project_euler/problem_135/sol1.py new file mode 100644 index 000000000000..d71a0439c7e9 --- /dev/null +++ b/project_euler/problem_135/sol1.py @@ -0,0 +1,61 @@ +""" +Project Euler Problem 135: https://projecteuler.net/problem=135 + +Given the positive integers, x, y, and z, +are consecutive terms of an arithmetic progression, +the least value of the positive integer, n, +for which the equation, +x2 − y2 − z2 = n, has exactly two solutions is n = 27: + +342 − 272 − 202 = 122 − 92 − 62 = 27 + +It turns out that n = 1155 is the least value +which has exactly ten solutions. + +How many values of n less than one million +have exactly ten distinct solutions? + + +Taking x,y,z of the form a+d,a,a-d respectively, +the given equation reduces to a*(4d-a)=n. +Calculating no of solutions for every n till 1 million by fixing a +,and n must be multiple of a. +Total no of steps=n*(1/1+1/2+1/3+1/4..+1/n) +,so roughly O(nlogn) time complexity. + +""" + + +def solution(limit: int = 1000000) -> int: + """ + returns the values of n less than or equal to the limit + have exactly ten distinct solutions. + >>> solution(100) + 0 + >>> solution(10000) + 45 + >>> solution(50050) + 292 + """ + limit = limit + 1 + frequency = [0] * limit + for first_term in range(1, limit): + for n in range(first_term, limit, first_term): + common_difference = first_term + n / first_term + if common_difference % 4: # d must be divisble by 4 + continue + else: + common_difference /= 4 + if ( + first_term > common_difference + and first_term < 4 * common_difference + ): # since x,y,z are positive integers + frequency[n] += 1 # so z>0 and a>d ,also 4d Date: Mon, 21 Dec 2020 13:55:59 -0800 Subject: [PATCH 133/195] add integer to roman function (#4050) * add integer to roman function simply added fastest method i found. * Rename roman_to_integer.py to roman_numerals.py * Update roman_numerals.py * Update roman_numerals.py Co-authored-by: Christian Clauss --- ...{roman_to_integer.py => roman_numerals.py} | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) rename conversions/{roman_to_integer.py => roman_numerals.py} (50%) diff --git a/conversions/roman_to_integer.py b/conversions/roman_numerals.py similarity index 50% rename from conversions/roman_to_integer.py rename to conversions/roman_numerals.py index ce52b6fb7cbb..9933e6a78a4d 100644 --- a/conversions/roman_to_integer.py +++ b/conversions/roman_numerals.py @@ -21,6 +21,38 @@ def roman_to_int(roman: str) -> int: return total +def int_to_roman(number: int) -> str: + """ + Given a integer, convert it to an roman numeral. + https://en.wikipedia.org/wiki/Roman_numerals + >>> tests = {"III": 3, "CLIV": 154, "MIX": 1009, "MMD": 2500, "MMMCMXCIX": 3999} + >>> all(int_to_roman(value) == key for key, value in tests.items()) + True + """ + ROMAN = [ + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I"), + ] + result = [] + for (arabic, roman) in ROMAN: + (factor, number) = divmod(number, arabic) + result.append(roman * factor) + if number == 0: + break + return "".join(result) + + if __name__ == "__main__": import doctest From 2869cd662c214242765b9b1fb876a9a0090185ea Mon Sep 17 00:00:00 2001 From: fpringle Date: Tue, 22 Dec 2020 13:02:31 +0100 Subject: [PATCH 134/195] Add solution for Project Euler problem 101 (#4033) * Added solution for Project Euler problem 101 * Got rid of map functions * updating DIRECTORY.md * Better function/variable names * Better variable names * Type hints * Doctest for nested function Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_101/__init__.py | 0 project_euler/problem_101/sol1.py | 219 ++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 project_euler/problem_101/__init__.py create mode 100644 project_euler/problem_101/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 929a986b0f3b..1f1bb9907e52 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -744,6 +744,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_097/sol1.py) * Problem 099 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_099/sol1.py) + * Problem 101 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_101/sol1.py) * Problem 112 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_112/sol1.py) * Problem 113 diff --git a/project_euler/problem_101/__init__.py b/project_euler/problem_101/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_101/sol1.py b/project_euler/problem_101/sol1.py new file mode 100644 index 000000000000..e66316090fb2 --- /dev/null +++ b/project_euler/problem_101/sol1.py @@ -0,0 +1,219 @@ +""" +If we are presented with the first k terms of a sequence it is impossible to say with +certainty the value of the next term, as there are infinitely many polynomial functions +that can model the sequence. + +As an example, let us consider the sequence of cube +numbers. This is defined by the generating function, +u(n) = n3: 1, 8, 27, 64, 125, 216, ... + +Suppose we were only given the first two terms of this sequence. Working on the +principle that "simple is best" we should assume a linear relationship and predict the +next term to be 15 (common difference 7). Even if we were presented with the first three +terms, by the same principle of simplicity, a quadratic relationship should be +assumed. + +We shall define OP(k, n) to be the nth term of the optimum polynomial +generating function for the first k terms of a sequence. It should be clear that +OP(k, n) will accurately generate the terms of the sequence for n ≤ k, and potentially +the first incorrect term (FIT) will be OP(k, k+1); in which case we shall call it a +bad OP (BOP). + +As a basis, if we were only given the first term of sequence, it would be most +sensible to assume constancy; that is, for n ≥ 2, OP(1, n) = u(1). + +Hence we obtain the +following OPs for the cubic sequence: + +OP(1, n) = 1 1, 1, 1, 1, ... +OP(2, n) = 7n-6 1, 8, 15, ... +OP(3, n) = 6n^2-11n+6 1, 8, 27, 58, ... +OP(4, n) = n^3 1, 8, 27, 64, 125, ... + +Clearly no BOPs exist for k ≥ 4. + +By considering the sum of FITs generated by the BOPs (indicated in red above), we +obtain 1 + 15 + 58 = 74. + +Consider the following tenth degree polynomial generating function: + +1 - n + n^2 - n^3 + n^4 - n^5 + n^6 - n^7 + n^8 - n^9 + n^10 + +Find the sum of FITs for the BOPs. +""" + + +from typing import Callable, List, Union + +Matrix = List[List[Union[float, int]]] + + +def solve(matrix: Matrix, vector: Matrix) -> Matrix: + """ + Solve the linear system of equations Ax = b (A = "matrix", b = "vector") + for x using Gaussian elimination and back substitution. We assume that A + is an invertible square matrix and that b is a column vector of the + same height. + >>> solve([[1, 0], [0, 1]], [[1],[2]]) + [[1.0], [2.0]] + >>> solve([[2, 1, -1],[-3, -1, 2],[-2, 1, 2]],[[8], [-11],[-3]]) + [[2.0], [3.0], [-1.0]] + """ + size: int = len(matrix) + augmented: Matrix = [[0 for _ in range(size + 1)] for _ in range(size)] + row: int + row2: int + col: int + col2: int + pivot_row: int + ratio: float + + for row in range(size): + for col in range(size): + augmented[row][col] = matrix[row][col] + + augmented[row][size] = vector[row][0] + + row = 0 + col = 0 + while row < size and col < size: + # pivoting + pivot_row = max( + [(abs(augmented[row2][col]), row2) for row2 in range(col, size)] + )[1] + if augmented[pivot_row][col] == 0: + col += 1 + continue + else: + augmented[row], augmented[pivot_row] = augmented[pivot_row], augmented[row] + + for row2 in range(row + 1, size): + ratio = augmented[row2][col] / augmented[row][col] + augmented[row2][col] = 0 + for col2 in range(col + 1, size + 1): + augmented[row2][col2] -= augmented[row][col2] * ratio + + row += 1 + col += 1 + + # back substitution + for col in range(1, size): + for row in range(col): + ratio = augmented[row][col] / augmented[col][col] + for col2 in range(col, size + 1): + augmented[row][col2] -= augmented[col][col2] * ratio + + # round to get rid of numbers like 2.000000000000004 + return [ + [round(augmented[row][size] / augmented[row][row], 10)] for row in range(size) + ] + + +def interpolate(y_list: List[int]) -> Callable[[int], int]: + """ + Given a list of data points (1,y0),(2,y1), ..., return a function that + interpolates the data points. We find the coefficients of the interpolating + polynomial by solving a system of linear equations corresponding to + x = 1, 2, 3... + + >>> interpolate([1])(3) + 1 + >>> interpolate([1, 8])(3) + 15 + >>> interpolate([1, 8, 27])(4) + 58 + >>> interpolate([1, 8, 27, 64])(6) + 216 + """ + + size: int = len(y_list) + matrix: Matrix = [[0 for _ in range(size)] for _ in range(size)] + vector: Matrix = [[0] for _ in range(size)] + coeffs: Matrix + x_val: int + y_val: int + col: int + + for x_val, y_val in enumerate(y_list): + for col in range(size): + matrix[x_val][col] = (x_val + 1) ** (size - col - 1) + vector[x_val][0] = y_val + + coeffs = solve(matrix, vector) + + def interpolated_func(var: int) -> int: + """ + >>> interpolate([1])(3) + 1 + >>> interpolate([1, 8])(3) + 15 + >>> interpolate([1, 8, 27])(4) + 58 + >>> interpolate([1, 8, 27, 64])(6) + 216 + """ + return sum( + round(coeffs[x_val][0]) * (var ** (size - x_val - 1)) + for x_val in range(size) + ) + + return interpolated_func + + +def question_function(variable: int) -> int: + """ + The generating function u as specified in the question. + >>> question_function(0) + 1 + >>> question_function(1) + 1 + >>> question_function(5) + 8138021 + >>> question_function(10) + 9090909091 + """ + return ( + 1 + - variable + + variable ** 2 + - variable ** 3 + + variable ** 4 + - variable ** 5 + + variable ** 6 + - variable ** 7 + + variable ** 8 + - variable ** 9 + + variable ** 10 + ) + + +def solution(func: Callable[[int], int] = question_function, order: int = 10) -> int: + """ + Find the sum of the FITs of the BOPS. For each interpolating polynomial of order + 1, 2, ... , 10, find the first x such that the value of the polynomial at x does + not equal u(x). + >>> solution(lambda n: n ** 3, 3) + 74 + """ + data_points: List[int] = [func(x_val) for x_val in range(1, order + 1)] + + polynomials: List[Callable[[int], int]] = [ + interpolate(data_points[:max_coeff]) for max_coeff in range(1, order + 1) + ] + + ret: int = 0 + poly: int + x_val: int + + for poly in polynomials: + x_val = 1 + while func(x_val) == poly(x_val): + x_val += 1 + + ret += poly(x_val) + + return ret + + +if __name__ == "__main__": + print(f"{solution() = }") From 3db9ef8ab9c1ec796662f8084f1892b8d77243ed Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Wed, 23 Dec 2020 15:22:43 +0530 Subject: [PATCH 135/195] Fix mypy errors for arithmetic analysis algorithms (#4053) --- arithmetic_analysis/in_static_equilibrium.py | 11 +-- arithmetic_analysis/lu_decomposition.py | 70 +++++++++++++------ .../newton_forward_interpolation.py | 7 +- arithmetic_analysis/newton_raphson.py | 5 +- arithmetic_analysis/secant_method.py | 57 +++++++-------- 5 files changed, 90 insertions(+), 60 deletions(-) diff --git a/arithmetic_analysis/in_static_equilibrium.py b/arithmetic_analysis/in_static_equilibrium.py index f08b39c3505c..9b2892151850 100644 --- a/arithmetic_analysis/in_static_equilibrium.py +++ b/arithmetic_analysis/in_static_equilibrium.py @@ -1,19 +1,14 @@ """ Checks if a system of forces is in static equilibrium. - -python/black : true -flake8 : passed -mypy : passed """ +from typing import List -from __future__ import annotations - -from numpy import array, cos, cross, radians, sin # type: ignore +from numpy import array, cos, cross, radians, sin def polar_force( magnitude: float, angle: float, radian_mode: bool = False -) -> list[float]: +) -> List[float]: """ Resolves force along rectangular components. (force, angle) => (force_x, force_y) diff --git a/arithmetic_analysis/lu_decomposition.py b/arithmetic_analysis/lu_decomposition.py index 763ba60f32b7..ef37d1b7b4ef 100644 --- a/arithmetic_analysis/lu_decomposition.py +++ b/arithmetic_analysis/lu_decomposition.py @@ -1,34 +1,64 @@ -"""Lower-Upper (LU) Decomposition.""" +"""Lower-Upper (LU) Decomposition. -# lower–upper (LU) decomposition - https://en.wikipedia.org/wiki/LU_decomposition -import numpy +Reference: +- https://en.wikipedia.org/wiki/LU_decomposition +""" +from typing import Tuple +import numpy as np +from numpy import ndarray -def LUDecompose(table): + +def lower_upper_decomposition(table: ndarray) -> Tuple[ndarray, ndarray]: + """Lower-Upper (LU) Decomposition + + Example: + + >>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) + >>> outcome = lower_upper_decomposition(matrix) + >>> outcome[0] + array([[1. , 0. , 0. ], + [0. , 1. , 0. ], + [2.5, 8. , 1. ]]) + >>> outcome[1] + array([[ 2. , -2. , 1. ], + [ 0. , 1. , 2. ], + [ 0. , 0. , -17.5]]) + + >>> matrix = np.array([[2, -2, 1], [0, 1, 2]]) + >>> lower_upper_decomposition(matrix) + Traceback (most recent call last): + ... + ValueError: 'table' has to be of square shaped array but got a 2x3 array: + [[ 2 -2 1] + [ 0 1 2]] + """ # Table that contains our data # Table has to be a square array so we need to check first - rows, columns = numpy.shape(table) - L = numpy.zeros((rows, columns)) - U = numpy.zeros((rows, columns)) + rows, columns = np.shape(table) if rows != columns: - return [] + raise ValueError( + f"'table' has to be of square shaped array but got a {rows}x{columns} " + + f"array:\n{table}" + ) + lower = np.zeros((rows, columns)) + upper = np.zeros((rows, columns)) for i in range(columns): for j in range(i): - sum = 0 + total = 0 for k in range(j): - sum += L[i][k] * U[k][j] - L[i][j] = (table[i][j] - sum) / U[j][j] - L[i][i] = 1 + total += lower[i][k] * upper[k][j] + lower[i][j] = (table[i][j] - total) / upper[j][j] + lower[i][i] = 1 for j in range(i, columns): - sum1 = 0 + total = 0 for k in range(i): - sum1 += L[i][k] * U[k][j] - U[i][j] = table[i][j] - sum1 - return L, U + total += lower[i][k] * upper[k][j] + upper[i][j] = table[i][j] - total + return lower, upper if __name__ == "__main__": - matrix = numpy.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]]) - L, U = LUDecompose(matrix) - print(L) - print(U) + import doctest + + doctest.testmod() diff --git a/arithmetic_analysis/newton_forward_interpolation.py b/arithmetic_analysis/newton_forward_interpolation.py index d32e3efbd1f2..66cde4b73c4f 100644 --- a/arithmetic_analysis/newton_forward_interpolation.py +++ b/arithmetic_analysis/newton_forward_interpolation.py @@ -1,10 +1,11 @@ # https://www.geeksforgeeks.org/newton-forward-backward-interpolation/ import math +from typing import List # for calculating u value -def ucal(u, p): +def ucal(u: float, p: int) -> float: """ >>> ucal(1, 2) 0 @@ -19,9 +20,9 @@ def ucal(u, p): return temp -def main(): +def main() -> None: n = int(input("enter the numbers of values: ")) - y = [] + y: List[List[float]] = [] for i in range(n): y.append([]) for i in range(n): diff --git a/arithmetic_analysis/newton_raphson.py b/arithmetic_analysis/newton_raphson.py index 948759a09a2a..146bb0aa5adf 100644 --- a/arithmetic_analysis/newton_raphson.py +++ b/arithmetic_analysis/newton_raphson.py @@ -4,11 +4,14 @@ # quickly find a good approximation for the root of a real-valued function from decimal import Decimal from math import * # noqa: F401, F403 +from typing import Union from sympy import diff -def newton_raphson(func: str, a: int, precision: int = 10 ** -10) -> float: +def newton_raphson( + func: str, a: Union[float, Decimal], precision: float = 10 ** -10 +) -> float: """Finds root from the point 'a' onwards by Newton-Raphson method >>> newton_raphson("sin(x)", 2) 3.1415926536808043 diff --git a/arithmetic_analysis/secant_method.py b/arithmetic_analysis/secant_method.py index b05d44c627d8..7eb1dd8f5c6b 100644 --- a/arithmetic_analysis/secant_method.py +++ b/arithmetic_analysis/secant_method.py @@ -1,28 +1,29 @@ -# Implementing Secant method in Python -# Author: dimgrichr - - -from math import exp - - -def f(x): - """ - >>> f(5) - 39.98652410600183 - """ - return 8 * x - 2 * exp(-x) - - -def SecantMethod(lower_bound, upper_bound, repeats): - """ - >>> SecantMethod(1, 3, 2) - 0.2139409276214589 - """ - x0 = lower_bound - x1 = upper_bound - for i in range(0, repeats): - x0, x1 = x1, x1 - (f(x1) * (x1 - x0)) / (f(x1) - f(x0)) - return x1 - - -print(f"The solution is: {SecantMethod(1, 3, 2)}") +""" +Implementing Secant method in Python +Author: dimgrichr +""" +from math import exp + + +def f(x: float) -> float: + """ + >>> f(5) + 39.98652410600183 + """ + return 8 * x - 2 * exp(-x) + + +def secant_method(lower_bound: float, upper_bound: float, repeats: int) -> float: + """ + >>> secant_method(1, 3, 2) + 0.2139409276214589 + """ + x0 = lower_bound + x1 = upper_bound + for i in range(0, repeats): + x0, x1 = x1, x1 - (f(x1) * (x1 - x0)) / (f(x1) - f(x0)) + return x1 + + +if __name__ == "__main__": + print(f"Example: {secant_method(1, 3, 2) = }") From 755ee3a66b2a4b48129e8e89a496fa4b2a8dfc92 Mon Sep 17 00:00:00 2001 From: fpringle Date: Wed, 23 Dec 2020 18:48:19 +0100 Subject: [PATCH 136/195] Add solution for Project Euler problem 102 (#4051) * Added solution for Project Euler problem 102 * Got rid of map functions * Snake case variable names * Type hints * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 6 +- project_euler/problem_102/__init__.py | 0 project_euler/problem_102/p102_triangles.txt | 1000 ++++++++++++++++++ project_euler/problem_102/sol1.py | 81 ++ project_euler/problem_102/test_triangles.txt | 2 + 5 files changed, 1088 insertions(+), 1 deletion(-) create mode 100644 project_euler/problem_102/__init__.py create mode 100644 project_euler/problem_102/p102_triangles.txt create mode 100644 project_euler/problem_102/sol1.py create mode 100644 project_euler/problem_102/test_triangles.txt diff --git a/DIRECTORY.md b/DIRECTORY.md index 1f1bb9907e52..d73ae11eb7c2 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -106,7 +106,7 @@ * [Molecular Chemistry](https://github.com/TheAlgorithms/Python/blob/master/conversions/molecular_chemistry.py) * [Octal To Decimal](https://github.com/TheAlgorithms/Python/blob/master/conversions/octal_to_decimal.py) * [Prefix Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/prefix_conversions.py) - * [Roman To Integer](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_to_integer.py) + * [Roman Numerals](https://github.com/TheAlgorithms/Python/blob/master/conversions/roman_numerals.py) * [Temperature Conversions](https://github.com/TheAlgorithms/Python/blob/master/conversions/temperature_conversions.py) * [Weight Conversion](https://github.com/TheAlgorithms/Python/blob/master/conversions/weight_conversion.py) @@ -746,6 +746,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_099/sol1.py) * Problem 101 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_101/sol1.py) + * Problem 102 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_102/sol1.py) * Problem 112 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_112/sol1.py) * Problem 113 @@ -760,6 +762,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_125/sol1.py) * Problem 129 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_129/sol1.py) + * Problem 135 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_135/sol1.py) * Problem 173 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_173/sol1.py) * Problem 174 diff --git a/project_euler/problem_102/__init__.py b/project_euler/problem_102/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_102/p102_triangles.txt b/project_euler/problem_102/p102_triangles.txt new file mode 100644 index 000000000000..3f01a1ac1f41 --- /dev/null +++ b/project_euler/problem_102/p102_triangles.txt @@ -0,0 +1,1000 @@ +-340,495,-153,-910,835,-947 +-175,41,-421,-714,574,-645 +-547,712,-352,579,951,-786 +419,-864,-83,650,-399,171 +-429,-89,-357,-930,296,-29 +-734,-702,823,-745,-684,-62 +-971,762,925,-776,-663,-157 +162,570,628,485,-807,-896 +641,91,-65,700,887,759 +215,-496,46,-931,422,-30 +-119,359,668,-609,-358,-494 +440,929,968,214,760,-857 +-700,785,838,29,-216,411 +-770,-458,-325,-53,-505,633 +-752,-805,349,776,-799,687 +323,5,561,-36,919,-560 +-907,358,264,320,204,274 +-728,-466,350,969,292,-345 +940,836,272,-533,748,185 +411,998,813,520,316,-949 +-152,326,658,-762,148,-651 +330,507,-9,-628,101,174 +551,-496,772,-541,-702,-45 +-164,-489,-90,322,631,-59 +673,366,-4,-143,-606,-704 +428,-609,801,-449,740,-269 +453,-924,-785,-346,-853,111 +-738,555,-181,467,-426,-20 +958,-692,784,-343,505,-569 +620,27,263,54,-439,-726 +804,87,998,859,871,-78 +-119,-453,-709,-292,-115,-56 +-626,138,-940,-476,-177,-274 +-11,160,142,588,446,158 +538,727,550,787,330,810 +420,-689,854,-546,337,516 +872,-998,-607,748,473,-192 +653,440,-516,-985,808,-857 +374,-158,331,-940,-338,-641 +137,-925,-179,771,734,-715 +-314,198,-115,29,-641,-39 +759,-574,-385,355,590,-603 +-189,-63,-168,204,289,305 +-182,-524,-715,-621,911,-255 +331,-816,-833,471,168,126 +-514,581,-855,-220,-731,-507 +129,169,576,651,-87,-458 +783,-444,-881,658,-266,298 +603,-430,-598,585,368,899 +43,-724,962,-376,851,409 +-610,-646,-883,-261,-482,-881 +-117,-237,978,641,101,-747 +579,125,-715,-712,208,534 +672,-214,-762,372,874,533 +-564,965,38,715,367,242 +500,951,-700,-981,-61,-178 +-382,-224,-959,903,-282,-60 +-355,295,426,-331,-591,655 +892,128,958,-271,-993,274 +-454,-619,302,138,-790,-874 +-642,601,-574,159,-290,-318 +266,-109,257,-686,54,975 +162,628,-478,840,264,-266 +466,-280,982,1,904,-810 +721,839,730,-807,777,981 +-129,-430,748,263,943,96 +434,-94,410,-990,249,-704 +237,42,122,-732,44,-51 +909,-116,-229,545,292,717 +824,-768,-807,-370,-262,30 +675,58,332,-890,-651,791 +363,825,-717,254,684,240 +405,-715,900,166,-589,422 +-476,686,-830,-319,634,-807 +633,837,-971,917,-764,207 +-116,-44,-193,-70,908,809 +-26,-252,998,408,70,-713 +-601,645,-462,842,-644,-591 +-160,653,274,113,-138,687 +369,-273,-181,925,-167,-693 +-338,135,480,-967,-13,-840 +-90,-270,-564,695,161,907 +607,-430,869,-713,461,-469 +919,-165,-776,522,606,-708 +-203,465,288,207,-339,-458 +-453,-534,-715,975,838,-677 +-973,310,-350,934,546,-805 +-835,385,708,-337,-594,-772 +-14,914,900,-495,-627,594 +833,-713,-213,578,-296,699 +-27,-748,484,455,915,291 +270,889,739,-57,442,-516 +119,811,-679,905,184,130 +-678,-469,925,553,612,482 +101,-571,-732,-842,644,588 +-71,-737,566,616,957,-663 +-634,-356,90,-207,936,622 +598,443,964,-895,-58,529 +847,-467,929,-742,91,10 +-633,829,-780,-408,222,-30 +-818,57,275,-38,-746,198 +-722,-825,-549,597,-391,99 +-570,908,430,873,-103,-360 +342,-681,512,434,542,-528 +297,850,479,609,543,-357 +9,784,212,548,56,859 +-152,560,-240,-969,-18,713 +140,-133,34,-635,250,-163 +-272,-22,-169,-662,989,-604 +471,-765,355,633,-742,-118 +-118,146,942,663,547,-376 +583,16,162,264,715,-33 +-230,-446,997,-838,561,555 +372,397,-729,-318,-276,649 +92,982,-970,-390,-922,922 +-981,713,-951,-337,-669,670 +-999,846,-831,-504,7,-128 +455,-954,-370,682,-510,45 +822,-960,-892,-385,-662,314 +-668,-686,-367,-246,530,-341 +-723,-720,-926,-836,-142,757 +-509,-134,384,-221,-873,-639 +-803,-52,-706,-669,373,-339 +933,578,631,-616,770,555 +741,-564,-33,-605,-576,275 +-715,445,-233,-730,734,-704 +120,-10,-266,-685,-490,-17 +-232,-326,-457,-946,-457,-116 +811,52,639,826,-200,147 +-329,279,293,612,943,955 +-721,-894,-393,-969,-642,453 +-688,-826,-352,-75,371,79 +-809,-979,407,497,858,-248 +-485,-232,-242,-582,-81,849 +141,-106,123,-152,806,-596 +-428,57,-992,811,-192,478 +864,393,122,858,255,-876 +-284,-780,240,457,354,-107 +956,605,-477,44,26,-678 +86,710,-533,-815,439,327 +-906,-626,-834,763,426,-48 +201,-150,-904,652,475,412 +-247,149,81,-199,-531,-148 +923,-76,-353,175,-121,-223 +427,-674,453,472,-410,585 +931,776,-33,85,-962,-865 +-655,-908,-902,208,869,792 +-316,-102,-45,-436,-222,885 +-309,768,-574,653,745,-975 +896,27,-226,993,332,198 +323,655,-89,260,240,-902 +501,-763,-424,793,813,616 +993,375,-938,-621,672,-70 +-880,-466,-283,770,-824,143 +63,-283,886,-142,879,-116 +-964,-50,-521,-42,-306,-161 +724,-22,866,-871,933,-383 +-344,135,282,966,-80,917 +-281,-189,420,810,362,-582 +-515,455,-588,814,162,332 +555,-436,-123,-210,869,-943 +589,577,232,286,-554,876 +-773,127,-58,-171,-452,125 +-428,575,906,-232,-10,-224 +437,276,-335,-348,605,878 +-964,511,-386,-407,168,-220 +307,513,912,-463,-423,-416 +-445,539,273,886,-18,760 +-396,-585,-670,414,47,364 +143,-506,754,906,-971,-203 +-544,472,-180,-541,869,-465 +-779,-15,-396,890,972,-220 +-430,-564,503,182,-119,456 +89,-10,-739,399,506,499 +954,162,-810,-973,127,870 +890,952,-225,158,828,237 +-868,952,349,465,574,750 +-915,369,-975,-596,-395,-134 +-135,-601,575,582,-667,640 +413,890,-560,-276,-555,-562 +-633,-269,561,-820,-624,499 +371,-92,-784,-593,864,-717 +-971,655,-439,367,754,-951 +172,-347,36,279,-247,-402 +633,-301,364,-349,-683,-387 +-780,-211,-713,-948,-648,543 +72,58,762,-465,-66,462 +78,502,781,-832,713,836 +-431,-64,-484,-392,208,-343 +-64,101,-29,-860,-329,844 +398,391,828,-858,700,395 +578,-896,-326,-604,314,180 +97,-321,-695,185,-357,852 +854,839,283,-375,951,-209 +194,96,-564,-847,162,524 +-354,532,494,621,580,560 +419,-678,-450,926,-5,-924 +-661,905,519,621,-143,394 +-573,268,296,-562,-291,-319 +-211,266,-196,158,564,-183 +18,-585,-398,777,-581,864 +790,-894,-745,-604,-418,70 +848,-339,150,773,11,851 +-954,-809,-53,-20,-648,-304 +658,-336,-658,-905,853,407 +-365,-844,350,-625,852,-358 +986,-315,-230,-159,21,180 +-15,599,45,-286,-941,847 +-613,-68,184,639,-987,550 +334,675,-56,-861,923,340 +-848,-596,960,231,-28,-34 +707,-811,-994,-356,-167,-171 +-470,-764,72,576,-600,-204 +379,189,-542,-576,585,800 +440,540,-445,-563,379,-334 +-155,64,514,-288,853,106 +-304,751,481,-520,-708,-694 +-709,132,594,126,-844,63 +723,471,421,-138,-962,892 +-440,-263,39,513,-672,-954 +775,809,-581,330,752,-107 +-376,-158,335,-708,-514,578 +-343,-769,456,-187,25,413 +548,-877,-172,300,-500,928 +938,-102,423,-488,-378,-969 +-36,564,-55,131,958,-800 +-322,511,-413,503,700,-847 +-966,547,-88,-17,-359,-67 +637,-341,-437,-181,527,-153 +-74,449,-28,3,485,189 +-997,658,-224,-948,702,-807 +-224,736,-896,127,-945,-850 +-395,-106,439,-553,-128,124 +-841,-445,-758,-572,-489,212 +633,-327,13,-512,952,771 +-940,-171,-6,-46,-923,-425 +-142,-442,-817,-998,843,-695 +340,847,-137,-920,-988,-658 +-653,217,-679,-257,651,-719 +-294,365,-41,342,74,-892 +690,-236,-541,494,408,-516 +180,-807,225,790,494,59 +707,605,-246,656,284,271 +65,294,152,824,442,-442 +-321,781,-540,341,316,415 +420,371,-2,545,995,248 +56,-191,-604,971,615,449 +-981,-31,510,592,-390,-362 +-317,-968,913,365,97,508 +832,63,-864,-510,86,202 +-483,456,-636,340,-310,676 +981,-847,751,-508,-962,-31 +-157,99,73,797,63,-172 +220,858,872,924,866,-381 +996,-169,805,321,-164,971 +896,11,-625,-973,-782,76 +578,-280,730,-729,307,-905 +-580,-749,719,-698,967,603 +-821,874,-103,-623,662,-491 +-763,117,661,-644,672,-607 +592,787,-798,-169,-298,690 +296,644,-526,-762,-447,665 +534,-818,852,-120,57,-379 +-986,-549,-329,294,954,258 +-133,352,-660,-77,904,-356 +748,343,215,500,317,-277 +311,7,910,-896,-809,795 +763,-602,-753,313,-352,917 +668,619,-474,-597,-650,650 +-297,563,-701,-987,486,-902 +-461,-740,-657,233,-482,-328 +-446,-250,-986,-458,-629,520 +542,-49,-327,-469,257,-947 +121,-575,-634,-143,-184,521 +30,504,455,-645,-229,-945 +-12,-295,377,764,771,125 +-686,-133,225,-25,-376,-143 +-6,-46,338,270,-405,-872 +-623,-37,582,467,963,898 +-804,869,-477,420,-475,-303 +94,41,-842,-193,-768,720 +-656,-918,415,645,-357,460 +-47,-486,-911,468,-608,-686 +-158,251,419,-394,-655,-895 +272,-695,979,508,-358,959 +-776,650,-918,-467,-690,-534 +-85,-309,-626,167,-366,-429 +-880,-732,-186,-924,970,-875 +517,645,-274,962,-804,544 +721,402,104,640,478,-499 +198,684,-134,-723,-452,-905 +-245,745,239,238,-826,441 +-217,206,-32,462,-981,-895 +-51,989,526,-173,560,-676 +-480,-659,-976,-580,-727,466 +-996,-90,-995,158,-239,642 +302,288,-194,-294,17,924 +-943,969,-326,114,-500,103 +-619,163,339,-880,230,421 +-344,-601,-795,557,565,-779 +590,345,-129,-202,-125,-58 +-777,-195,159,674,775,411 +-939,312,-665,810,121,855 +-971,254,712,815,452,581 +442,-9,327,-750,61,757 +-342,869,869,-160,390,-772 +620,601,565,-169,-69,-183 +-25,924,-817,964,321,-970 +-64,-6,-133,978,825,-379 +601,436,-24,98,-115,940 +-97,502,614,-574,922,513 +-125,262,-946,695,99,-220 +429,-721,719,-694,197,-558 +326,689,-70,-908,-673,338 +-468,-856,-902,-254,-358,305 +-358,530,542,355,-253,-47 +-438,-74,-362,963,988,788 +137,717,467,622,319,-380 +-86,310,-336,851,918,-288 +721,395,646,-53,255,-425 +255,175,912,84,-209,878 +-632,-485,-400,-357,991,-608 +235,-559,992,-297,857,-591 +87,-71,148,130,647,578 +-290,-584,-639,-788,-21,592 +386,984,625,-731,-993,-336 +-538,634,-209,-828,-150,-774 +-754,-387,607,-781,976,-199 +412,-798,-664,295,709,-537 +-412,932,-880,-232,561,852 +-656,-358,-198,-964,-433,-848 +-762,-668,-632,186,-673,-11 +-876,237,-282,-312,-83,682 +403,73,-57,-436,-622,781 +-587,873,798,976,-39,329 +-369,-622,553,-341,817,794 +-108,-616,920,-849,-679,96 +290,-974,234,239,-284,-321 +-22,394,-417,-419,264,58 +-473,-551,69,923,591,-228 +-956,662,-113,851,-581,-794 +-258,-681,413,-471,-637,-817 +-866,926,992,-653,-7,794 +556,-350,602,917,831,-610 +188,245,-906,361,492,174 +-720,384,-818,329,638,-666 +-246,846,890,-325,-59,-850 +-118,-509,620,-762,-256,15 +-787,-536,-452,-338,-399,813 +458,560,525,-311,-608,-419 +494,-811,-825,-127,-812,894 +-801,890,-629,-860,574,925 +-709,-193,-213,138,-410,-403 +861,91,708,-187,5,-222 +789,646,777,154,90,-49 +-267,-830,-114,531,591,-698 +-126,-82,881,-418,82,652 +-894,130,-726,-935,393,-815 +-142,563,654,638,-712,-597 +-759,60,-23,977,100,-765 +-305,595,-570,-809,482,762 +-161,-267,53,963,998,-529 +-300,-57,798,353,703,486 +-990,696,-764,699,-565,719 +-232,-205,566,571,977,369 +740,865,151,-817,-204,-293 +94,445,-768,229,537,-406 +861,620,37,-424,-36,656 +390,-369,952,733,-464,569 +-482,-604,959,554,-705,-626 +-396,-615,-991,108,272,-723 +143,780,535,142,-917,-147 +138,-629,-217,-908,905,115 +915,103,-852,64,-468,-642 +570,734,-785,-268,-326,-759 +738,531,-332,586,-779,24 +870,440,-217,473,-383,415 +-296,-333,-330,-142,-924,950 +118,120,-35,-245,-211,-652 +61,634,153,-243,838,789 +726,-582,210,105,983,537 +-313,-323,758,234,29,848 +-847,-172,-593,733,-56,617 +54,255,-512,156,-575,675 +-873,-956,-148,623,95,200 +700,-370,926,649,-978,157 +-639,-202,719,130,747,222 +194,-33,955,943,505,114 +-226,-790,28,-930,827,783 +-392,-74,-28,714,218,-612 +209,626,-888,-683,-912,495 +487,751,614,933,631,445 +-348,-34,-411,-106,835,321 +-689,872,-29,-800,312,-542 +-52,566,827,570,-862,-77 +471,992,309,-402,389,912 +24,520,-83,-51,555,503 +-265,-317,283,-970,-472,690 +606,526,137,71,-651,150 +217,-518,663,66,-605,-331 +-562,232,-76,-503,205,-323 +842,-521,546,285,625,-186 +997,-927,344,909,-546,974 +-677,419,81,121,-705,771 +719,-379,-944,-797,784,-155 +-378,286,-317,-797,-111,964 +-288,-573,784,80,-532,-646 +-77,407,-248,-797,769,-816 +-24,-637,287,-858,-927,-333 +-902,37,894,-823,141,684 +125,467,-177,-516,686,399 +-321,-542,641,-590,527,-224 +-400,-712,-876,-208,632,-543 +-676,-429,664,-242,-269,922 +-608,-273,-141,930,687,380 +786,-12,498,494,310,326 +-739,-617,606,-960,804,188 +384,-368,-243,-350,-459,31 +-550,397,320,-868,328,-279 +969,-179,853,864,-110,514 +910,793,302,-822,-285,488 +-605,-128,218,-283,-17,-227 +16,324,667,708,750,3 +485,-813,19,585,71,930 +-218,816,-687,-97,-732,-360 +-497,-151,376,-23,3,315 +-412,-989,-610,-813,372,964 +-878,-280,87,381,-311,69 +-609,-90,-731,-679,150,585 +889,27,-162,605,75,-770 +448,617,-988,0,-103,-504 +-800,-537,-69,627,608,-668 +534,686,-664,942,830,920 +-238,775,495,932,-793,497 +-343,958,-914,-514,-691,651 +568,-136,208,359,728,28 +286,912,-794,683,556,-102 +-638,-629,-484,445,-64,-497 +58,505,-801,-110,872,632 +-390,777,353,267,976,369 +-993,515,105,-133,358,-572 +964,996,355,-212,-667,38 +-725,-614,-35,365,132,-196 +237,-536,-416,-302,312,477 +-664,574,-210,224,48,-925 +869,-261,-256,-240,-3,-698 +712,385,32,-34,916,-315 +895,-409,-100,-346,728,-624 +-806,327,-450,889,-781,-939 +-586,-403,698,318,-939,899 +557,-57,-920,659,333,-51 +-441,232,-918,-205,246,1 +783,167,-797,-595,245,-736 +-36,-531,-486,-426,-813,-160 +777,-843,817,313,-228,-572 +735,866,-309,-564,-81,190 +-413,645,101,719,-719,218 +-83,164,767,796,-430,-459 +122,779,-15,-295,-96,-892 +462,379,70,548,834,-312 +-630,-534,124,187,-737,114 +-299,-604,318,-591,936,826 +-879,218,-642,-483,-318,-866 +-691,62,-658,761,-895,-854 +-822,493,687,569,910,-202 +-223,784,304,-5,541,925 +-914,541,737,-662,-662,-195 +-622,615,414,358,881,-878 +339,745,-268,-968,-280,-227 +-364,855,148,-709,-827,472 +-890,-532,-41,664,-612,577 +-702,-859,971,-722,-660,-920 +-539,-605,737,149,973,-802 +800,42,-448,-811,152,511 +-933,377,-110,-105,-374,-937 +-766,152,482,120,-308,390 +-568,775,-292,899,732,890 +-177,-317,-502,-259,328,-511 +612,-696,-574,-660,132,31 +-119,563,-805,-864,179,-672 +425,-627,183,-331,839,318 +-711,-976,-749,152,-916,261 +181,-63,497,211,262,406 +-537,700,-859,-765,-928,77 +892,832,231,-749,-82,613 +816,216,-642,-216,-669,-912 +-6,624,-937,-370,-344,268 +737,-710,-869,983,-324,-274 +565,952,-547,-158,374,-444 +51,-683,645,-845,515,636 +-953,-631,114,-377,-764,-144 +-8,470,-242,-399,-675,-730 +-540,689,-20,47,-607,590 +-329,-710,-779,942,-388,979 +123,829,674,122,203,563 +46,782,396,-33,386,610 +872,-846,-523,-122,-55,-190 +388,-994,-525,974,127,596 +781,-680,796,-34,-959,-62 +-749,173,200,-384,-745,-446 +379,618,136,-250,-224,970 +-58,240,-921,-760,-901,-626 +366,-185,565,-100,515,688 +489,999,-893,-263,-637,816 +838,-496,-316,-513,419,479 +107,676,-15,882,98,-397 +-999,941,-903,-424,670,-325 +171,-979,835,178,169,-984 +-609,-607,378,-681,184,402 +-316,903,-575,-800,224,983 +591,-18,-460,551,-167,918 +-756,405,-117,441,163,-320 +456,24,6,881,-836,-539 +-489,-585,915,651,-892,-382 +-177,-122,73,-711,-386,591 +181,724,530,686,-131,241 +737,288,886,216,233,33 +-548,-386,-749,-153,-85,-982 +-835,227,904,160,-99,25 +-9,-42,-162,728,840,-963 +217,-763,870,771,47,-846 +-595,808,-491,556,337,-900 +-134,281,-724,441,-134,708 +-789,-508,651,-962,661,315 +-839,-923,339,402,41,-487 +300,-790,48,703,-398,-811 +955,-51,462,-685,960,-717 +910,-880,592,-255,-51,-776 +-885,169,-793,368,-565,458 +-905,940,-492,-630,-535,-988 +245,797,763,869,-82,550 +-310,38,-933,-367,-650,824 +-95,32,-83,337,226,990 +-218,-975,-191,-208,-785,-293 +-672,-953,517,-901,-247,465 +681,-148,261,-857,544,-923 +640,341,446,-618,195,769 +384,398,-846,365,671,815 +578,576,-911,907,762,-859 +548,-428,144,-630,-759,-146 +710,-73,-700,983,-97,-889 +-46,898,-973,-362,-817,-717 +151,-81,-125,-900,-478,-154 +483,615,-537,-932,181,-68 +786,-223,518,25,-306,-12 +-422,268,-809,-683,635,468 +983,-734,-694,-608,-110,4 +-786,-196,749,-354,137,-8 +-181,36,668,-200,691,-973 +-629,-838,692,-736,437,-871 +-208,-536,-159,-596,8,197 +-3,370,-686,170,913,-376 +44,-998,-149,-993,-200,512 +-519,136,859,497,536,434 +77,-985,972,-340,-705,-837 +-381,947,250,360,344,322 +-26,131,699,750,707,384 +-914,655,299,193,406,955 +-883,-921,220,595,-546,794 +-599,577,-569,-404,-704,489 +-594,-963,-624,-460,880,-760 +-603,88,-99,681,55,-328 +976,472,139,-453,-531,-860 +192,-290,513,-89,666,432 +417,487,575,293,567,-668 +655,711,-162,449,-980,972 +-505,664,-685,-239,603,-592 +-625,-802,-67,996,384,-636 +365,-593,522,-666,-200,-431 +-868,708,560,-860,-630,-355 +-702,785,-637,-611,-597,960 +-137,-696,-93,-803,408,406 +891,-123,-26,-609,-610,518 +133,-832,-198,555,708,-110 +791,617,-69,487,696,315 +-900,694,-565,517,-269,-416 +914,135,-781,600,-71,-600 +991,-915,-422,-351,-837,313 +-840,-398,-302,21,590,146 +62,-558,-702,-384,-625,831 +-363,-426,-924,-496,792,-908 +73,361,-817,-466,400,922 +-626,-164,-626,860,-524,286 +255,26,-944,809,-606,986 +-457,-256,-103,50,-867,-871 +-223,803,196,480,612,136 +-820,-928,700,780,-977,721 +717,332,53,-933,-128,793 +-602,-648,562,593,890,702 +-469,-875,-527,911,-475,-222 +110,-281,-552,-536,-816,596 +-981,654,413,-981,-75,-95 +-754,-742,-515,894,-220,-344 +795,-52,156,408,-603,76 +474,-157,423,-499,-807,-791 +260,688,40,-52,702,-122 +-584,-517,-390,-881,302,-504 +61,797,665,708,14,668 +366,166,458,-614,564,-983 +72,539,-378,796,381,-824 +-485,201,-588,842,736,379 +-149,-894,-298,705,-303,-406 +660,-935,-580,521,93,633 +-382,-282,-375,-841,-828,171 +-567,743,-100,43,144,122 +-281,-786,-749,-551,296,304 +11,-426,-792,212,857,-175 +594,143,-699,289,315,137 +341,596,-390,107,-631,-804 +-751,-636,-424,-854,193,651 +-145,384,749,675,-786,517 +224,-865,-323,96,-916,258 +-309,403,-388,826,35,-270 +-942,709,222,158,-699,-103 +-589,842,-997,29,-195,-210 +264,426,566,145,-217,623 +217,965,507,-601,-453,507 +-206,307,-982,4,64,-292 +676,-49,-38,-701,550,883 +5,-850,-438,659,745,-773 +933,238,-574,-570,91,-33 +-866,121,-928,358,459,-843 +-568,-631,-352,-580,-349,189 +-737,849,-963,-486,-662,970 +135,334,-967,-71,-365,-792 +789,21,-227,51,990,-275 +240,412,-886,230,591,256 +-609,472,-853,-754,959,661 +401,521,521,314,929,982 +-499,784,-208,71,-302,296 +-557,-948,-553,-526,-864,793 +270,-626,828,44,37,14 +-412,224,617,-593,502,699 +41,-908,81,562,-849,163 +165,917,761,-197,331,-341 +-687,314,799,755,-969,648 +-164,25,578,439,-334,-576 +213,535,874,-177,-551,24 +-689,291,-795,-225,-496,-125 +465,461,558,-118,-568,-909 +567,660,-810,46,-485,878 +-147,606,685,-690,-774,984 +568,-886,-43,854,-738,616 +-800,386,-614,585,764,-226 +-518,23,-225,-732,-79,440 +-173,-291,-689,636,642,-447 +-598,-16,227,410,496,211 +-474,-930,-656,-321,-420,36 +-435,165,-819,555,540,144 +-969,149,828,568,394,648 +65,-848,257,720,-625,-851 +981,899,275,635,465,-877 +80,290,792,760,-191,-321 +-605,-858,594,33,706,593 +585,-472,318,-35,354,-927 +-365,664,803,581,-965,-814 +-427,-238,-480,146,-55,-606 +879,-193,250,-890,336,117 +-226,-322,-286,-765,-836,-218 +-913,564,-667,-698,937,283 +872,-901,810,-623,-52,-709 +473,171,717,38,-429,-644 +225,824,-219,-475,-180,234 +-530,-797,-948,238,851,-623 +85,975,-363,529,598,28 +-799,166,-804,210,-769,851 +-687,-158,885,736,-381,-461 +447,592,928,-514,-515,-661 +-399,-777,-493,80,-544,-78 +-884,631,171,-825,-333,551 +191,268,-577,676,137,-33 +212,-853,709,798,583,-56 +-908,-172,-540,-84,-135,-56 +303,311,406,-360,-240,811 +798,-708,824,59,234,-57 +491,693,-74,585,-85,877 +509,-65,-936,329,-51,722 +-122,858,-52,467,-77,-609 +850,760,547,-495,-953,-952 +-460,-541,890,910,286,724 +-914,843,-579,-983,-387,-460 +989,-171,-877,-326,-899,458 +846,175,-915,540,-1000,-982 +-852,-920,-306,496,530,-18 +338,-991,160,85,-455,-661 +-186,-311,-460,-563,-231,-414 +-932,-302,959,597,793,748 +-366,-402,-788,-279,514,53 +-940,-956,447,-956,211,-285 +564,806,-911,-914,934,754 +575,-858,-277,15,409,-714 +848,462,100,-381,135,242 +330,718,-24,-190,860,-78 +479,458,941,108,-866,-653 +212,980,962,-962,115,841 +-827,-474,-206,881,323,765 +506,-45,-30,-293,524,-133 +832,-173,547,-852,-561,-842 +-397,-661,-708,819,-545,-228 +521,51,-489,852,36,-258 +227,-164,189,465,-987,-882 +-73,-997,641,-995,449,-615 +151,-995,-638,415,257,-400 +-663,-297,-748,537,-734,198 +-585,-401,-81,-782,-80,-105 +99,-21,238,-365,-704,-368 +45,416,849,-211,-371,-1 +-404,-443,795,-406,36,-933 +272,-363,981,-491,-380,77 +713,-342,-366,-849,643,911 +-748,671,-537,813,961,-200 +-194,-909,703,-662,-601,188 +281,500,724,286,267,197 +-832,847,-595,820,-316,637 +520,521,-54,261,923,-10 +4,-808,-682,-258,441,-695 +-793,-107,-969,905,798,446 +-108,-739,-590,69,-855,-365 +380,-623,-930,817,468,713 +759,-849,-236,433,-723,-931 +95,-320,-686,124,-69,-329 +-655,518,-210,-523,284,-866 +144,303,639,70,-171,269 +173,-333,947,-304,55,40 +274,878,-482,-888,-835,375 +-982,-854,-36,-218,-114,-230 +905,-979,488,-485,-479,114 +877,-157,553,-530,-47,-321 +350,664,-881,442,-220,-284 +434,-423,-365,878,-726,584 +535,909,-517,-447,-660,-141 +-966,191,50,353,182,-642 +-785,-634,123,-907,-162,511 +146,-850,-214,814,-704,25 +692,1,521,492,-637,274 +-662,-372,-313,597,983,-647 +-962,-526,68,-549,-819,231 +740,-890,-318,797,-666,948 +-190,-12,-468,-455,948,284 +16,478,-506,-888,628,-154 +272,630,-976,308,433,3 +-169,-391,-132,189,302,-388 +109,-784,474,-167,-265,-31 +-177,-532,283,464,421,-73 +650,635,592,-138,1,-387 +-932,703,-827,-492,-355,686 +586,-311,340,-618,645,-434 +-951,736,647,-127,-303,590 +188,444,903,718,-931,500 +-872,-642,-296,-571,337,241 +23,65,152,125,880,470 +512,823,-42,217,823,-263 +180,-831,-380,886,607,762 +722,443,-149,-216,-115,759 +-19,660,-36,901,923,231 +562,-322,-626,-968,194,-825 +204,-920,938,784,362,150 +-410,-266,-715,559,-672,124 +-198,446,-140,454,-461,-447 +83,-346,830,-493,-759,-382 +-881,601,581,234,-134,-925 +-494,914,-42,899,235,629 +-390,50,956,437,774,-700 +-514,514,44,-512,-576,-313 +63,-688,808,-534,-570,-399 +-726,572,-896,102,-294,-28 +-688,757,401,406,955,-511 +-283,423,-485,480,-767,908 +-541,952,-594,116,-854,451 +-273,-796,236,625,-626,257 +-407,-493,373,826,-309,297 +-750,955,-476,641,-809,713 +8,415,695,226,-111,2 +733,209,152,-920,401,995 +921,-103,-919,66,871,-947 +-907,89,-869,-214,851,-559 +-307,748,524,-755,314,-711 +188,897,-72,-763,482,103 +545,-821,-232,-596,-334,-754 +-217,-788,-820,388,-200,-662 +779,160,-723,-975,-142,-998 +-978,-519,-78,-981,842,904 +-504,-736,-295,21,-472,-482 +391,115,-705,574,652,-446 +813,-988,865,830,-263,487 +194,80,774,-493,-761,-872 +-415,-284,-803,7,-810,670 +-484,-4,881,-872,55,-852 +-379,822,-266,324,-48,748 +-304,-278,406,-60,959,-89 +404,756,577,-643,-332,658 +291,460,125,491,-312,83 +311,-734,-141,582,282,-557 +-450,-661,-981,710,-177,794 +328,264,-787,971,-743,-407 +-622,518,993,-241,-738,229 +273,-826,-254,-917,-710,-111 +809,770,96,368,-818,725 +-488,773,502,-342,534,745 +-28,-414,236,-315,-484,363 +179,-466,-566,713,-683,56 +560,-240,-597,619,916,-940 +893,473,872,-868,-642,-461 +799,489,383,-321,-776,-833 +980,490,-508,764,-512,-426 +917,961,-16,-675,440,559 +-812,212,784,-987,-132,554 +-886,454,747,806,190,231 +910,341,21,-66,708,725 +29,929,-831,-494,-303,389 +-103,492,-271,-174,-515,529 +-292,119,419,788,247,-951 +483,543,-347,-673,664,-549 +-926,-871,-437,337,162,-877 +299,472,-771,5,-88,-643 +-103,525,-725,-998,264,22 +-505,708,550,-545,823,347 +-738,931,59,147,-156,-259 +456,968,-162,889,132,-911 +535,120,968,-517,-864,-541 +24,-395,-593,-766,-565,-332 +834,611,825,-576,280,629 +211,-548,140,-278,-592,929 +-999,-240,-63,-78,793,573 +-573,160,450,987,529,322 +63,353,315,-187,-461,577 +189,-950,-247,656,289,241 +209,-297,397,664,-805,484 +-655,452,435,-556,917,874 +253,-756,262,-888,-778,-214 +793,-451,323,-251,-401,-458 +-396,619,-651,-287,-668,-781 +698,720,-349,742,-807,546 +738,280,680,279,-540,858 +-789,387,530,-36,-551,-491 +162,579,-427,-272,228,710 +689,356,917,-580,729,217 +-115,-638,866,424,-82,-194 +411,-338,-917,172,227,-29 +-612,63,630,-976,-64,-204 +-200,911,583,-571,682,-579 +91,298,396,-183,788,-955 +141,-873,-277,149,-396,916 +321,958,-136,573,541,-777 +797,-909,-469,-877,988,-653 +784,-198,129,883,-203,399 +-68,-810,223,-423,-467,-512 +531,-445,-603,-997,-841,641 +-274,-242,174,261,-636,-158 +-574,494,-796,-798,-798,99 +95,-82,-613,-954,-753,986 +-883,-448,-864,-401,938,-392 +913,930,-542,-988,310,410 +506,-99,43,512,790,-222 +724,31,49,-950,260,-134 +-287,-947,-234,-700,56,588 +-33,782,-144,948,105,-791 +548,-546,-652,-293,881,-520 +691,-91,76,991,-631,742 +-520,-429,-244,-296,724,-48 +778,646,377,50,-188,56 +-895,-507,-898,-165,-674,652 +654,584,-634,177,-349,-620 +114,-980,355,62,182,975 +516,9,-442,-298,274,-579 +-238,262,-431,-896,506,-850 +47,748,846,821,-537,-293 +839,726,593,285,-297,840 +634,-486,468,-304,-887,-567 +-864,914,296,-124,335,233 +88,-253,-523,-956,-554,803 +-587,417,281,-62,-409,-363 +-136,-39,-292,-768,-264,876 +-127,506,-891,-331,-744,-430 +778,584,-750,-129,-479,-94 +-876,-771,-987,-757,180,-641 +-777,-694,411,-87,329,190 +-347,-999,-882,158,-754,232 +-105,918,188,237,-110,-591 +-209,703,-838,77,838,909 +-995,-339,-762,750,860,472 +185,271,-289,173,811,-300 +2,65,-656,-22,36,-139 +765,-210,883,974,961,-905 +-212,295,-615,-840,77,474 +211,-910,-440,703,-11,859 +-559,-4,-196,841,-277,969 +-73,-159,-887,126,978,-371 +-569,633,-423,-33,512,-393 +503,143,-383,-109,-649,-998 +-663,339,-317,-523,-2,596 +690,-380,570,378,-652,132 +72,-744,-930,399,-525,935 +865,-983,115,37,995,826 +594,-621,-872,443,188,-241 +-1000,291,754,234,-435,-869 +-868,901,654,-907,59,181 +-868,-793,-431,596,-446,-564 +900,-944,-680,-796,902,-366 +331,430,943,853,-851,-942 +315,-538,-354,-909,139,721 +170,-884,-225,-818,-808,-657 +-279,-34,-533,-871,-972,552 +691,-986,-800,-950,654,-747 +603,988,899,841,-630,591 +876,-949,809,562,602,-536 +-693,363,-189,495,738,-1000 +-383,431,-633,297,665,959 +-740,686,-207,-803,188,-520 +-820,226,31,-339,10,121 +-312,-844,624,-516,483,621 +-822,-529,69,-278,800,328 +834,-82,-759,420,811,-264 +-960,-240,-921,561,173,46 +-324,909,-790,-814,-2,-785 +976,334,-290,-891,704,-581 +150,-798,689,-823,237,-639 +-551,-320,876,-502,-622,-628 +-136,845,904,595,-702,-261 +-857,-377,-522,-101,-943,-805 +-682,-787,-888,-459,-752,-985 +-571,-81,623,-133,447,643 +-375,-158,72,-387,-324,-696 +-660,-650,340,188,569,526 +727,-218,16,-7,-595,-988 +-966,-684,802,-783,-272,-194 +115,-566,-888,47,712,180 +-237,-69,45,-272,981,-812 +48,897,439,417,50,325 +348,616,180,254,104,-784 +-730,811,-548,612,-736,790 +138,-810,123,930,65,865 +-768,-299,-49,-895,-692,-418 +487,-531,802,-159,-12,634 +808,-179,552,-73,470,717 +720,-644,886,-141,625,144 +-485,-505,-347,-244,-916,66 +600,-565,995,-5,324,227 +-771,-35,904,-482,753,-303 +-701,65,426,-763,-504,-479 +409,733,-823,475,64,718 +865,975,368,893,-413,-433 +812,-597,-970,819,813,624 +193,-642,-381,-560,545,398 +711,28,-316,771,717,-865 +-509,462,809,-136,786,635 +618,-49,484,169,635,547 +-747,685,-882,-496,-332,82 +-501,-851,870,563,290,570 +-279,-829,-509,397,457,816 +-508,80,850,-188,483,-326 +860,-100,360,119,-205,787 +-870,21,-39,-827,-185,932 +826,284,-136,-866,-330,-97 +-944,-82,745,899,-97,365 +929,262,564,632,-115,632 +244,-276,713,330,-897,-214 +-890,-109,664,876,-974,-907 +716,249,816,489,723,141 +-96,-560,-272,45,-70,645 +762,-503,414,-828,-254,-646 +909,-13,903,-422,-344,-10 +658,-486,743,545,50,674 +-241,507,-367,18,-48,-241 +886,-268,884,-762,120,-486 +-412,-528,879,-647,223,-393 +851,810,234,937,-726,797 +-999,942,839,-134,-996,-189 +100,979,-527,-521,378,800 +544,-844,-832,-530,-77,-641 +43,889,31,442,-934,-503 +-330,-370,-309,-439,173,547 +169,945,62,-753,-542,-597 +208,751,-372,-647,-520,70 +765,-840,907,-257,379,918 +334,-135,-689,730,-427,618 +137,-508,66,-695,78,169 +-962,-123,400,-417,151,969 +328,689,666,427,-555,-642 +-907,343,605,-341,-647,582 +-667,-363,-571,818,-265,-399 +525,-938,904,898,725,692 +-176,-802,-858,-9,780,275 +580,170,-740,287,691,-97 +365,557,-375,361,-288,859 +193,737,842,-808,520,282 +-871,65,-799,836,179,-720 +958,-144,744,-789,797,-48 +122,582,662,912,68,757 +595,241,-801,513,388,186 +-103,-677,-259,-731,-281,-857 +921,319,-696,683,-88,-997 +775,200,78,858,648,768 +316,821,-763,68,-290,-741 +564,664,691,504,760,787 +694,-119,973,-385,309,-760 +777,-947,-57,990,74,19 +971,626,-496,-781,-602,-239 +-651,433,11,-339,939,294 +-965,-728,560,569,-708,-247 diff --git a/project_euler/problem_102/sol1.py b/project_euler/problem_102/sol1.py new file mode 100644 index 000000000000..00af726656ce --- /dev/null +++ b/project_euler/problem_102/sol1.py @@ -0,0 +1,81 @@ +""" +Three distinct points are plotted at random on a Cartesian plane, +for which -1000 ≤ x, y ≤ 1000, such that a triangle is formed. + +Consider the following two triangles: + +A(-340,495), B(-153,-910), C(835,-947) + +X(-175,41), Y(-421,-714), Z(574,-645) + +It can be verified that triangle ABC contains the origin, whereas +triangle XYZ does not. + +Using triangles.txt (right click and 'Save Link/Target As...'), a 27K text +file containing the coordinates of one thousand "random" triangles, find +the number of triangles for which the interior contains the origin. + +NOTE: The first two examples in the file represent the triangles in the +example given above. +""" + +from pathlib import Path +from typing import List, Tuple + + +def vector_product(point1: Tuple[int, int], point2: Tuple[int, int]) -> int: + """ + Return the 2-d vector product of two vectors. + >>> vector_product((1, 2), (-5, 0)) + 10 + >>> vector_product((3, 1), (6, 10)) + 24 + """ + return point1[0] * point2[1] - point1[1] * point2[0] + + +def contains_origin(x1: int, y1: int, x2: int, y2: int, x3: int, y3: int) -> bool: + """ + Check if the triangle given by the points A(x1, y1), B(x2, y2), C(x3, y3) + contains the origin. + >>> contains_origin(-340, 495, -153, -910, 835, -947) + True + >>> contains_origin(-175, 41, -421, -714, 574, -645) + False + """ + point_a: Tuple[int, int] = (x1, y1) + point_a_to_b: Tuple[int, int] = (x2 - x1, y2 - y1) + point_a_to_c: Tuple[int, int] = (x3 - x1, y3 - y1) + a: float = -vector_product(point_a, point_a_to_b) / vector_product( + point_a_to_c, point_a_to_b + ) + b: float = +vector_product(point_a, point_a_to_c) / vector_product( + point_a_to_c, point_a_to_b + ) + + return a > 0 and b > 0 and a + b < 1 + + +def solution(filename: str = "p102_triangles.txt") -> int: + """ + Find the number of triangles whose interior contains the origin. + >>> solution("test_triangles.txt") + 1 + """ + data: str = Path(__file__).parent.joinpath(filename).read_text(encoding="utf-8") + + triangles: List[List[int]] = [] + for line in data.strip().split("\n"): + triangles.append([int(number) for number in line.split(",")]) + + ret: int = 0 + triangle: List[int] + + for triangle in triangles: + ret += contains_origin(*triangle) + + return ret + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_102/test_triangles.txt b/project_euler/problem_102/test_triangles.txt new file mode 100644 index 000000000000..5c10cd651e9b --- /dev/null +++ b/project_euler/problem_102/test_triangles.txt @@ -0,0 +1,2 @@ +-340,495,-153,-910,835,-947 +-175,41,-421,-714,574,-645 From 6e772e48f028d6782cbb5ccb7d75b27b602cc980 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 24 Dec 2020 18:16:21 +0530 Subject: [PATCH 137/195] [mypy] Add/fix type annotations for backtracking algorithms (#4055) * Fix mypy errors for backtracking algorithms * Fix CI failure --- backtracking/all_subsequences.py | 28 ++++++-------- backtracking/coloring.py | 8 ++-- backtracking/hamiltonian_cycle.py | 8 ++-- backtracking/knight_tour.py | 12 +++--- backtracking/minimax.py | 26 ++++++------- backtracking/n_queens_math.py | 63 ++++++++++++++----------------- backtracking/sudoku.py | 59 ++++++++++++++++------------- 7 files changed, 98 insertions(+), 106 deletions(-) diff --git a/backtracking/all_subsequences.py b/backtracking/all_subsequences.py index 9086e3a3d659..99db4ea46589 100644 --- a/backtracking/all_subsequences.py +++ b/backtracking/all_subsequences.py @@ -1,12 +1,11 @@ -from typing import Any, List - """ - In this problem, we want to determine all possible subsequences - of the given sequence. We use backtracking to solve this problem. +In this problem, we want to determine all possible subsequences +of the given sequence. We use backtracking to solve this problem. - Time complexity: O(2^n), - where n denotes the length of the given sequence. +Time complexity: O(2^n), +where n denotes the length of the given sequence. """ +from typing import Any, List def generate_all_subsequences(sequence: List[Any]) -> None: @@ -32,15 +31,10 @@ def create_state_space_tree( current_subsequence.pop() -""" -remove the comment to take an input from the user - -print("Enter the elements") -sequence = list(map(int, input().split())) -""" - -sequence = [3, 1, 2, 4] -generate_all_subsequences(sequence) +if __name__ == "__main__": + seq: List[Any] = [3, 1, 2, 4] + generate_all_subsequences(seq) -sequence = ["A", "B", "C"] -generate_all_subsequences(sequence) + seq.clear() + seq.extend(["A", "B", "C"]) + generate_all_subsequences(seq) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index ceaffe3fae76..3956b21a9182 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -5,11 +5,11 @@ Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring """ -from __future__ import annotations +from typing import List def valid_coloring( - neighbours: list[int], colored_vertices: list[int], color: int + neighbours: List[int], colored_vertices: List[int], color: int ) -> bool: """ For each neighbour check if coloring constraint is satisfied @@ -35,7 +35,7 @@ def valid_coloring( def util_color( - graph: list[list[int]], max_colors: int, colored_vertices: list[int], index: int + graph: List[List[int]], max_colors: int, colored_vertices: List[int], index: int ) -> bool: """ Pseudo-Code @@ -86,7 +86,7 @@ def util_color( return False -def color(graph: list[list[int]], max_colors: int) -> list[int]: +def color(graph: List[List[int]], max_colors: int) -> List[int]: """ Wrapper function to call subroutine called util_color which will either return True or False. diff --git a/backtracking/hamiltonian_cycle.py b/backtracking/hamiltonian_cycle.py index bf15cce4aca4..7be1ea350d7c 100644 --- a/backtracking/hamiltonian_cycle.py +++ b/backtracking/hamiltonian_cycle.py @@ -6,11 +6,11 @@ Wikipedia: https://en.wikipedia.org/wiki/Hamiltonian_path """ -from __future__ import annotations +from typing import List def valid_connection( - graph: list[list[int]], next_ver: int, curr_ind: int, path: list[int] + graph: List[List[int]], next_ver: int, curr_ind: int, path: List[int] ) -> bool: """ Checks whether it is possible to add next into path by validating 2 statements @@ -47,7 +47,7 @@ def valid_connection( return not any(vertex == next_ver for vertex in path) -def util_hamilton_cycle(graph: list[list[int]], path: list[int], curr_ind: int) -> bool: +def util_hamilton_cycle(graph: List[List[int]], path: List[int], curr_ind: int) -> bool: """ Pseudo-Code Base Case: @@ -108,7 +108,7 @@ def util_hamilton_cycle(graph: list[list[int]], path: list[int], curr_ind: int) return False -def hamilton_cycle(graph: list[list[int]], start_index: int = 0) -> list[int]: +def hamilton_cycle(graph: List[List[int]], start_index: int = 0) -> List[int]: r""" Wrapper function to call subroutine called util_hamilton_cycle, which will either return array of vertices indicating hamiltonian cycle diff --git a/backtracking/knight_tour.py b/backtracking/knight_tour.py index 2413ba468838..8e6613e07d8b 100644 --- a/backtracking/knight_tour.py +++ b/backtracking/knight_tour.py @@ -1,9 +1,9 @@ # Knight Tour Intro: https://www.youtube.com/watch?v=ab_dY3dZFHM -from __future__ import annotations +from typing import List, Tuple -def get_valid_pos(position: tuple[int], n: int) -> list[tuple[int]]: +def get_valid_pos(position: Tuple[int, int], n: int) -> List[Tuple[int, int]]: """ Find all the valid positions a knight can move to from the current position. @@ -32,7 +32,7 @@ def get_valid_pos(position: tuple[int], n: int) -> list[tuple[int]]: return permissible_positions -def is_complete(board: list[list[int]]) -> bool: +def is_complete(board: List[List[int]]) -> bool: """ Check if the board (matrix) has been completely filled with non-zero values. @@ -46,7 +46,9 @@ def is_complete(board: list[list[int]]) -> bool: return not any(elem == 0 for row in board for elem in row) -def open_knight_tour_helper(board: list[list[int]], pos: tuple[int], curr: int) -> bool: +def open_knight_tour_helper( + board: List[List[int]], pos: Tuple[int, int], curr: int +) -> bool: """ Helper function to solve knight tour problem. """ @@ -66,7 +68,7 @@ def open_knight_tour_helper(board: list[list[int]], pos: tuple[int], curr: int) return False -def open_knight_tour(n: int) -> list[list[int]]: +def open_knight_tour(n: int) -> List[List[int]]: """ Find the solution for the knight tour problem for a board of size n. Raises ValueError if the tour cannot be performed for the given size. diff --git a/backtracking/minimax.py b/backtracking/minimax.py index 91188090c899..dda29b47d6cc 100644 --- a/backtracking/minimax.py +++ b/backtracking/minimax.py @@ -1,18 +1,18 @@ -from __future__ import annotations - -import math +""" +Minimax helps to achieve maximum score in a game by checking all possible moves +depth is current depth in game tree. -""" Minimax helps to achieve maximum score in a game by checking all possible moves - depth is current depth in game tree. - nodeIndex is index of current node in scores[]. - if move is of maximizer return true else false - leaves of game tree is stored in scores[] - height is maximum height of Game tree +nodeIndex is index of current node in scores[]. +if move is of maximizer return true else false +leaves of game tree is stored in scores[] +height is maximum height of Game tree """ +import math +from typing import List def minimax( - depth: int, node_index: int, is_max: bool, scores: list[int], height: float + depth: int, node_index: int, is_max: bool, scores: List[int], height: float ) -> int: """ >>> import math @@ -32,10 +32,6 @@ def minimax( >>> height = math.log(len(scores), 2) >>> minimax(0, 0, True, scores, height) 12 - >>> minimax('1', 2, True, [], 2 ) - Traceback (most recent call last): - ... - TypeError: '<' not supported between instances of 'str' and 'int' """ if depth < 0: @@ -59,7 +55,7 @@ def minimax( ) -def main(): +def main() -> None: scores = [90, 23, 6, 33, 21, 65, 123, 34423] height = math.log(len(scores), 2) print("Optimal value : ", end="") diff --git a/backtracking/n_queens_math.py b/backtracking/n_queens_math.py index 811611971616..a8651c5c362e 100644 --- a/backtracking/n_queens_math.py +++ b/backtracking/n_queens_math.py @@ -75,14 +75,14 @@ for another one or vice versa. """ -from __future__ import annotations +from typing import List def depth_first_search( - possible_board: list[int], - diagonal_right_collisions: list[int], - diagonal_left_collisions: list[int], - boards: list[list[str]], + possible_board: List[int], + diagonal_right_collisions: List[int], + diagonal_left_collisions: List[int], + boards: List[List[str]], n: int, ) -> None: """ @@ -94,40 +94,33 @@ def depth_first_search( ['. . Q . ', 'Q . . . ', '. . . Q ', '. Q . . '] """ - """ Get next row in the current board (possible_board) to fill it with a queen """ + # Get next row in the current board (possible_board) to fill it with a queen row = len(possible_board) - """ - If row is equal to the size of the board it means there are a queen in each row in - the current board (possible_board) - """ + # If row is equal to the size of the board it means there are a queen in each row in + # the current board (possible_board) if row == n: - """ - We convert the variable possible_board that looks like this: [1, 3, 0, 2] to - this: ['. Q . . ', '. . . Q ', 'Q . . . ', '. . Q . '] - """ - possible_board = [". " * i + "Q " + ". " * (n - 1 - i) for i in possible_board] - boards.append(possible_board) + # We convert the variable possible_board that looks like this: [1, 3, 0, 2] to + # this: ['. Q . . ', '. . . Q ', 'Q . . . ', '. . Q . '] + boards.append([". " * i + "Q " + ". " * (n - 1 - i) for i in possible_board]) return - """ We iterate each column in the row to find all possible results in each row """ + # We iterate each column in the row to find all possible results in each row for col in range(n): - """ - We apply that we learned previously. First we check that in the current board - (possible_board) there are not other same value because if there is it means - that there are a collision in vertical. Then we apply the two formulas we - learned before: - - 45º: y - x = b or 45: row - col = b - 135º: y + x = b or row + col = b. - - And we verify if the results of this two formulas not exist in their variables - respectively. (diagonal_right_collisions, diagonal_left_collisions) - - If any or these are True it means there is a collision so we continue to the - next value in the for loop. - """ + # We apply that we learned previously. First we check that in the current board + # (possible_board) there are not other same value because if there is it means + # that there are a collision in vertical. Then we apply the two formulas we + # learned before: + # + # 45º: y - x = b or 45: row - col = b + # 135º: y + x = b or row + col = b. + # + # And we verify if the results of this two formulas not exist in their variables + # respectively. (diagonal_right_collisions, diagonal_left_collisions) + # + # If any or these are True it means there is a collision so we continue to the + # next value in the for loop. if ( col in possible_board or row - col in diagonal_right_collisions @@ -135,7 +128,7 @@ def depth_first_search( ): continue - """ If it is False we call dfs function again and we update the inputs """ + # If it is False we call dfs function again and we update the inputs depth_first_search( possible_board + [col], diagonal_right_collisions + [row - col], @@ -146,10 +139,10 @@ def depth_first_search( def n_queens_solution(n: int) -> None: - boards = [] + boards: List[List[str]] = [] depth_first_search([], [], [], boards, n) - """ Print all the boards """ + # Print all the boards for board in boards: for column in board: print(column) diff --git a/backtracking/sudoku.py b/backtracking/sudoku.py index 614bdb8530ac..3bfaddd6e56f 100644 --- a/backtracking/sudoku.py +++ b/backtracking/sudoku.py @@ -1,20 +1,20 @@ -from typing import List, Tuple, Union +""" +Given a partially filled 9×9 2D array, the objective is to fill a 9×9 +square grid with digits numbered 1 to 9, so that every row, column, and +and each of the nine 3×3 sub-grids contains all of the digits. + +This can be solved using Backtracking and is similar to n-queens. +We check to see if a cell is safe or not and recursively call the +function on the next column to see if it returns True. if yes, we +have solved the puzzle. else, we backtrack and place another number +in that cell and repeat this process. +""" +from typing import List, Optional, Tuple Matrix = List[List[int]] -""" - Given a partially filled 9×9 2D array, the objective is to fill a 9×9 - square grid with digits numbered 1 to 9, so that every row, column, and - and each of the nine 3×3 sub-grids contains all of the digits. - - This can be solved using Backtracking and is similar to n-queens. - We check to see if a cell is safe or not and recursively call the - function on the next column to see if it returns True. if yes, we - have solved the puzzle. else, we backtrack and place another number - in that cell and repeat this process. -""" # assigning initial values to the grid -initial_grid = [ +initial_grid: Matrix = [ [3, 0, 6, 5, 0, 8, 4, 0, 0], [5, 2, 0, 0, 0, 0, 0, 0, 0], [0, 8, 7, 0, 0, 0, 0, 3, 1], @@ -27,7 +27,7 @@ ] # a grid with no solution -no_solution = [ +no_solution: Matrix = [ [5, 0, 6, 5, 0, 8, 4, 0, 3], [5, 2, 0, 0, 0, 0, 0, 0, 2], [1, 8, 7, 0, 0, 0, 0, 3, 1], @@ -80,7 +80,7 @@ def is_completed(grid: Matrix) -> bool: return all(all(cell != 0 for cell in row) for row in grid) -def find_empty_location(grid: Matrix) -> Tuple[int, int]: +def find_empty_location(grid: Matrix) -> Optional[Tuple[int, int]]: """ This function finds an empty location so that we can assign a number for that particular row and column. @@ -89,9 +89,10 @@ def find_empty_location(grid: Matrix) -> Tuple[int, int]: for j in range(9): if grid[i][j] == 0: return i, j + return None -def sudoku(grid: Matrix) -> Union[Matrix, bool]: +def sudoku(grid: Matrix) -> Optional[Matrix]: """ Takes a partially filled-in grid and attempts to assign values to all unassigned locations in such a way to meet the requirements @@ -107,25 +108,30 @@ def sudoku(grid: Matrix) -> Union[Matrix, bool]: [1, 3, 8, 9, 4, 7, 2, 5, 6], [6, 9, 2, 3, 5, 1, 8, 7, 4], [7, 4, 5, 2, 8, 6, 3, 1, 9]] - >>> sudoku(no_solution) - False + >>> sudoku(no_solution) is None + True """ if is_completed(grid): return grid - row, column = find_empty_location(grid) + location = find_empty_location(grid) + if location is not None: + row, column = location + else: + # If the location is ``None``, then the grid is solved. + return grid for digit in range(1, 10): if is_safe(grid, row, column, digit): grid[row][column] = digit - if sudoku(grid): + if sudoku(grid) is not None: return grid grid[row][column] = 0 - return False + return None def print_solution(grid: Matrix) -> None: @@ -141,11 +147,12 @@ def print_solution(grid: Matrix) -> None: if __name__ == "__main__": # make a copy of grid so that you can compare with the unmodified grid - for grid in (initial_grid, no_solution): - grid = list(map(list, grid)) - solution = sudoku(grid) - if solution: - print("grid after solving:") + for example_grid in (initial_grid, no_solution): + print("\nExample grid:\n" + "=" * 20) + print_solution(example_grid) + print("\nExample grid solution:") + solution = sudoku(example_grid) + if solution is not None: print_solution(solution) else: print("Cannot find a solution.") From 513064b8df0e5b0a772b115584152ce1a5aaea94 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 24 Dec 2020 18:16:44 +0530 Subject: [PATCH 138/195] Fix type annotations for bit manipulation algorithms (#4056) --- bit_manipulation/binary_and_operator.py | 2 +- bit_manipulation/binary_or_operator.py | 2 +- bit_manipulation/binary_xor_operator.py | 2 +- bit_manipulation/single_bit_manipulation_operations.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bit_manipulation/binary_and_operator.py b/bit_manipulation/binary_and_operator.py index f1b910f8cc9b..191ff8eb44a4 100644 --- a/bit_manipulation/binary_and_operator.py +++ b/bit_manipulation/binary_and_operator.py @@ -1,7 +1,7 @@ # https://www.tutorialspoint.com/python3/bitwise_operators_example.htm -def binary_and(a: int, b: int): +def binary_and(a: int, b: int) -> str: """ Take in 2 integers, convert them to binary, return a binary number that is the diff --git a/bit_manipulation/binary_or_operator.py b/bit_manipulation/binary_or_operator.py index e83a86d6a8bc..dabf5bcb09fd 100644 --- a/bit_manipulation/binary_or_operator.py +++ b/bit_manipulation/binary_or_operator.py @@ -1,7 +1,7 @@ # https://www.tutorialspoint.com/python3/bitwise_operators_example.htm -def binary_or(a: int, b: int): +def binary_or(a: int, b: int) -> str: """ Take in 2 integers, convert them to binary, and return a binary number that is the result of a binary or operation on the integers provided. diff --git a/bit_manipulation/binary_xor_operator.py b/bit_manipulation/binary_xor_operator.py index 0edf2ba6606d..6f8962192ad8 100644 --- a/bit_manipulation/binary_xor_operator.py +++ b/bit_manipulation/binary_xor_operator.py @@ -1,7 +1,7 @@ # https://www.tutorialspoint.com/python3/bitwise_operators_example.htm -def binary_xor(a: int, b: int): +def binary_xor(a: int, b: int) -> str: """ Take in 2 integers, convert them to binary, return a binary number that is the diff --git a/bit_manipulation/single_bit_manipulation_operations.py b/bit_manipulation/single_bit_manipulation_operations.py index 114eafe3235b..e4a54028d9ee 100644 --- a/bit_manipulation/single_bit_manipulation_operations.py +++ b/bit_manipulation/single_bit_manipulation_operations.py @@ -3,7 +3,7 @@ """Provide the functionality to manipulate a single bit.""" -def set_bit(number: int, position: int): +def set_bit(number: int, position: int) -> int: """ Set the bit at position to 1. @@ -21,7 +21,7 @@ def set_bit(number: int, position: int): return number | (1 << position) -def clear_bit(number: int, position: int): +def clear_bit(number: int, position: int) -> int: """ Set the bit at position to 0. @@ -37,7 +37,7 @@ def clear_bit(number: int, position: int): return number & ~(1 << position) -def flip_bit(number: int, position: int): +def flip_bit(number: int, position: int) -> int: """ Flip the bit at position. From a1833ad596b30f33ea7d439386cf131cd78ac924 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Sat, 26 Dec 2020 11:12:37 +0800 Subject: [PATCH 139/195] [mypy] Add type hints and docstrings to heap.py (#3013) * Add type hints and docstrings to heap.py - Add type hints - Add docstrings - Add explanatory comments - Improve code readability - Change to use f-string * Fix import sorting * fixup! Format Python code with psf/black push * Fix static type error * Fix failing test * Fix type hints * Add return annotation Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Dhruv Manilawala --- data_structures/heap/heap.py | 186 ++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 79 deletions(-) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index 2dc047436a77..8592362c23b9 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -1,101 +1,138 @@ -#!/usr/bin/python3 +from typing import Iterable, List, Optional class Heap: - """ + """A Max Heap Implementation + >>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5] >>> h = Heap() - >>> h.build_heap(unsorted) - >>> h.display() + >>> h.build_max_heap(unsorted) + >>> print(h) [209, 201, 25, 103, 107, 15, 1, 9, 7, 11, 5] >>> - >>> h.get_max() + >>> h.extract_max() 209 - >>> h.display() + >>> print(h) [201, 107, 25, 103, 11, 15, 1, 9, 7, 5] >>> >>> h.insert(100) - >>> h.display() + >>> print(h) [201, 107, 25, 103, 100, 15, 1, 9, 7, 5, 11] >>> >>> h.heap_sort() - >>> h.display() + >>> print(h) [1, 5, 7, 9, 11, 15, 25, 100, 103, 107, 201] - >>> """ - def __init__(self): - self.h = [] - self.curr_size = 0 + def __init__(self) -> None: + self.h: List[float] = [] + self.heap_size: int = 0 + + def __repr__(self) -> str: + return str(self.h) - def get_left_child_index(self, i): - left_child_index = 2 * i + 1 - if left_child_index < self.curr_size: + def parent_index(self, child_idx: int) -> Optional[int]: + """ return the parent index of given child """ + if child_idx > 0: + return (child_idx - 1) // 2 + return None + + def left_child_idx(self, parent_idx: int) -> Optional[int]: + """ + return the left child index if the left child exists. + if not, return None. + """ + left_child_index = 2 * parent_idx + 1 + if left_child_index < self.heap_size: return left_child_index return None - def get_right_child(self, i): - right_child_index = 2 * i + 2 - if right_child_index < self.curr_size: + def right_child_idx(self, parent_idx: int) -> Optional[int]: + """ + return the right child index if the right child exists. + if not, return None. + """ + right_child_index = 2 * parent_idx + 2 + if right_child_index < self.heap_size: return right_child_index return None - def max_heapify(self, index): - if index < self.curr_size: - largest = index - lc = self.get_left_child_index(index) - rc = self.get_right_child(index) - if lc is not None and self.h[lc] > self.h[largest]: - largest = lc - if rc is not None and self.h[rc] > self.h[largest]: - largest = rc - if largest != index: - self.h[largest], self.h[index] = self.h[index], self.h[largest] - self.max_heapify(largest) - - def build_heap(self, collection): - self.curr_size = len(collection) + def max_heapify(self, index: int) -> None: + """ + correct a single violation of the heap property in a subtree's root. + """ + if index < self.heap_size: + violation: int = index + left_child = self.left_child_idx(index) + right_child = self.right_child_idx(index) + # check which child is larger than its parent + if left_child is not None and self.h[left_child] > self.h[violation]: + violation = left_child + if right_child is not None and self.h[right_child] > self.h[violation]: + violation = right_child + # if violation indeed exists + if violation != index: + # swap to fix the violation + self.h[violation], self.h[index] = self.h[index], self.h[violation] + # fix the subsequent violation recursively if any + self.max_heapify(violation) + + def build_max_heap(self, collection: Iterable[float]) -> None: + """ build max heap from an unsorted array""" self.h = list(collection) - if self.curr_size <= 1: - return - for i in range(self.curr_size // 2 - 1, -1, -1): - self.max_heapify(i) - - def get_max(self): - if self.curr_size >= 2: + self.heap_size = len(self.h) + if self.heap_size > 1: + # max_heapify from right to left but exclude leaves (last level) + for i in range(self.heap_size // 2 - 1, -1, -1): + self.max_heapify(i) + + def max(self) -> float: + """ return the max in the heap """ + if self.heap_size >= 1: + return self.h[0] + else: + raise Exception("Empty heap") + + def extract_max(self) -> float: + """ get and remove max from heap """ + if self.heap_size >= 2: me = self.h[0] self.h[0] = self.h.pop(-1) - self.curr_size -= 1 + self.heap_size -= 1 self.max_heapify(0) return me - elif self.curr_size == 1: - self.curr_size -= 1 + elif self.heap_size == 1: + self.heap_size -= 1 return self.h.pop(-1) - return None - - def heap_sort(self): - size = self.curr_size + else: + raise Exception("Empty heap") + + def insert(self, value: float) -> None: + """ insert a new value into the max heap """ + self.h.append(value) + idx = (self.heap_size - 1) // 2 + self.heap_size += 1 + while idx >= 0: + self.max_heapify(idx) + idx = (idx - 1) // 2 + + def heap_sort(self) -> None: + size = self.heap_size for j in range(size - 1, 0, -1): self.h[0], self.h[j] = self.h[j], self.h[0] - self.curr_size -= 1 + self.heap_size -= 1 self.max_heapify(0) - self.curr_size = size + self.heap_size = size - def insert(self, data): - self.h.append(data) - curr = (self.curr_size - 1) // 2 - self.curr_size += 1 - while curr >= 0: - self.max_heapify(curr) - curr = (curr - 1) // 2 - def display(self): - print(self.h) +if __name__ == "__main__": + import doctest + # run doc test + doctest.testmod() -def main(): + # demo for unsorted in [ - [], [0], [2], [3, 5], @@ -110,26 +147,17 @@ def main(): [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5], [-45, -2, -5], ]: - print("source unsorted list: %s" % unsorted) + print(f"unsorted array: {unsorted}") - h = Heap() - h.build_heap(unsorted) - print("after build heap: ", end=" ") - h.display() + heap = Heap() + heap.build_max_heap(unsorted) + print(f"after build heap: {heap}") - print("max value: %s" % h.get_max()) - print("delete max value: ", end=" ") - h.display() + print(f"max value: {heap.extract_max()}") + print(f"after max value removed: {heap}") - h.insert(100) - print("after insert new value 100: ", end=" ") - h.display() + heap.insert(100) + print(f"after new value 100 inserted: {heap}") - h.heap_sort() - print("heap sort: ", end=" ") - h.display() - print() - - -if __name__ == "__main__": - main() + heap.heap_sort() + print(f"heap-sorted array: {heap}\n") From ffa30583d2238beb5ab90f41b87e3b9126029173 Mon Sep 17 00:00:00 2001 From: Ramandeep Singh Date: Sat, 26 Dec 2020 21:43:20 +0530 Subject: [PATCH 140/195] Add 2-hidden layer neural network with back propagation using sigmoid activation function (#4037) * added neural network with 2 hidden layers * Revert "added neural network with 2 hidden layers" This reverts commit fa4e2ac86eceaae018cb18c720420665b485f3b7. * added neural network with 2 hidden layers * passing pre-commit requirements * doctest completed * added return hints * added example * example added * completed doctest's * changes made as per the review * changes made * changes after review * changes * spacing * changed return type * changed dtype --- .../2_hidden_layers_neural_network.py | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 neural_network/2_hidden_layers_neural_network.py diff --git a/neural_network/2_hidden_layers_neural_network.py b/neural_network/2_hidden_layers_neural_network.py new file mode 100644 index 000000000000..baa4316200d9 --- /dev/null +++ b/neural_network/2_hidden_layers_neural_network.py @@ -0,0 +1,295 @@ +""" +References: + - http://neuralnetworksanddeeplearning.com/chap2.html (Backpropagation) + - https://en.wikipedia.org/wiki/Sigmoid_function (Sigmoid activation function) + - https://en.wikipedia.org/wiki/Feedforward_neural_network (Feedforward) +""" + +import numpy + + +class TwoHiddenLayerNeuralNetwork: + def __init__(self, input_array: numpy.ndarray, output_array: numpy.ndarray) -> None: + """ + This function initializes the TwoHiddenLayerNeuralNetwork class with random + weights for every layer and initializes predicted output with zeroes. + + input_array : input values for training the neural network (i.e training data) . + output_array : expected output values of the given inputs. + """ + + # Input values provided for training the model. + self.input_array = input_array + + # Random initial weights are assigned where first argument is the + # number of nodes in previous layer and second argument is the + # number of nodes in the next layer. + + # Random initial weights are assigned. + # self.input_array.shape[1] is used to represent number of nodes in input layer. + # First hidden layer consists of 4 nodes. + self.input_layer_and_first_hidden_layer_weights = numpy.random.rand( + self.input_array.shape[1], 4 + ) + + # Random initial values for the first hidden layer. + # First hidden layer has 4 nodes. + # Second hidden layer has 3 nodes. + self.first_hidden_layer_and_second_hidden_layer_weights = numpy.random.rand( + 4, 3 + ) + + # Random initial values for the second hidden layer. + # Second hidden layer has 3 nodes. + # Output layer has 1 node. + self.second_hidden_layer_and_output_layer_weights = numpy.random.rand(3, 1) + + # Real output values provided. + self.output_array = output_array + + # Predicted output values by the neural network. + # Predicted_output array initially consists of zeroes. + self.predicted_output = numpy.zeros(output_array.shape) + + def feedforward(self) -> numpy.ndarray: + """ + The information moves in only one direction i.e. forward from the input nodes, + through the two hidden nodes and to the output nodes. + There are no cycles or loops in the network. + + Return layer_between_second_hidden_layer_and_output + (i.e the last layer of the neural network). + + >>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) + >>> output_val = numpy.array(([0], [0], [0]), dtype=float) + >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) + >>> res = nn.feedforward() + >>> array_sum = numpy.sum(res) + >>> numpy.isnan(array_sum) + False + """ + # Layer_between_input_and_first_hidden_layer is the layer connecting the + # input nodes with the first hidden layer nodes. + self.layer_between_input_and_first_hidden_layer = sigmoid( + numpy.dot(self.input_array, self.input_layer_and_first_hidden_layer_weights) + ) + + # layer_between_first_hidden_layer_and_second_hidden_layer is the layer + # connecting the first hidden set of nodes with the second hidden set of nodes. + self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( + numpy.dot( + self.layer_between_input_and_first_hidden_layer, + self.first_hidden_layer_and_second_hidden_layer_weights, + ) + ) + + # layer_between_second_hidden_layer_and_output is the layer connecting + # second hidden layer with the output node. + self.layer_between_second_hidden_layer_and_output = sigmoid( + numpy.dot( + self.layer_between_first_hidden_layer_and_second_hidden_layer, + self.second_hidden_layer_and_output_layer_weights, + ) + ) + + return self.layer_between_second_hidden_layer_and_output + + def back_propagation(self) -> None: + """ + Function for fine-tuning the weights of the neural net based on the + error rate obtained in the previous epoch (i.e., iteration). + Updation is done using derivative of sogmoid activation function. + + >>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) + >>> output_val = numpy.array(([0], [0], [0]), dtype=float) + >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) + >>> res = nn.feedforward() + >>> nn.back_propagation() + >>> updated_weights = nn.second_hidden_layer_and_output_layer_weights + >>> (res == updated_weights).all() + False + """ + + updated_second_hidden_layer_and_output_layer_weights = numpy.dot( + self.layer_between_first_hidden_layer_and_second_hidden_layer.T, + 2 + * (self.output_array - self.predicted_output) + * sigmoid_derivative(self.predicted_output), + ) + updated_first_hidden_layer_and_second_hidden_layer_weights = numpy.dot( + self.layer_between_input_and_first_hidden_layer.T, + numpy.dot( + 2 + * (self.output_array - self.predicted_output) + * sigmoid_derivative(self.predicted_output), + self.second_hidden_layer_and_output_layer_weights.T, + ) + * sigmoid_derivative( + self.layer_between_first_hidden_layer_and_second_hidden_layer + ), + ) + updated_input_layer_and_first_hidden_layer_weights = numpy.dot( + self.input_array.T, + numpy.dot( + numpy.dot( + 2 + * (self.output_array - self.predicted_output) + * sigmoid_derivative(self.predicted_output), + self.second_hidden_layer_and_output_layer_weights.T, + ) + * sigmoid_derivative( + self.layer_between_first_hidden_layer_and_second_hidden_layer + ), + self.first_hidden_layer_and_second_hidden_layer_weights.T, + ) + * sigmoid_derivative(self.layer_between_input_and_first_hidden_layer), + ) + + self.input_layer_and_first_hidden_layer_weights += ( + updated_input_layer_and_first_hidden_layer_weights + ) + self.first_hidden_layer_and_second_hidden_layer_weights += ( + updated_first_hidden_layer_and_second_hidden_layer_weights + ) + self.second_hidden_layer_and_output_layer_weights += ( + updated_second_hidden_layer_and_output_layer_weights + ) + + def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None: + """ + Performs the feedforwarding and back propagation process for the + given number of iterations. + Every iteration will update the weights of neural network. + + output : real output values,required for calculating loss. + iterations : number of times the weights are to be updated. + give_loss : boolean value, If True then prints loss for each iteration, + If False then nothing is printed + + >>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) + >>> output_val = numpy.array(([0], [1], [1]), dtype=float) + >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) + >>> first_iteration_weights = nn.feedforward() + >>> nn.back_propagation() + >>> updated_weights = nn.second_hidden_layer_and_output_layer_weights + >>> (first_iteration_weights == updated_weights).all() + False + """ + for iteration in range(1, iterations + 1): + self.output = self.feedforward() + self.back_propagation() + if give_loss: + loss = numpy.mean(numpy.square(output - self.feedforward())) + print(f"Iteration {iteration} Loss: {loss}") + + def predict(self, input: numpy.ndarray) -> int: + """ + Predict's the output for the given input values using + the trained neural network. + + The output value given by the model ranges in-between 0 and 1. + The predict function returns 1 if the model value is greater + than the threshold value else returns 0, + as the real output values are in binary. + + >>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) + >>> output_val = numpy.array(([0], [1], [1]), dtype=float) + >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) + >>> nn.train(output_val, 1000, False) + >>> nn.predict([0,1,0]) + 1 + """ + + # Input values for which the predictions are to be made. + self.array = input + + self.layer_between_input_and_first_hidden_layer = sigmoid( + numpy.dot(self.array, self.input_layer_and_first_hidden_layer_weights) + ) + + self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( + numpy.dot( + self.layer_between_input_and_first_hidden_layer, + self.first_hidden_layer_and_second_hidden_layer_weights, + ) + ) + + self.layer_between_second_hidden_layer_and_output = sigmoid( + numpy.dot( + self.layer_between_first_hidden_layer_and_second_hidden_layer, + self.second_hidden_layer_and_output_layer_weights, + ) + ) + + return int(self.layer_between_second_hidden_layer_and_output > 0.6) + + +def sigmoid(value: numpy.ndarray) -> numpy.ndarray: + """ + Applies sigmoid activation function. + + return normalized values + + >>> sigmoid(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) + array([[0.73105858, 0.5 , 0.88079708], + [0.73105858, 0.5 , 0.5 ]]) + """ + return 1 / (1 + numpy.exp(-value)) + + +def sigmoid_derivative(value: numpy.ndarray) -> numpy.ndarray: + """ + Provides the derivative value of the sigmoid function. + + returns derivative of the sigmoid value + + >>> sigmoid_derivative(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) + array([[ 0., 0., -2.], + [ 0., 0., 0.]]) + """ + return (value) * (1 - (value)) + + +def example() -> int: + """ + Example for "how to use the neural network class and use the + respected methods for the desired output". + Calls the TwoHiddenLayerNeuralNetwork class and + provides the fixed input output values to the model. + Model is trained for a fixed amount of iterations then the predict method is called. + In this example the output is divided into 2 classes i.e. binary classification, + the two classes are represented by '0' and '1'. + + >>> example() + 1 + """ + # Input values. + input = numpy.array( + ( + [0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1], + ), + dtype=numpy.float64, + ) + + # True output values for the given input values. + output = numpy.array(([0], [1], [1], [0], [1], [0], [0], [1]), dtype=numpy.float64) + + # Calling neural network class. + neural_network = TwoHiddenLayerNeuralNetwork(input_array=input, output_array=output) + + # Calling training function. + # Set give_loss to True if you want to see loss in every iteration. + neural_network.train(output=output, iterations=10, give_loss=False) + + return neural_network.predict(numpy.array(([1, 1, 1]), dtype=numpy.float64)) + + +if __name__ == "__main__": + example() From 9529d7f478b3204686181ba795e65da3f8927b2a Mon Sep 17 00:00:00 2001 From: shan7030 <42472191+shan7030@users.noreply.github.com> Date: Mon, 28 Dec 2020 09:34:40 +0530 Subject: [PATCH 141/195] [mypy] Add/fix type annotations for scheduling algorithms (#4074) * Fix mypy errors for scheduling/first_come_first_served * Fix mypy errors for scheduling/round_robin.py * Fix mypy errors for scheduling/shortest_job_first.py * Fix isort errors --- scheduling/first_come_first_served.py | 12 ++++++------ scheduling/round_robin.py | 9 ++++----- scheduling/shortest_job_first.py | 16 ++++++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/scheduling/first_come_first_served.py b/scheduling/first_come_first_served.py index c5f61720f97e..b51fc9fe0c04 100644 --- a/scheduling/first_come_first_served.py +++ b/scheduling/first_come_first_served.py @@ -2,10 +2,10 @@ # In this Algorithm we just care about the order that the processes arrived # without carring about their duration time # https://en.wikipedia.org/wiki/Scheduling_(computing)#First_come,_first_served -from __future__ import annotations +from typing import List -def calculate_waiting_times(duration_times: list[int]) -> list[int]: +def calculate_waiting_times(duration_times: List[int]) -> List[int]: """ This function calculates the waiting time of some processes that have a specified duration time. @@ -24,8 +24,8 @@ def calculate_waiting_times(duration_times: list[int]) -> list[int]: def calculate_turnaround_times( - duration_times: list[int], waiting_times: list[int] -) -> list[int]: + duration_times: List[int], waiting_times: List[int] +) -> List[int]: """ This function calculates the turnaround time of some processes. Return: The time difference between the completion time and the @@ -44,7 +44,7 @@ def calculate_turnaround_times( ] -def calculate_average_turnaround_time(turnaround_times: list[int]) -> float: +def calculate_average_turnaround_time(turnaround_times: List[int]) -> float: """ This function calculates the average of the turnaround times Return: The average of the turnaround times. @@ -58,7 +58,7 @@ def calculate_average_turnaround_time(turnaround_times: list[int]) -> float: return sum(turnaround_times) / len(turnaround_times) -def calculate_average_waiting_time(waiting_times: list[int]) -> float: +def calculate_average_waiting_time(waiting_times: List[int]) -> float: """ This function calculates the average of the waiting times Return: The average of the waiting times. diff --git a/scheduling/round_robin.py b/scheduling/round_robin.py index e8d54dd9a553..4a79301c1816 100644 --- a/scheduling/round_robin.py +++ b/scheduling/round_robin.py @@ -3,12 +3,11 @@ In Round Robin each process is assigned a fixed time slot in a cyclic way. https://en.wikipedia.org/wiki/Round-robin_scheduling """ -from __future__ import annotations - from statistics import mean +from typing import List -def calculate_waiting_times(burst_times: list[int]) -> list[int]: +def calculate_waiting_times(burst_times: List[int]) -> List[int]: """ Calculate the waiting times of a list of processes that have a specified duration. @@ -41,8 +40,8 @@ def calculate_waiting_times(burst_times: list[int]) -> list[int]: def calculate_turn_around_times( - burst_times: list[int], waiting_times: list[int] -) -> list[int]: + burst_times: List[int], waiting_times: List[int] +) -> List[int]: """ >>> calculate_turn_around_times([1, 2, 3, 4], [0, 1, 3]) [1, 3, 6] diff --git a/scheduling/shortest_job_first.py b/scheduling/shortest_job_first.py index f9e2ad975627..a49d037d6a23 100644 --- a/scheduling/shortest_job_first.py +++ b/scheduling/shortest_job_first.py @@ -3,17 +3,17 @@ Please note arrival time and burst Please use spaces to separate times entered. """ -from __future__ import annotations +from typing import List import pandas as pd def calculate_waitingtime( - arrival_time: list[int], burst_time: list[int], no_of_processes: int -) -> list[int]: + arrival_time: List[int], burst_time: List[int], no_of_processes: int +) -> List[int]: """ Calculate the waiting time of each processes - Return: list of waiting times. + Return: List of waiting times. >>> calculate_waitingtime([1,2,3,4],[3,3,5,1],4) [0, 3, 5, 0] >>> calculate_waitingtime([1,2,3],[2,5,1],3) @@ -72,8 +72,8 @@ def calculate_waitingtime( def calculate_turnaroundtime( - burst_time: list[int], no_of_processes: int, waiting_time: list[int] -) -> list[int]: + burst_time: List[int], no_of_processes: int, waiting_time: List[int] +) -> List[int]: """ Calculate the turn around time of each Processes Return: list of turn around times. @@ -91,8 +91,8 @@ def calculate_turnaroundtime( def calculate_average_times( - waiting_time: list[int], turn_around_time: list[int], no_of_processes: int -): + waiting_time: List[int], turn_around_time: List[int], no_of_processes: int +) -> None: """ This function calculates the average of the waiting & turnaround times Prints: Average Waiting time & Average Turn Around Time From b071c3ea63d9fb0338fd7f21171b7dc252dc841e Mon Sep 17 00:00:00 2001 From: fpringle Date: Mon, 28 Dec 2020 08:51:02 +0100 Subject: [PATCH 142/195] Add solution for Project Euler problem 107 (#4066) * Added solution for Project Euler problem 107 * Doctests and better variable names * Type hints * Small edits * Forward reference for typing hint * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 + project_euler/problem_107/__init__.py | 0 project_euler/problem_107/p107_network.txt | 40 +++++++ project_euler/problem_107/sol1.py | 128 +++++++++++++++++++++ project_euler/problem_107/test_network.txt | 7 ++ 5 files changed, 178 insertions(+) create mode 100644 project_euler/problem_107/__init__.py create mode 100644 project_euler/problem_107/p107_network.txt create mode 100644 project_euler/problem_107/sol1.py create mode 100644 project_euler/problem_107/test_network.txt diff --git a/DIRECTORY.md b/DIRECTORY.md index d73ae11eb7c2..4f17cf9c03ed 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -499,6 +499,7 @@ * [Minimum Cut](https://github.com/TheAlgorithms/Python/blob/master/networking_flow/minimum_cut.py) ## Neural Network + * [2 Hidden Layers Neural Network](https://github.com/TheAlgorithms/Python/blob/master/neural_network/2_hidden_layers_neural_network.py) * [Back Propagation Neural Network](https://github.com/TheAlgorithms/Python/blob/master/neural_network/back_propagation_neural_network.py) * [Convolution Neural Network](https://github.com/TheAlgorithms/Python/blob/master/neural_network/convolution_neural_network.py) * [Perceptron](https://github.com/TheAlgorithms/Python/blob/master/neural_network/perceptron.py) @@ -748,6 +749,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_101/sol1.py) * Problem 102 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_102/sol1.py) + * Problem 107 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_107/sol1.py) * Problem 112 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_112/sol1.py) * Problem 113 diff --git a/project_euler/problem_107/__init__.py b/project_euler/problem_107/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_107/p107_network.txt b/project_euler/problem_107/p107_network.txt new file mode 100644 index 000000000000..fcc3c4192b96 --- /dev/null +++ b/project_euler/problem_107/p107_network.txt @@ -0,0 +1,40 @@ +-,-,-,427,668,495,377,678,-,177,-,-,870,-,869,624,300,609,131,-,251,-,-,-,856,221,514,-,591,762,182,56,-,884,412,273,636,-,-,774 +-,-,262,-,-,508,472,799,-,956,578,363,940,143,-,162,122,910,-,729,802,941,922,573,531,539,667,607,-,920,-,-,315,649,937,-,185,102,636,289 +-,262,-,-,926,-,958,158,647,47,621,264,81,-,402,813,649,386,252,391,264,637,349,-,-,-,108,-,727,225,578,699,-,898,294,-,575,168,432,833 +427,-,-,-,366,-,-,635,-,32,962,468,893,854,718,427,448,916,258,-,760,909,529,311,404,-,-,588,680,875,-,615,-,409,758,221,-,-,76,257 +668,-,926,366,-,-,-,250,268,-,503,944,-,677,-,727,793,457,981,191,-,-,-,351,969,925,987,328,282,589,-,873,477,-,-,19,450,-,-,- +495,508,-,-,-,-,-,765,711,819,305,302,926,-,-,582,-,861,-,683,293,-,-,66,-,27,-,-,290,-,786,-,554,817,33,-,54,506,386,381 +377,472,958,-,-,-,-,-,-,120,42,-,134,219,457,639,538,374,-,-,-,966,-,-,-,-,-,449,120,797,358,232,550,-,305,997,662,744,686,239 +678,799,158,635,250,765,-,-,-,35,-,106,385,652,160,-,890,812,605,953,-,-,-,79,-,712,613,312,452,-,978,900,-,901,-,-,225,533,770,722 +-,-,647,-,268,711,-,-,-,283,-,172,-,663,236,36,403,286,986,-,-,810,761,574,53,793,-,-,777,330,936,883,286,-,174,-,-,-,828,711 +177,956,47,32,-,819,120,35,283,-,50,-,565,36,767,684,344,489,565,-,-,103,810,463,733,665,494,644,863,25,385,-,342,470,-,-,-,730,582,468 +-,578,621,962,503,305,42,-,-,50,-,155,519,-,-,256,990,801,154,53,474,650,402,-,-,-,966,-,-,406,989,772,932,7,-,823,391,-,-,933 +-,363,264,468,944,302,-,106,172,-,155,-,-,-,380,438,-,41,266,-,-,104,867,609,-,270,861,-,-,165,-,675,250,686,995,366,191,-,433,- +870,940,81,893,-,926,134,385,-,565,519,-,-,313,851,-,-,-,248,220,-,826,359,829,-,234,198,145,409,68,359,-,814,218,186,-,-,929,203,- +-,143,-,854,677,-,219,652,663,36,-,-,313,-,132,-,433,598,-,-,168,870,-,-,-,128,437,-,383,364,966,227,-,-,807,993,-,-,526,17 +869,-,402,718,-,-,457,160,236,767,-,380,851,132,-,-,596,903,613,730,-,261,-,142,379,885,89,-,848,258,112,-,900,-,-,818,639,268,600,- +624,162,813,427,727,582,639,-,36,684,256,438,-,-,-,-,539,379,664,561,542,-,999,585,-,-,321,398,-,-,950,68,193,-,697,-,390,588,848,- +300,122,649,448,793,-,538,890,403,344,990,-,-,433,596,539,-,-,73,-,318,-,-,500,-,968,-,291,-,-,765,196,504,757,-,542,-,395,227,148 +609,910,386,916,457,861,374,812,286,489,801,41,-,598,903,379,-,-,-,946,136,399,-,941,707,156,757,258,251,-,807,-,-,-,461,501,-,-,616,- +131,-,252,258,981,-,-,605,986,565,154,266,248,-,613,664,73,-,-,686,-,-,575,627,817,282,-,698,398,222,-,649,-,-,-,-,-,654,-,- +-,729,391,-,191,683,-,953,-,-,53,-,220,-,730,561,-,946,686,-,-,389,729,553,304,703,455,857,260,-,991,182,351,477,867,-,-,889,217,853 +251,802,264,760,-,293,-,-,-,-,474,-,-,168,-,542,318,136,-,-,-,-,392,-,-,-,267,407,27,651,80,927,-,974,977,-,-,457,117,- +-,941,637,909,-,-,966,-,810,103,650,104,826,870,261,-,-,399,-,389,-,-,-,202,-,-,-,-,867,140,403,962,785,-,511,-,1,-,707,- +-,922,349,529,-,-,-,-,761,810,402,867,359,-,-,999,-,-,575,729,392,-,-,388,939,-,959,-,83,463,361,-,-,512,931,-,224,690,369,- +-,573,-,311,351,66,-,79,574,463,-,609,829,-,142,585,500,941,627,553,-,202,388,-,164,829,-,620,523,639,936,-,-,490,-,695,-,505,109,- +856,531,-,404,969,-,-,-,53,733,-,-,-,-,379,-,-,707,817,304,-,-,939,164,-,-,616,716,728,-,889,349,-,963,150,447,-,292,586,264 +221,539,-,-,925,27,-,712,793,665,-,270,234,128,885,-,968,156,282,703,-,-,-,829,-,-,-,822,-,-,-,736,576,-,697,946,443,-,205,194 +514,667,108,-,987,-,-,613,-,494,966,861,198,437,89,321,-,757,-,455,267,-,959,-,616,-,-,-,349,156,339,-,102,790,359,-,439,938,809,260 +-,607,-,588,328,-,449,312,-,644,-,-,145,-,-,398,291,258,698,857,407,-,-,620,716,822,-,-,293,486,943,-,779,-,6,880,116,775,-,947 +591,-,727,680,282,290,120,452,777,863,-,-,409,383,848,-,-,251,398,260,27,867,83,523,728,-,349,293,-,212,684,505,341,384,9,992,507,48,-,- +762,920,225,875,589,-,797,-,330,25,406,165,68,364,258,-,-,-,222,-,651,140,463,639,-,-,156,486,212,-,-,349,723,-,-,186,-,36,240,752 +182,-,578,-,-,786,358,978,936,385,989,-,359,966,112,950,765,807,-,991,80,403,361,936,889,-,339,943,684,-,-,965,302,676,725,-,327,134,-,147 +56,-,699,615,873,-,232,900,883,-,772,675,-,227,-,68,196,-,649,182,927,962,-,-,349,736,-,-,505,349,965,-,474,178,833,-,-,555,853,- +-,315,-,-,477,554,550,-,286,342,932,250,814,-,900,193,504,-,-,351,-,785,-,-,-,576,102,779,341,723,302,474,-,689,-,-,-,451,-,- +884,649,898,409,-,817,-,901,-,470,7,686,218,-,-,-,757,-,-,477,974,-,512,490,963,-,790,-,384,-,676,178,689,-,245,596,445,-,-,343 +412,937,294,758,-,33,305,-,174,-,-,995,186,807,-,697,-,461,-,867,977,511,931,-,150,697,359,6,9,-,725,833,-,245,-,949,-,270,-,112 +273,-,-,221,19,-,997,-,-,-,823,366,-,993,818,-,542,501,-,-,-,-,-,695,447,946,-,880,992,186,-,-,-,596,949,-,91,-,768,273 +636,185,575,-,450,54,662,225,-,-,391,191,-,-,639,390,-,-,-,-,-,1,224,-,-,443,439,116,507,-,327,-,-,445,-,91,-,248,-,344 +-,102,168,-,-,506,744,533,-,730,-,-,929,-,268,588,395,-,654,889,457,-,690,505,292,-,938,775,48,36,134,555,451,-,270,-,248,-,371,680 +-,636,432,76,-,386,686,770,828,582,-,433,203,526,600,848,227,616,-,217,117,707,369,109,586,205,809,-,-,240,-,853,-,-,-,768,-,371,-,540 +774,289,833,257,-,381,239,722,711,468,933,-,-,17,-,-,148,-,-,853,-,-,-,-,264,194,260,947,-,752,147,-,-,343,112,273,344,680,540,- diff --git a/project_euler/problem_107/sol1.py b/project_euler/problem_107/sol1.py new file mode 100644 index 000000000000..80a10e499f76 --- /dev/null +++ b/project_euler/problem_107/sol1.py @@ -0,0 +1,128 @@ +""" +The following undirected network consists of seven vertices and twelve edges +with a total weight of 243. + +The same network can be represented by the matrix below. + + A B C D E F G +A - 16 12 21 - - - +B 16 - - 17 20 - - +C 12 - - 28 - 31 - +D 21 17 28 - 18 19 23 +E - 20 - 18 - - 11 +F - - 31 19 - - 27 +G - - - 23 11 27 - + +However, it is possible to optimise the network by removing some edges and still +ensure that all points on the network remain connected. The network which achieves +the maximum saving is shown below. It has a weight of 93, representing a saving of +243 - 93 = 150 from the original network. + +Using network.txt (right click and 'Save Link/Target As...'), a 6K text file +containing a network with forty vertices, and given in matrix form, find the maximum +saving which can be achieved by removing redundant edges whilst ensuring that the +network remains connected. + +Solution: + We use Prim's algorithm to find a Minimum Spanning Tree. + Reference: https://en.wikipedia.org/wiki/Prim%27s_algorithm +""" + +import os +from typing import Dict, List, Mapping, Set, Tuple + +EdgeT = Tuple[int, int] + + +class Graph: + """ + A class representing an undirected weighted graph. + """ + + def __init__(self, vertices: Set[int], edges: Mapping[EdgeT, int]) -> None: + self.vertices: Set[int] = vertices + self.edges: Dict[EdgeT, int] = { + (min(edge), max(edge)): weight for edge, weight in edges.items() + } + + def add_edge(self, edge: EdgeT, weight: int) -> None: + """ + Add a new edge to the graph. + >>> graph = Graph({1, 2}, {(2, 1): 4}) + >>> graph.add_edge((3, 1), 5) + >>> sorted(graph.vertices) + [1, 2, 3] + >>> sorted([(v,k) for k,v in graph.edges.items()]) + [(4, (1, 2)), (5, (1, 3))] + """ + self.vertices.add(edge[0]) + self.vertices.add(edge[1]) + self.edges[(min(edge), max(edge))] = weight + + def prims_algorithm(self) -> "Graph": + """ + Run Prim's algorithm to find the minimum spanning tree. + Reference: https://en.wikipedia.org/wiki/Prim%27s_algorithm + >>> graph = Graph({1,2,3,4},{(1,2):5, (1,3):10, (1,4):20, (2,4):30, (3,4):1}) + >>> mst = graph.prims_algorithm() + >>> sorted(mst.vertices) + [1, 2, 3, 4] + >>> sorted(mst.edges) + [(1, 2), (1, 3), (3, 4)] + """ + subgraph: Graph = Graph({min(self.vertices)}, {}) + min_edge: EdgeT + min_weight: int + edge: EdgeT + weight: int + + while len(subgraph.vertices) < len(self.vertices): + min_weight = max(self.edges.values()) + 1 + for edge, weight in self.edges.items(): + if (edge[0] in subgraph.vertices) ^ (edge[1] in subgraph.vertices): + if weight < min_weight: + min_edge = edge + min_weight = weight + + subgraph.add_edge(min_edge, min_weight) + + return subgraph + + +def solution(filename: str = "p107_network.txt") -> int: + """ + Find the maximum saving which can be achieved by removing redundant edges + whilst ensuring that the network remains connected. + >>> solution("test_network.txt") + 150 + """ + script_dir: str = os.path.abspath(os.path.dirname(__file__)) + network_file: str = os.path.join(script_dir, filename) + adjacency_matrix: List[List[str]] + edges: Dict[EdgeT, int] = dict() + data: List[str] + edge1: int + edge2: int + + with open(network_file, "r") as f: + data = f.read().strip().split("\n") + + adjaceny_matrix = [line.split(",") for line in data] + + for edge1 in range(1, len(adjaceny_matrix)): + for edge2 in range(edge1): + if adjaceny_matrix[edge1][edge2] != "-": + edges[(edge2, edge1)] = int(adjaceny_matrix[edge1][edge2]) + + graph: Graph = Graph(set(range(len(adjaceny_matrix))), edges) + + subgraph: Graph = graph.prims_algorithm() + + initial_total: int = sum(graph.edges.values()) + optimal_total: int = sum(subgraph.edges.values()) + + return initial_total - optimal_total + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_107/test_network.txt b/project_euler/problem_107/test_network.txt new file mode 100644 index 000000000000..f5f2accb5720 --- /dev/null +++ b/project_euler/problem_107/test_network.txt @@ -0,0 +1,7 @@ +-,16,12,21,-,-,- +16,-,-,17,20,-,- +12,-,-,28,-,31,- +21,17,28,-,18,19,23 +-,20,-,18,-,-,11 +-,-,31,19,-,-,27 +-,-,-,23,11,27,- From bee23dd49186ff3db52c6e81bdeb3bf8c0c1d6db Mon Sep 17 00:00:00 2001 From: SiddhantJain15 Date: Mon, 28 Dec 2020 13:36:57 +0530 Subject: [PATCH 143/195] Add function to calculate area of triangle using Heron's formula (#4065) * Update area.py Modified area of triangle function. Added a new algorithm to calculate area when 3 sides are known * Add files via upload * Update area.py * Update area.py * Update area.py * Update area.py * Remove unnecessary whitespace Co-authored-by: Dhruv Manilawala --- maths/area.py | 81 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/maths/area.py b/maths/area.py index 24216e223ebf..8689f323cc9a 100644 --- a/maths/area.py +++ b/maths/area.py @@ -1,7 +1,7 @@ """ Find the area of various geometric shapes """ -from math import pi +from math import pi, sqrt def surface_area_cube(side_length: float) -> float: @@ -26,7 +26,7 @@ def surface_area_sphere(radius: float) -> float: """ Calculate the Surface Area of a Sphere. Wikipedia reference: https://en.wikipedia.org/wiki/Sphere - :return 4 * pi * r^2 + Formula: 4 * pi * r^2 >>> surface_area_sphere(5) 314.1592653589793 @@ -44,7 +44,7 @@ def surface_area_sphere(radius: float) -> float: def area_rectangle(length: float, width: float) -> float: """ - Calculate the area of a rectangle + Calculate the area of a rectangle. >>> area_rectangle(10, 20) 200 @@ -68,7 +68,7 @@ def area_rectangle(length: float, width: float) -> float: def area_square(side_length: float) -> float: """ - Calculate the area of a square + Calculate the area of a square. >>> area_square(10) 100 @@ -84,7 +84,7 @@ def area_square(side_length: float) -> float: def area_triangle(base: float, height: float) -> float: """ - Calculate the area of a triangle + Calculate the area of a triangle given the base and height. >>> area_triangle(10, 10) 50.0 @@ -106,9 +106,42 @@ def area_triangle(base: float, height: float) -> float: return (base * height) / 2 +def area_triangle_three_sides(side1: float, side2: float, side3: float) -> float: + """ + Calculate area of triangle when the length of 3 sides are known. + + This function uses Heron's formula: https://en.wikipedia.org/wiki/Heron%27s_formula + + >>> area_triangle_three_sides(5, 12, 13) + 30.0 + >>> area_triangle_three_sides(10, 11, 12) + 51.521233486786784 + >>> area_triangle_three_sides(-1, -2, -1) + Traceback (most recent call last): + ... + ValueError: area_triangle_three_sides() only accepts non-negative values + >>> area_triangle_three_sides(1, -2, 1) + Traceback (most recent call last): + ... + ValueError: area_triangle_three_sides() only accepts non-negative values + """ + if side1 < 0 or side2 < 0 or side3 < 0: + raise ValueError("area_triangle_three_sides() only accepts non-negative values") + elif side1 + side2 < side3 or side1 + side3 < side2 or side2 + side3 < side1: + raise ValueError("Given three sides do not form a triangle") + semi_perimeter = (side1 + side2 + side3) / 2 + area = sqrt( + semi_perimeter + * (semi_perimeter - side1) + * (semi_perimeter - side2) + * (semi_perimeter - side3) + ) + return area + + def area_parallelogram(base: float, height: float) -> float: """ - Calculate the area of a parallelogram + Calculate the area of a parallelogram. >>> area_parallelogram(10, 20) 200 @@ -132,7 +165,7 @@ def area_parallelogram(base: float, height: float) -> float: def area_trapezium(base1: float, base2: float, height: float) -> float: """ - Calculate the area of a trapezium + Calculate the area of a trapezium. >>> area_trapezium(10, 20, 30) 450.0 @@ -172,7 +205,7 @@ def area_trapezium(base1: float, base2: float, height: float) -> float: def area_circle(radius: float) -> float: """ - Calculate the area of a circle + Calculate the area of a circle. >>> area_circle(20) 1256.6370614359173 @@ -188,7 +221,7 @@ def area_circle(radius: float) -> float: def area_ellipse(radius_x: float, radius_y: float) -> float: """ - Calculate the area of a ellipse + Calculate the area of a ellipse. >>> area_ellipse(10, 10) 314.1592653589793 @@ -214,7 +247,7 @@ def area_ellipse(radius_x: float, radius_y: float) -> float: def area_rhombus(diagonal_1: float, diagonal_2: float) -> float: """ - Calculate the area of a rhombus + Calculate the area of a rhombus. >>> area_rhombus(10, 20) 100.0 @@ -236,24 +269,20 @@ def area_rhombus(diagonal_1: float, diagonal_2: float) -> float: return 1 / 2 * diagonal_1 * diagonal_2 -def main(): - print("Areas of various geometric shapes: \n") - print(f"Rectangle: {area_rectangle(10, 20)}") - print(f"Square: {area_square(10)}") - print(f"Triangle: {area_triangle(10, 10)}") - print(f"Parallelogram: {area_parallelogram(10, 20)}") - print(f"Trapezium: {area_trapezium(10, 20, 30)}") - print(f"Circle: {area_circle(20)}") - print("\nSurface Areas of various geometric shapes: \n") - print(f"Cube: {surface_area_cube(20)}") - print(f"Sphere: {surface_area_sphere(20)}") - print(f"Rhombus: {area_rhombus(10, 20)}") - - if __name__ == "__main__": - import doctest doctest.testmod(verbose=True) # verbose so we can see methods missing tests - main() + print("[DEMO] Areas of various geometric shapes: \n") + print(f"Rectangle: {area_rectangle(10, 20) = }") + print(f"Square: {area_square(10) = }") + print(f"Triangle: {area_triangle(10, 10) = }") + print(f"Triangle: {area_triangle_three_sides(5, 12, 13) = }") + print(f"Parallelogram: {area_parallelogram(10, 20) = }") + print(f"Trapezium: {area_trapezium(10, 20, 30) = }") + print(f"Circle: {area_circle(20) = }") + print("\nSurface Areas of various geometric shapes: \n") + print(f"Cube: {surface_area_cube(20) = }") + print(f"Sphere: {surface_area_sphere(20) = }") + print(f"Rhombus: {area_rhombus(10, 20) = }") From 0a304bd60f7469110024edaba85f32b49b49883e Mon Sep 17 00:00:00 2001 From: Kanak <63765823+Cosmicoppai@users.noreply.github.com> Date: Mon, 11 Jan 2021 16:55:15 +0530 Subject: [PATCH 144/195] Rename coin_change.py to minimum_coin_change.py (#4108) --- dynamic_programming/{coin_change.py => minimum_coin_change.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dynamic_programming/{coin_change.py => minimum_coin_change.py} (100%) diff --git a/dynamic_programming/coin_change.py b/dynamic_programming/minimum_coin_change.py similarity index 100% rename from dynamic_programming/coin_change.py rename to dynamic_programming/minimum_coin_change.py From 744cf0d7310b55db2163ffeb549fdf6d5e9d06de Mon Sep 17 00:00:00 2001 From: Nwachukwu Chidiebere Godwin Date: Tue, 12 Jan 2021 14:41:48 +0100 Subject: [PATCH 145/195] Graph list patch (#4113) * new implementation for adjacency list graph * add example code for undirected graph * reduce length to 88 columns max to fix build errors7 * fix pre commit issues * replace print_list method with __str__ * return object in add_edge method to enable fluent syntax * improve class docstring and include doctests * add end of file line * fix pre-commit issues * remove __str__ method * trigger build * Update graph_list.py * Update graph_list.py Co-authored-by: gnc Co-authored-by: Christian Clauss --- graphs/graph_list.py | 169 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 134 insertions(+), 35 deletions(-) diff --git a/graphs/graph_list.py b/graphs/graph_list.py index a812fecd961e..bab6d6893a89 100644 --- a/graphs/graph_list.py +++ b/graphs/graph_list.py @@ -1,44 +1,143 @@ -#!/usr/bin/python +#!/usr/bin/env python3 -# Author: OMKAR PATHAK +# Author: OMKAR PATHAK, Nwachukwu Chidiebere -# We can use Python's dictionary for constructing the graph. +# Use a Python dictionary to construct the graph. +from pprint import pformat -class AdjacencyList: - def __init__(self): - self.adj_list = {} - def add_edge(self, from_vertex: int, to_vertex: int) -> None: - # check if vertex is already present - if from_vertex in self.adj_list: - self.adj_list[from_vertex].append(to_vertex) - else: - self.adj_list[from_vertex] = [to_vertex] +class GraphAdjacencyList: + """ + Adjacency List type Graph Data Structure that accounts for directed and undirected + Graphs. Initialize graph object indicating whether it's directed or undirected. - def print_list(self) -> None: - for i in self.adj_list: - print((i, "->", " -> ".join([str(j) for j in self.adj_list[i]]))) + Directed graph example: + >>> d_graph = GraphAdjacencyList() + >>> d_graph + {} + >>> d_graph.add_edge(0, 1) + {0: [1], 1: []} + >>> d_graph.add_edge(1, 2).add_edge(1, 4).add_edge(1, 5) + {0: [1], 1: [2, 4, 5], 2: [], 4: [], 5: []} + >>> d_graph.add_edge(2, 0).add_edge(2, 6).add_edge(2, 7) + {0: [1], 1: [2, 4, 5], 2: [0, 6, 7], 4: [], 5: [], 6: [], 7: []} + >>> print(d_graph) + {0: [1], 1: [2, 4, 5], 2: [0, 6, 7], 4: [], 5: [], 6: [], 7: []} + >>> print(repr(d_graph)) + {0: [1], 1: [2, 4, 5], 2: [0, 6, 7], 4: [], 5: [], 6: [], 7: []} + + Undirected graph example: + >>> u_graph = GraphAdjacencyList(directed=False) + >>> u_graph.add_edge(0, 1) + {0: [1], 1: [0]} + >>> u_graph.add_edge(1, 2).add_edge(1, 4).add_edge(1, 5) + {0: [1], 1: [0, 2, 4, 5], 2: [1], 4: [1], 5: [1]} + >>> u_graph.add_edge(2, 0).add_edge(2, 6).add_edge(2, 7) + {0: [1, 2], 1: [0, 2, 4, 5], 2: [1, 0, 6, 7], 4: [1], 5: [1], 6: [2], 7: [2]} + >>> u_graph.add_edge(4, 5) + {0: [1, 2], + 1: [0, 2, 4, 5], + 2: [1, 0, 6, 7], + 4: [1, 5], + 5: [1, 4], + 6: [2], + 7: [2]} + >>> print(u_graph) + {0: [1, 2], + 1: [0, 2, 4, 5], + 2: [1, 0, 6, 7], + 4: [1, 5], + 5: [1, 4], + 6: [2], + 7: [2]} + >>> print(repr(u_graph)) + {0: [1, 2], + 1: [0, 2, 4, 5], + 2: [1, 0, 6, 7], + 4: [1, 5], + 5: [1, 4], + 6: [2], + 7: [2]} + """ + + def __init__(self, directed: bool = True): + """ + Parameters: + directed: (bool) Indicates if graph is directed or undirected. Default is True. + """ + + self.adj_list = {} # dictionary of lists + self.directed = directed + + def add_edge(self, source_vertex: int, destination_vertex: int) -> object: + """ + Connects vertices together. Creates and Edge from source vertex to destination + vertex. + Vertices will be created if not found in graph + """ + + if not self.directed: # For undirected graphs + # if both source vertex and destination vertex are both present in the + # adjacency list, add destination vertex to source vertex list of adjacent + # vertices and add source vertex to destination vertex list of adjacent + # vertices. + if source_vertex in self.adj_list and destination_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + self.adj_list[destination_vertex].append(source_vertex) + # if only source vertex is present in adjacency list, add destination vertex + # to source vertex list of adjacent vertices, then create a new vertex with + # destination vertex as key and assign a list containing the source vertex + # as it's first adjacent vertex. + elif source_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + self.adj_list[destination_vertex] = [source_vertex] + # if only destination vertex is present in adjacency list, add source vertex + # to destination vertex list of adjacent vertices, then create a new vertex + # with source vertex as key and assign a list containing the source vertex + # as it's first adjacent vertex. + elif destination_vertex in self.adj_list: + self.adj_list[destination_vertex].append(source_vertex) + self.adj_list[source_vertex] = [destination_vertex] + # if both source vertex and destination vertex are not present in adjacency + # list, create a new vertex with source vertex as key and assign a list + # containing the destination vertex as it's first adjacent vertex also + # create a new vertex with destination vertex as key and assign a list + # containing the source vertex as it's first adjacent vertex. + else: + self.adj_list[source_vertex] = [destination_vertex] + self.adj_list[destination_vertex] = [source_vertex] + else: # For directed graphs + # if both source vertex and destination vertex are present in adjacency + # list, add destination vertex to source vertex list of adjacent vertices. + if source_vertex in self.adj_list and destination_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + # if only source vertex is present in adjacency list, add destination + # vertex to source vertex list of adjacent vertices and create a new vertex + # with destination vertex as key, which has no adjacent vertex + elif source_vertex in self.adj_list: + self.adj_list[source_vertex].append(destination_vertex) + self.adj_list[destination_vertex] = [] + # if only destination vertex is present in adjacency list, create a new + # vertex with source vertex as key and assign a list containing destination + # vertex as first adjacent vertex + elif destination_vertex in self.adj_list: + self.adj_list[source_vertex] = [destination_vertex] + # if both source vertex and destination vertex are not present in adjacency + # list, create a new vertex with source vertex as key and a list containing + # destination vertex as it's first adjacent vertex. Then create a new vertex + # with destination vertex as key, which has no adjacent vertex + else: + self.adj_list[source_vertex] = [destination_vertex] + self.adj_list[destination_vertex] = [] + + return self + + def __repr__(self) -> str: + return pformat(self.adj_list) if __name__ == "__main__": - al = AdjacencyList() - al.add_edge(0, 1) - al.add_edge(0, 4) - al.add_edge(4, 1) - al.add_edge(4, 3) - al.add_edge(1, 0) - al.add_edge(1, 4) - al.add_edge(1, 3) - al.add_edge(1, 2) - al.add_edge(2, 3) - al.add_edge(3, 4) - - al.print_list() - - # OUTPUT: - # 0 -> 1 -> 4 - # 1 -> 0 -> 4 -> 3 -> 2 - # 2 -> 3 - # 3 -> 4 - # 4 -> 1 -> 3 + import doctest + + doctest.testmod() From ccae2e5c16228db0e649beb57495b11064fda3d5 Mon Sep 17 00:00:00 2001 From: Gaurav Jindal <54955413+jindal2309@users.noreply.github.com> Date: Sun, 17 Jan 2021 23:38:22 -0800 Subject: [PATCH 146/195] Added code to merge two trees (#4121) * Added code to merge two trees * Added doctest and type hints * Added pre-commit --- .../binary_tree/merge_two_binary_trees.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 data_structures/binary_tree/merge_two_binary_trees.py diff --git a/data_structures/binary_tree/merge_two_binary_trees.py b/data_structures/binary_tree/merge_two_binary_trees.py new file mode 100644 index 000000000000..6b202adb3cf5 --- /dev/null +++ b/data_structures/binary_tree/merge_two_binary_trees.py @@ -0,0 +1,93 @@ +#!/usr/local/bin/python3 +""" +Problem Description: Given two binary tree, return the merged tree. +The rule for merging is that if two nodes overlap, then put the value sum of +both nodes to the new value of the merged node. Otherwise, the NOT null node +will be used as the node of new tree. +""" +from typing import Optional + + +class Node: + """ + A binary node has value variable and pointers to its left and right node. + """ + + def __init__(self, value: int = 0) -> None: + self.value = value + self.left: Optional[Node] = None + self.right: Optional[Node] = None + + +def merge_two_binary_trees(tree1: Optional[Node], tree2: Optional[Node]) -> Node: + """ + Returns root node of the merged tree. + + >>> tree1 = Node(5) + >>> tree1.left = Node(6) + >>> tree1.right = Node(7) + >>> tree1.left.left = Node(2) + >>> tree2 = Node(4) + >>> tree2.left = Node(5) + >>> tree2.right = Node(8) + >>> tree2.left.right = Node(1) + >>> tree2.right.right = Node(4) + >>> merged_tree = merge_two_binary_trees(tree1, tree2) + >>> print_preorder(merged_tree) + 9 + 11 + 2 + 1 + 15 + 4 + """ + if tree1 is None: + return tree2 + if tree2 is None: + return tree1 + + tree1.value = tree1.value + tree2.value + tree1.left = merge_two_binary_trees(tree1.left, tree2.left) + tree1.right = merge_two_binary_trees(tree1.right, tree2.right) + return tree1 + + +def print_preorder(root: Optional[Node]) -> None: + """ + Print pre-order traversal of the tree. + + >>> root = Node(1) + >>> root.left = Node(2) + >>> root.right = Node(3) + >>> print_preorder(root) + 1 + 2 + 3 + >>> print_preorder(root.right) + 3 + """ + if root: + print(root.value) + print_preorder(root.left) + print_preorder(root.right) + + +if __name__ == "__main__": + tree1 = Node(1) + tree1.left = Node(2) + tree1.right = Node(3) + tree1.left.left = Node(4) + + tree2 = Node(2) + tree2.left = Node(4) + tree2.right = Node(6) + tree2.left.right = Node(9) + tree2.right.right = Node(5) + + print("Tree1 is: ") + print_preorder(tree1) + print("Tree2 is: ") + print_preorder(tree2) + merged_tree = merge_two_binary_trees(tree1, tree2) + print("Merged Tree is: ") + print_preorder(merged_tree) From 40bc2db83a2842dc1eed1b364e43b2e468f67925 Mon Sep 17 00:00:00 2001 From: Tapajyoti Bose <44058757+ruppysuppy@users.noreply.github.com> Date: Thu, 21 Jan 2021 08:30:47 +0530 Subject: [PATCH 147/195] Added diffie-hellman algorithm (#4128) * updating DIRECTORY.md * feat: added diffie-hellman key exchange algorithm * fix: enforce maxline length = 88 * fix: fixed import order * fix: used flake to correct styling * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 3 +- ciphers/diffie_hellman.py | 271 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 ciphers/diffie_hellman.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 4f17cf9c03ed..d487b39490ed 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -59,6 +59,7 @@ * [Decrypt Caesar With Chi Squared](https://github.com/TheAlgorithms/Python/blob/master/ciphers/decrypt_caesar_with_chi_squared.py) * [Deterministic Miller Rabin](https://github.com/TheAlgorithms/Python/blob/master/ciphers/deterministic_miller_rabin.py) * [Diffie](https://github.com/TheAlgorithms/Python/blob/master/ciphers/diffie.py) + * [Diffie Hellman](https://github.com/TheAlgorithms/Python/blob/master/ciphers/diffie_hellman.py) * [Elgamal Key Generator](https://github.com/TheAlgorithms/Python/blob/master/ciphers/elgamal_key_generator.py) * [Enigma Machine2](https://github.com/TheAlgorithms/Python/blob/master/ciphers/enigma_machine2.py) * [Hill Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/hill_cipher.py) @@ -224,7 +225,6 @@ * [Abbreviation](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/abbreviation.py) * [Bitmask](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/bitmask.py) * [Climbing Stairs](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/climbing_stairs.py) - * [Coin Change](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/coin_change.py) * [Edit Distance](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/edit_distance.py) * [Factorial](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/factorial.py) * [Fast Fibonacci](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/fast_fibonacci.py) @@ -243,6 +243,7 @@ * [Max Non Adjacent Sum](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/max_non_adjacent_sum.py) * [Max Sub Array](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/max_sub_array.py) * [Max Sum Contiguous Subsequence](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/max_sum_contiguous_subsequence.py) + * [Minimum Coin Change](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_coin_change.py) * [Minimum Cost Path](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_cost_path.py) * [Minimum Partition](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_partition.py) * [Minimum Steps To One](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/minimum_steps_to_one.py) diff --git a/ciphers/diffie_hellman.py b/ciphers/diffie_hellman.py new file mode 100644 index 000000000000..ea35b67b483e --- /dev/null +++ b/ciphers/diffie_hellman.py @@ -0,0 +1,271 @@ +from binascii import hexlify +from hashlib import sha256 +from os import urandom + +# RFC 3526 - More Modular Exponential (MODP) Diffie-Hellman groups for +# Internet Key Exchange (IKE) https://tools.ietf.org/html/rfc3526 + +primes = { + # 1536-bit + 5: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, + # 2048-bit + 14: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, + # 3072-bit + 15: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, + # 4096-bit + 16: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, + # 6144-bit + 17: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + + "6DCC4024FFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, + # 8192-bit + 18: { + "prime": int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", + base=16, + ), + "generator": 2, + }, +} + + +class DiffieHellman: + """ + Class to represent the Diffie-Hellman key exchange protocol + + + >>> alice = DiffieHellman() + >>> bob = DiffieHellman() + + >>> alice_private = alice.get_private_key() + >>> alice_public = alice.generate_public_key() + + >>> bob_private = bob.get_private_key() + >>> bob_public = bob.generate_public_key() + + >>> # generating shared key using the DH object + >>> alice_shared = alice.generate_shared_key(bob_public) + >>> bob_shared = bob.generate_shared_key(alice_public) + + >>> assert alice_shared == bob_shared + + >>> # generating shared key using static methods + >>> alice_shared = DiffieHellman.generate_shared_key_static( + ... alice_private, bob_public + ... ) + >>> bob_shared = DiffieHellman.generate_shared_key_static( + ... bob_private, alice_public + ... ) + + >>> assert alice_shared == bob_shared + """ + + # Current minimum recommendation is 2048 bit (group 14) + def __init__(self, group: int = 14) -> None: + if group not in primes: + raise ValueError("Unsupported Group") + self.prime = primes[group]["prime"] + self.generator = primes[group]["generator"] + + self.__private_key = int(hexlify(urandom(32)), base=16) + + def get_private_key(self) -> str: + return hex(self.__private_key)[2:] + + def generate_public_key(self) -> str: + public_key = pow(self.generator, self.__private_key, self.prime) + return hex(public_key)[2:] + + def is_valid_public_key(self, key: int) -> bool: + # check if the other public key is valid based on NIST SP800-56 + if 2 <= key and key <= self.prime - 2: + if pow(key, (self.prime - 1) // 2, self.prime) == 1: + return True + return False + + def generate_shared_key(self, other_key_str: str) -> str: + other_key = int(other_key_str, base=16) + if not self.is_valid_public_key(other_key): + raise ValueError("Invalid public key") + shared_key = pow(other_key, self.__private_key, self.prime) + return sha256(str(shared_key).encode()).hexdigest() + + @staticmethod + def is_valid_public_key_static( + local_private_key_str: str, remote_public_key_str: str, prime: int + ) -> bool: + # check if the other public key is valid based on NIST SP800-56 + if 2 <= remote_public_key_str and remote_public_key_str <= prime - 2: + if pow(remote_public_key_str, (prime - 1) // 2, prime) == 1: + return True + return False + + @staticmethod + def generate_shared_key_static( + local_private_key_str: str, remote_public_key_str: str, group: int = 14 + ) -> str: + local_private_key = int(local_private_key_str, base=16) + remote_public_key = int(remote_public_key_str, base=16) + prime = primes[group]["prime"] + if not DiffieHellman.is_valid_public_key_static( + local_private_key, remote_public_key, prime + ): + raise ValueError("Invalid public key") + shared_key = pow(remote_public_key, local_private_key, prime) + return sha256(str(shared_key).encode()).hexdigest() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ced6ea8045d35bfab27a3a1b27ae00ba93546ea0 Mon Sep 17 00:00:00 2001 From: Steve Kim <54872857+SteveKimSR@users.noreply.github.com> Date: Fri, 22 Jan 2021 13:40:21 +0900 Subject: [PATCH 148/195] [mypy] Add/fix type annotations for similarity search in machine learning (#4088) * [mypy] Add/fix type annotations for similarity search in machine learning * fix annotation * fix annotation (Union) * isort --- machine_learning/similarity_search.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/machine_learning/similarity_search.py b/machine_learning/similarity_search.py index 6bfb12ed88cb..af845c9109b1 100644 --- a/machine_learning/similarity_search.py +++ b/machine_learning/similarity_search.py @@ -8,6 +8,7 @@ 2. distance between the vector and the nearest vector (float) """ import math +from typing import List, Union import numpy as np @@ -30,7 +31,9 @@ def euclidean(input_a: np.ndarray, input_b: np.ndarray) -> float: return math.sqrt(sum(pow(a - b, 2) for a, b in zip(input_a, input_b))) -def similarity_search(dataset: np.ndarray, value_array: np.ndarray) -> list: +def similarity_search( + dataset: np.ndarray, value_array: np.ndarray +) -> List[List[Union[List[float], float]]]: """ :param dataset: Set containing the vectors. Should be ndarray. :param value_array: vector/vectors we want to know the nearest vector from dataset. From 59c0277c64676fcf09c59bd51bf0d89fcf19f41d Mon Sep 17 00:00:00 2001 From: ayushbisht2001 <61404154+ayushbisht2001@users.noreply.github.com> Date: Wed, 27 Jan 2021 15:54:57 +0530 Subject: [PATCH 149/195] add reverse_bits.py (#4120) * add reverse_bits.py * check * Delete binary_xor_operator_new.py * Fix All the errors Co-authored-by: xcodz-dot --- bit_manipulation/reverse_bits.py | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 bit_manipulation/reverse_bits.py diff --git a/bit_manipulation/reverse_bits.py b/bit_manipulation/reverse_bits.py new file mode 100644 index 000000000000..55608ae12908 --- /dev/null +++ b/bit_manipulation/reverse_bits.py @@ -0,0 +1,85 @@ +def get_reverse_bit_string(number: int) -> str: + """ + return the bit string of an integer + + >>> get_reverse_bit_string(9) + '10010000000000000000000000000000' + >>> get_reverse_bit_string(43) + '11010100000000000000000000000000' + >>> get_reverse_bit_string(2873) + '10011100110100000000000000000000' + >>> get_reverse_bit_string("this is not a number") + Traceback (most recent call last): + ... + TypeError: operation can not be conducted on a object of type str + """ + if not isinstance(number, int): + raise TypeError( + "operation can not be conducted on a object of type " + f"{type(number).__name__}" + ) + bit_string = "" + for _ in range(0, 32): + bit_string += str(number % 2) + number = number >> 1 + return bit_string + + +def reverse_bit(number: int) -> str: + """ + Take in an 32 bit integer, reverse its bits, + return a string of reverse bits + + result of a reverse_bit and operation on the integer provided. + + >>> reverse_bit(25) + '00000000000000000000000000011001' + >>> reverse_bit(37) + '00000000000000000000000000100101' + >>> reverse_bit(21) + '00000000000000000000000000010101' + >>> reverse_bit(58) + '00000000000000000000000000111010' + >>> reverse_bit(0) + '00000000000000000000000000000000' + >>> reverse_bit(256) + '00000000000000000000000100000000' + >>> reverse_bit(-1) + Traceback (most recent call last): + ... + ValueError: the value of input must be positive + + >>> reverse_bit(1.1) + Traceback (most recent call last): + ... + TypeError: Input value must be a 'int' type + + >>> reverse_bit("0") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if number < 0: + raise ValueError("the value of input must be positive") + elif isinstance(number, float): + raise TypeError("Input value must be a 'int' type") + elif isinstance(number, str): + raise TypeError("'<' not supported between instances of 'str' and 'int'") + result = 0 + # iterator over [1 to 32],since we are dealing with 32 bit integer + for _ in range(1, 33): + # left shift the bits by unity + result = result << 1 + # get the end bit + end_bit = number % 2 + # right shift the bits by unity + number = number >> 1 + # add that bit to our ans + result = result | end_bit + return get_reverse_bit_string(result) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 0d436415b9002ec6b673b2be50a96945c9c3fc71 Mon Sep 17 00:00:00 2001 From: Ayush Raj Date: Thu, 4 Feb 2021 22:28:29 +0530 Subject: [PATCH 150/195] [mypy] Add/fix type annotations for boolean_algebra (#4172) * [mypy] Add/fix type annotations for boolean_algebra * [mypy] Add/fix type annotations for boolean_algebra * [mypy] Add/fix annotations for boolean_algebra --- boolean_algebra/quine_mc_cluskey.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/boolean_algebra/quine_mc_cluskey.py b/boolean_algebra/quine_mc_cluskey.py index 19bac336f6c5..70cdf25a701d 100644 --- a/boolean_algebra/quine_mc_cluskey.py +++ b/boolean_algebra/quine_mc_cluskey.py @@ -1,10 +1,13 @@ +from typing import List + + def compare_string(string1: str, string2: str) -> str: """ >>> compare_string('0010','0110') '0_10' >>> compare_string('0110','1101') - -1 + 'X' """ l1 = list(string1) l2 = list(string2) @@ -14,12 +17,12 @@ def compare_string(string1: str, string2: str) -> str: count += 1 l1[i] = "_" if count > 1: - return -1 + return "X" else: return "".join(l1) -def check(binary: [str]) -> [str]: +def check(binary: List[str]) -> List[str]: """ >>> check(['0.00.01.5']) ['0.00.01.5'] @@ -31,7 +34,7 @@ def check(binary: [str]) -> [str]: for i in range(len(binary)): for j in range(i + 1, len(binary)): k = compare_string(binary[i], binary[j]) - if k != -1: + if k != "X": check1[i] = "*" check1[j] = "*" temp.append(k) @@ -43,7 +46,7 @@ def check(binary: [str]) -> [str]: binary = list(set(temp)) -def decimal_to_binary(no_of_variable: int, minterms: [float]) -> [str]: +def decimal_to_binary(no_of_variable: int, minterms: List[float]) -> List[str]: """ >>> decimal_to_binary(3,[1.5]) ['0.00.01.5'] @@ -79,7 +82,7 @@ def is_for_table(string1: str, string2: str, count: int) -> bool: return False -def selection(chart: [[int]], prime_implicants: [str]) -> [str]: +def selection(chart: List[List[int]], prime_implicants: List[str]) -> List[str]: """ >>> selection([[1]],['0.00.01.5']) ['0.00.01.5'] @@ -126,7 +129,9 @@ def selection(chart: [[int]], prime_implicants: [str]) -> [str]: chart[j][i] = 0 -def prime_implicant_chart(prime_implicants: [str], binary: [str]) -> [[int]]: +def prime_implicant_chart( + prime_implicants: List[str], binary: List[str] +) -> List[List[int]]: """ >>> prime_implicant_chart(['0.00.01.5'],['0.00.01.5']) [[1]] From dba0d7ea68fa899418f9e167243c523eaddf32ef Mon Sep 17 00:00:00 2001 From: Hao LI <8520588+Leo-LiHao@users.noreply.github.com> Date: Fri, 5 Feb 2021 00:59:38 +0800 Subject: [PATCH 151/195] [mypy] Add/fix type annotations for binary trees in data structures (#4085) * fix mypy: data_structures:binary_tree * mypy --strict for binary_trees in data_structures * fix pre-commit Co-authored-by: LiHao --- .../binary_search_tree_recursive.py | 99 ++++++++++++------- .../binary_tree/lazy_segment_tree.py | 9 +- data_structures/binary_tree/treap.py | 33 ++++--- 3 files changed, 84 insertions(+), 57 deletions(-) diff --git a/data_structures/binary_tree/binary_search_tree_recursive.py b/data_structures/binary_tree/binary_search_tree_recursive.py index f1e46e33cd24..a05e28a7bd54 100644 --- a/data_structures/binary_tree/binary_search_tree_recursive.py +++ b/data_structures/binary_tree/binary_search_tree_recursive.py @@ -8,21 +8,22 @@ python binary_search_tree_recursive.py """ import unittest +from typing import Iterator, Optional class Node: - def __init__(self, label: int, parent): + def __init__(self, label: int, parent: Optional["Node"]) -> None: self.label = label self.parent = parent - self.left = None - self.right = None + self.left: Optional[Node] = None + self.right: Optional[Node] = None class BinarySearchTree: - def __init__(self): - self.root = None + def __init__(self) -> None: + self.root: Optional[Node] = None - def empty(self): + def empty(self) -> None: """ Empties the tree @@ -46,7 +47,7 @@ def is_empty(self) -> bool: """ return self.root is None - def put(self, label: int): + def put(self, label: int) -> None: """ Put a new node in the tree @@ -65,7 +66,9 @@ def put(self, label: int): """ self.root = self._put(self.root, label) - def _put(self, node: Node, label: int, parent: Node = None) -> Node: + def _put( + self, node: Optional[Node], label: int, parent: Optional[Node] = None + ) -> Node: if node is None: node = Node(label, parent) else: @@ -95,7 +98,7 @@ def search(self, label: int) -> Node: """ return self._search(self.root, label) - def _search(self, node: Node, label: int) -> Node: + def _search(self, node: Optional[Node], label: int) -> Node: if node is None: raise Exception(f"Node with label {label} does not exist") else: @@ -106,7 +109,7 @@ def _search(self, node: Node, label: int) -> Node: return node - def remove(self, label: int): + def remove(self, label: int) -> None: """ Removes a node in the tree @@ -122,13 +125,7 @@ def remove(self, label: int): Exception: Node with label 3 does not exist """ node = self.search(label) - if not node.right and not node.left: - self._reassign_nodes(node, None) - elif not node.right and node.left: - self._reassign_nodes(node, node.left) - elif node.right and not node.left: - self._reassign_nodes(node, node.right) - else: + if node.right and node.left: lowest_node = self._get_lowest_node(node.right) lowest_node.left = node.left lowest_node.right = node.right @@ -136,8 +133,14 @@ def remove(self, label: int): if node.right: node.right.parent = lowest_node self._reassign_nodes(node, lowest_node) + elif not node.right and node.left: + self._reassign_nodes(node, node.left) + elif node.right and not node.left: + self._reassign_nodes(node, node.right) + else: + self._reassign_nodes(node, None) - def _reassign_nodes(self, node: Node, new_children: Node): + def _reassign_nodes(self, node: Node, new_children: Optional[Node]) -> None: if new_children: new_children.parent = node.parent @@ -192,7 +195,7 @@ def get_max_label(self) -> int: >>> t.get_max_label() 10 """ - if self.is_empty(): + if self.root is None: raise Exception("Binary search tree is empty") node = self.root @@ -216,7 +219,7 @@ def get_min_label(self) -> int: >>> t.get_min_label() 8 """ - if self.is_empty(): + if self.root is None: raise Exception("Binary search tree is empty") node = self.root @@ -225,7 +228,7 @@ def get_min_label(self) -> int: return node.label - def inorder_traversal(self) -> list: + def inorder_traversal(self) -> Iterator[Node]: """ Return the inorder traversal of the tree @@ -241,13 +244,13 @@ def inorder_traversal(self) -> list: """ return self._inorder_traversal(self.root) - def _inorder_traversal(self, node: Node) -> list: + def _inorder_traversal(self, node: Optional[Node]) -> Iterator[Node]: if node is not None: yield from self._inorder_traversal(node.left) yield node yield from self._inorder_traversal(node.right) - def preorder_traversal(self) -> list: + def preorder_traversal(self) -> Iterator[Node]: """ Return the preorder traversal of the tree @@ -263,7 +266,7 @@ def preorder_traversal(self) -> list: """ return self._preorder_traversal(self.root) - def _preorder_traversal(self, node: Node) -> list: + def _preorder_traversal(self, node: Optional[Node]) -> Iterator[Node]: if node is not None: yield node yield from self._preorder_traversal(node.left) @@ -272,7 +275,7 @@ def _preorder_traversal(self, node: Node) -> list: class BinarySearchTreeTest(unittest.TestCase): @staticmethod - def _get_binary_search_tree(): + def _get_binary_search_tree() -> BinarySearchTree: r""" 8 / \ @@ -298,7 +301,7 @@ def _get_binary_search_tree(): return t - def test_put(self): + def test_put(self) -> None: t = BinarySearchTree() assert t.is_empty() @@ -306,6 +309,7 @@ def test_put(self): r""" 8 """ + assert t.root is not None assert t.root.parent is None assert t.root.label == 8 @@ -315,6 +319,7 @@ def test_put(self): \ 10 """ + assert t.root.right is not None assert t.root.right.parent == t.root assert t.root.right.label == 10 @@ -324,6 +329,7 @@ def test_put(self): / \ 3 10 """ + assert t.root.left is not None assert t.root.left.parent == t.root assert t.root.left.label == 3 @@ -335,6 +341,7 @@ def test_put(self): \ 6 """ + assert t.root.left.right is not None assert t.root.left.right.parent == t.root.left assert t.root.left.right.label == 6 @@ -346,13 +353,14 @@ def test_put(self): / \ 1 6 """ + assert t.root.left.left is not None assert t.root.left.left.parent == t.root.left assert t.root.left.left.label == 1 with self.assertRaises(Exception): t.put(1) - def test_search(self): + def test_search(self) -> None: t = self._get_binary_search_tree() node = t.search(6) @@ -364,7 +372,7 @@ def test_search(self): with self.assertRaises(Exception): t.search(2) - def test_remove(self): + def test_remove(self) -> None: t = self._get_binary_search_tree() t.remove(13) @@ -379,6 +387,9 @@ def test_remove(self): \ 5 """ + assert t.root is not None + assert t.root.right is not None + assert t.root.right.right is not None assert t.root.right.right.right is None assert t.root.right.right.left is None @@ -394,6 +405,9 @@ def test_remove(self): \ 5 """ + assert t.root.left is not None + assert t.root.left.right is not None + assert t.root.left.right.left is not None assert t.root.left.right.right is None assert t.root.left.right.left.label == 4 @@ -407,6 +421,8 @@ def test_remove(self): \ 5 """ + assert t.root.left.left is not None + assert t.root.left.right.right is not None assert t.root.left.left.label == 1 assert t.root.left.right.label == 4 assert t.root.left.right.right.label == 5 @@ -422,6 +438,7 @@ def test_remove(self): / \ \ 1 5 14 """ + assert t.root is not None assert t.root.left.label == 4 assert t.root.left.right.label == 5 assert t.root.left.left.label == 1 @@ -437,13 +454,15 @@ def test_remove(self): / \ 1 14 """ + assert t.root.left is not None + assert t.root.left.left is not None assert t.root.left.label == 5 assert t.root.left.right is None assert t.root.left.left.label == 1 assert t.root.left.parent == t.root assert t.root.left.left.parent == t.root.left - def test_remove_2(self): + def test_remove_2(self) -> None: t = self._get_binary_search_tree() t.remove(3) @@ -456,6 +475,12 @@ def test_remove_2(self): / \ / 5 7 13 """ + assert t.root is not None + assert t.root.left is not None + assert t.root.left.left is not None + assert t.root.left.right is not None + assert t.root.left.right.left is not None + assert t.root.left.right.right is not None assert t.root.left.label == 4 assert t.root.left.right.label == 6 assert t.root.left.left.label == 1 @@ -466,25 +491,25 @@ def test_remove_2(self): assert t.root.left.left.parent == t.root.left assert t.root.left.right.left.parent == t.root.left.right - def test_empty(self): + def test_empty(self) -> None: t = self._get_binary_search_tree() t.empty() assert t.root is None - def test_is_empty(self): + def test_is_empty(self) -> None: t = self._get_binary_search_tree() assert not t.is_empty() t.empty() assert t.is_empty() - def test_exists(self): + def test_exists(self) -> None: t = self._get_binary_search_tree() assert t.exists(6) assert not t.exists(-1) - def test_get_max_label(self): + def test_get_max_label(self) -> None: t = self._get_binary_search_tree() assert t.get_max_label() == 14 @@ -493,7 +518,7 @@ def test_get_max_label(self): with self.assertRaises(Exception): t.get_max_label() - def test_get_min_label(self): + def test_get_min_label(self) -> None: t = self._get_binary_search_tree() assert t.get_min_label() == 1 @@ -502,20 +527,20 @@ def test_get_min_label(self): with self.assertRaises(Exception): t.get_min_label() - def test_inorder_traversal(self): + def test_inorder_traversal(self) -> None: t = self._get_binary_search_tree() inorder_traversal_nodes = [i.label for i in t.inorder_traversal()] assert inorder_traversal_nodes == [1, 3, 4, 5, 6, 7, 8, 10, 13, 14] - def test_preorder_traversal(self): + def test_preorder_traversal(self) -> None: t = self._get_binary_search_tree() preorder_traversal_nodes = [i.label for i in t.preorder_traversal()] assert preorder_traversal_nodes == [8, 3, 1, 6, 4, 5, 7, 10, 14, 13] -def binary_search_tree_example(): +def binary_search_tree_example() -> None: r""" Example 8 diff --git a/data_structures/binary_tree/lazy_segment_tree.py b/data_structures/binary_tree/lazy_segment_tree.py index 5bc79e74efcd..9066db294613 100644 --- a/data_structures/binary_tree/lazy_segment_tree.py +++ b/data_structures/binary_tree/lazy_segment_tree.py @@ -1,6 +1,7 @@ from __future__ import annotations import math +from typing import List, Union class SegmentTree: @@ -37,7 +38,7 @@ def right(self, idx: int) -> int: return idx * 2 + 1 def build( - self, idx: int, left_element: int, right_element: int, A: list[int] + self, idx: int, left_element: int, right_element: int, A: List[int] ) -> None: if left_element == right_element: self.segment_tree[idx] = A[left_element - 1] @@ -88,7 +89,7 @@ def update( # query with O(lg n) def query( self, idx: int, left_element: int, right_element: int, a: int, b: int - ) -> int: + ) -> Union[int, float]: """ query(1, 1, size, a, b) for query max of [a,b] >>> A = [1, 2, -4, 7, 3, -5, 6, 11, -20, 9, 14, 15, 5, 2, -8] @@ -118,8 +119,8 @@ def query( q2 = self.query(self.right(idx), mid + 1, right_element, a, b) return max(q1, q2) - def __str__(self) -> None: - return [self.query(1, 1, self.size, i, i) for i in range(1, self.size + 1)] + def __str__(self) -> str: + return str([self.query(1, 1, self.size, i, i) for i in range(1, self.size + 1)]) if __name__ == "__main__": diff --git a/data_structures/binary_tree/treap.py b/data_structures/binary_tree/treap.py index 26648f7aba61..a09dcc928143 100644 --- a/data_structures/binary_tree/treap.py +++ b/data_structures/binary_tree/treap.py @@ -3,6 +3,7 @@ from __future__ import annotations from random import random +from typing import Optional, Tuple class Node: @@ -11,13 +12,13 @@ class Node: Treap is a binary tree by value and heap by priority """ - def __init__(self, value: int = None): + def __init__(self, value: Optional[int] = None): self.value = value self.prior = random() - self.left = None - self.right = None + self.left: Optional[Node] = None + self.right: Optional[Node] = None - def __repr__(self): + def __repr__(self) -> str: from pprint import pformat if self.left is None and self.right is None: @@ -27,14 +28,14 @@ def __repr__(self): {f"{self.value}: {self.prior:.5}": (self.left, self.right)}, indent=1 ) - def __str__(self): + def __str__(self) -> str: value = str(self.value) + " " left = str(self.left or "") right = str(self.right or "") return value + left + right -def split(root: Node, value: int) -> tuple[Node, Node]: +def split(root: Optional[Node], value: int) -> Tuple[Optional[Node], Optional[Node]]: """ We split current tree into 2 trees with value: @@ -42,9 +43,9 @@ def split(root: Node, value: int) -> tuple[Node, Node]: Right tree contains all values greater or equal, than split value """ if root is None: # None tree is split into 2 Nones - return (None, None) + return None, None elif root.value is None: - return (None, None) + return None, None else: if value < root.value: """ @@ -54,16 +55,16 @@ def split(root: Node, value: int) -> tuple[Node, Node]: Right tree's left son: right part of that split """ left, root.left = split(root.left, value) - return (left, root) + return left, root else: """ Just symmetric to previous case """ root.right, right = split(root.right, value) - return (root, right) + return root, right -def merge(left: Node, right: Node) -> Node: +def merge(left: Optional[Node], right: Optional[Node]) -> Optional[Node]: """ We merge 2 trees into one. Note: all left tree's values must be less than all right tree's @@ -85,7 +86,7 @@ def merge(left: Node, right: Node) -> Node: return right -def insert(root: Node, value: int) -> Node: +def insert(root: Optional[Node], value: int) -> Optional[Node]: """ Insert element @@ -98,7 +99,7 @@ def insert(root: Node, value: int) -> Node: return merge(merge(left, node), right) -def erase(root: Node, value: int) -> Node: +def erase(root: Optional[Node], value: int) -> Optional[Node]: """ Erase element @@ -111,7 +112,7 @@ def erase(root: Node, value: int) -> Node: return merge(left, right) -def inorder(root: Node): +def inorder(root: Optional[Node]) -> None: """ Just recursive print of a tree """ @@ -123,7 +124,7 @@ def inorder(root: Node): inorder(root.right) -def interactTreap(root, args): +def interactTreap(root: Optional[Node], args: str) -> Optional[Node]: """ Commands: + value to add value into treap @@ -160,7 +161,7 @@ def interactTreap(root, args): return root -def main(): +def main() -> None: """After each command, program prints treap""" root = None print( From 4e5865b82577ec0d4f9b3451d5702af2e97b5119 Mon Sep 17 00:00:00 2001 From: Abdeldjaouad Nusayr Medakene <31663979+MrGeek1337@users.noreply.github.com> Date: Tue, 9 Feb 2021 17:13:48 +0100 Subject: [PATCH 152/195] Create slowsort.py (#3865) * Create slowsort.py added slowsort algorithm implementation to sorts * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py * Update slowsort.py --- sorts/slowsort.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 sorts/slowsort.py diff --git a/sorts/slowsort.py b/sorts/slowsort.py new file mode 100644 index 000000000000..53bb14554ee2 --- /dev/null +++ b/sorts/slowsort.py @@ -0,0 +1,63 @@ +""" +Slowsort is a sorting algorithm. It is of humorous nature and not useful. +It's based on the principle of multiply and surrender, +a tongue-in-cheek joke of divide and conquer. +It was published in 1986 by Andrei Broder and Jorge Stolfi +in their paper Pessimal Algorithms and Simplexity Analysis +(a parody of optimal algorithms and complexity analysis). + +Source: https://en.wikipedia.org/wiki/Slowsort +""" + +from typing import Optional + + +def slowsort( + sequence: list, start: Optional[int] = None, end: Optional[int] = None +) -> None: + """ + Sorts sequence[start..end] (both inclusive) in-place. + start defaults to 0 if not given. + end defaults to len(sequence) - 1 if not given. + It returns None. + >>> seq = [1, 6, 2, 5, 3, 4, 4, 5]; slowsort(seq); seq + [1, 2, 3, 4, 4, 5, 5, 6] + >>> seq = []; slowsort(seq); seq + [] + >>> seq = [2]; slowsort(seq); seq + [2] + >>> seq = [1, 2, 3, 4]; slowsort(seq); seq + [1, 2, 3, 4] + >>> seq = [4, 3, 2, 1]; slowsort(seq); seq + [1, 2, 3, 4] + >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; slowsort(seq, 2, 7); seq + [9, 8, 2, 3, 4, 5, 6, 7, 1, 0] + >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; slowsort(seq, end = 4); seq + [5, 6, 7, 8, 9, 4, 3, 2, 1, 0] + >>> seq = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; slowsort(seq, start = 5); seq + [9, 8, 7, 6, 5, 0, 1, 2, 3, 4] + """ + if start is None: + start = 0 + + if end is None: + end = len(sequence) - 1 + + if start >= end: + return + + mid = (start + end) // 2 + + slowsort(sequence, start, mid) + slowsort(sequence, mid + 1, end) + + if sequence[end] < sequence[mid]: + sequence[end], sequence[mid] = sequence[mid], sequence[end] + + slowsort(sequence, start, end - 1) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() From 43b985baccf455c29583b506c21fc74eb8c3eb51 Mon Sep 17 00:00:00 2001 From: Ocean Monjur <75680423+OCM-7898@users.noreply.github.com> Date: Thu, 11 Feb 2021 22:49:53 +0600 Subject: [PATCH 153/195] odd_even_sort.py (#4199) * odd_even_sort.py * Update odd_even_sort.py * Update odd_even_sort.py --- sorts/odd_even_sort.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 sorts/odd_even_sort.py diff --git a/sorts/odd_even_sort.py b/sorts/odd_even_sort.py new file mode 100644 index 000000000000..557337ee77bc --- /dev/null +++ b/sorts/odd_even_sort.py @@ -0,0 +1,47 @@ +"""For reference +https://en.wikipedia.org/wiki/Odd%E2%80%93even_sort +""" + + +def odd_even_sort(input_list: list) -> list: + """this algorithm uses the same idea of bubblesort, + but by first dividing in two phase (odd and even). + Originally developed for use on parallel processors + with local interconnections. + :param collection: mutable ordered sequence of elements + :return: same collection in ascending order + Examples: + >>> odd_even_sort([5 , 4 ,3 ,2 ,1]) + [1, 2, 3, 4, 5] + >>> odd_even_sort([]) + [] + >>> odd_even_sort([-10 ,-1 ,10 ,2]) + [-10, -1, 2, 10] + >>> odd_even_sort([1 ,2 ,3 ,4]) + [1, 2, 3, 4] + """ + sorted = False + while sorted is False: # Until all the indices are traversed keep looping + sorted = True + for i in range(0, len(input_list) - 1, 2): # iterating over all even indices + if input_list[i] > input_list[i + 1]: + + input_list[i], input_list[i + 1] = input_list[i + 1], input_list[i] + # swapping if elements not in order + sorted = False + + for i in range(1, len(input_list) - 1, 2): # iterating over all odd indices + if input_list[i] > input_list[i + 1]: + input_list[i], input_list[i + 1] = input_list[i + 1], input_list[i] + # swapping if elements not in order + sorted = False + return input_list + + +if __name__ == "__main__": + print("Enter list to be sorted") + input_list = [int(x) for x in input().split()] + # inputing elements of the list in one line + sorted_list = odd_even_sort(input_list) + print("The sorted list is") + print(sorted_list) From 2b09c4b5575f009096d9b8036ad8dfd3078cfb6c Mon Sep 17 00:00:00 2001 From: Ayush Bisht <61404154+ayushbisht2001@users.noreply.github.com> Date: Fri, 12 Feb 2021 07:59:24 +0530 Subject: [PATCH 154/195] add count_number_of_one_bits.py (#4195) * count-bits * update --- bit_manipulation/count_number_of_one_bits.py | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 bit_manipulation/count_number_of_one_bits.py diff --git a/bit_manipulation/count_number_of_one_bits.py b/bit_manipulation/count_number_of_one_bits.py new file mode 100644 index 000000000000..51fd2b630483 --- /dev/null +++ b/bit_manipulation/count_number_of_one_bits.py @@ -0,0 +1,34 @@ +def get_set_bits_count(number: int) -> int: + """ + Count the number of set bits in a 32 bit integer + >>> get_set_bits_count(25) + 3 + >>> get_set_bits_count(37) + 3 + >>> get_set_bits_count(21) + 3 + >>> get_set_bits_count(58) + 4 + >>> get_set_bits_count(0) + 0 + >>> get_set_bits_count(256) + 1 + >>> get_set_bits_count(-1) + Traceback (most recent call last): + ... + ValueError: the value of input must be positive + """ + if number < 0: + raise ValueError("the value of input must be positive") + result = 0 + while number: + if number % 2 == 1: + result += 1 + number = number >> 1 + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 5aaec9eac38dde0e3af70c848a54fbd1fdf9afa6 Mon Sep 17 00:00:00 2001 From: MarineJoker Date: Wed, 17 Feb 2021 22:28:50 +0800 Subject: [PATCH 155/195] Quick sort with lomuto partition (#3875) * add quick sort algorithm with Lomuto partition * fix(lomuto_partition): fix snake_case --- sorts/quick_sort_3_partition.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sorts/quick_sort_3_partition.py b/sorts/quick_sort_3_partition.py index 18c6e0f876d2..1a6db6a364f0 100644 --- a/sorts/quick_sort_3_partition.py +++ b/sorts/quick_sort_3_partition.py @@ -18,6 +18,53 @@ def quick_sort_3partition(sorting: list, left: int, right: int) -> None: quick_sort_3partition(sorting, b + 1, right) +def quick_sort_lomuto_partition(sorting: list, left: int, right: int) -> None: + """ + A pure Python implementation of quick sort algorithm(in-place) + with Lomuto partition scheme: + https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme + + :param sorting: sort list + :param left: left endpoint of sorting + :param right: right endpoint of sorting + :return: None + + Examples: + >>> nums1 = [0, 5, 3, 1, 2] + >>> quick_sort_lomuto_partition(nums1, 0, 4) + >>> nums1 + [0, 1, 2, 3, 5] + >>> nums2 = [] + >>> quick_sort_lomuto_partition(nums2, 0, 0) + >>> nums2 + [] + >>> nums3 = [-2, 5, 0, -4] + >>> quick_sort_lomuto_partition(nums3, 0, 3) + >>> nums3 + [-4, -2, 0, 5] + """ + if left < right: + pivot_index = lomuto_partition(sorting, left, right) + quick_sort_lomuto_partition(sorting, left, pivot_index - 1) + quick_sort_lomuto_partition(sorting, pivot_index + 1, right) + + +def lomuto_partition(sorting: list, left: int, right: int) -> int: + """ + Example: + >>> lomuto_partition([1,5,7,6], 0, 3) + 2 + """ + pivot = sorting[right] + store_index = left + for i in range(left, right): + if sorting[i] < pivot: + sorting[store_index], sorting[i] = sorting[i], sorting[store_index] + store_index += 1 + sorting[right], sorting[store_index] = sorting[store_index], sorting[right] + return store_index + + def three_way_radix_quicksort(sorting: list) -> list: """ Three-way radix quicksort: From 898113738b00e3c508c4591a1864dfbfadf53d43 Mon Sep 17 00:00:00 2001 From: Liu Baolin--CN Date: Sat, 20 Feb 2021 02:15:19 +0800 Subject: [PATCH 156/195] Update LICENSE.md (#4210) --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 3b7951527ab3..c3c2857cd312 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 The Algorithms +Copyright (c) 2016-2021 The Algorithms Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 3d85def5ba5f925d24443cd478e8207177dfcc09 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Feb 2021 18:39:39 +0530 Subject: [PATCH 157/195] Implementation of the algorithm for the Koch snowflake (#4207) * Add files via upload Implementation of the algorithm for the Koch snowflake * added underscore to variable names * added newline and comment I fixed the sorting of the imports and I added a comment to the plot-function to explain what it does and why it doesn't use a doctest. Thank you to user mrmaxguns for suggesting these changes. * fixed accidental newline in the middle of expression * improved looping * moved "koch_snowflake.py" from "other" to "graphics" * Update koch_snowflake.py Co-authored-by: Christian Clauss --- graphics/koch_snowflake.py | 116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 graphics/koch_snowflake.py diff --git a/graphics/koch_snowflake.py b/graphics/koch_snowflake.py new file mode 100644 index 000000000000..07c1835b41ed --- /dev/null +++ b/graphics/koch_snowflake.py @@ -0,0 +1,116 @@ +""" +Description + The Koch snowflake is a fractal curve and one of the earliest fractals to + have been described. The Koch snowflake can be built up iteratively, in a + sequence of stages. The first stage is an equilateral triangle, and each + successive stage is formed by adding outward bends to each side of the + previous stage, making smaller equilateral triangles. + This can be achieved through the following steps for each line: + 1. divide the line segment into three segments of equal length. + 2. draw an equilateral triangle that has the middle segment from step 1 + as its base and points outward. + 3. remove the line segment that is the base of the triangle from step 2. + (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) + (for a more detailed explanation and an implementation in the + Processing language, see https://natureofcode.com/book/chapter-8-fractals/ + #84-the-koch-curve-and-the-arraylist-technique ) + +Requirements (pip): + - matplotlib + - numpy +""" + + +from __future__ import annotations + +import matplotlib.pyplot as plt # type: ignore +import numpy + +# initial triangle of Koch snowflake +VECTOR_1 = numpy.array([0, 0]) +VECTOR_2 = numpy.array([0.5, 0.8660254]) +VECTOR_3 = numpy.array([1, 0]) +INITIAL_VECTORS = [VECTOR_1, VECTOR_2, VECTOR_3, VECTOR_1] + +# uncomment for simple Koch curve instead of Koch snowflake +# INITIAL_VECTORS = [VECTOR_1, VECTOR_3] + + +def iterate(initial_vectors: list[numpy.ndarray], steps: int) -> list[numpy.ndarray]: + """ + Go through the number of iterations determined by the argument "steps". + Be careful with high values (above 5) since the time to calculate increases + exponentially. + >>> iterate([numpy.array([0, 0]), numpy.array([1, 0])], 1) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + vectors = initial_vectors + for i in range(steps): + vectors = iteration_step(vectors) + return vectors + + +def iteration_step(vectors: list[numpy.ndarray]) -> list[numpy.ndarray]: + """ + Loops through each pair of adjacent vectors. Each line between two adjacent + vectors is divided into 4 segments by adding 3 additional vectors in-between + the original two vectors. The vector in the middle is constructed through a + 60 degree rotation so it is bent outwards. + >>> iteration_step([numpy.array([0, 0]), numpy.array([1, 0])]) + [array([0, 0]), array([0.33333333, 0. ]), array([0.5 , \ +0.28867513]), array([0.66666667, 0. ]), array([1, 0])] + """ + new_vectors = [] + for i, start_vector in enumerate(vectors[:-1]): + end_vector = vectors[i + 1] + new_vectors.append(start_vector) + difference_vector = end_vector - start_vector + new_vectors.append(start_vector + difference_vector / 3) + new_vectors.append( + start_vector + difference_vector / 3 + rotate(difference_vector / 3, 60) + ) + new_vectors.append(start_vector + difference_vector * 2 / 3) + new_vectors.append(vectors[-1]) + return new_vectors + + +def rotate(vector: numpy.ndarray, angle_in_degrees: float) -> numpy.ndarray: + """ + Standard rotation of a 2D vector with a rotation matrix + (see https://en.wikipedia.org/wiki/Rotation_matrix ) + >>> rotate(numpy.array([1, 0]), 60) + array([0.5 , 0.8660254]) + >>> rotate(numpy.array([1, 0]), 90) + array([6.123234e-17, 1.000000e+00]) + """ + theta = numpy.radians(angle_in_degrees) + c, s = numpy.cos(theta), numpy.sin(theta) + rotation_matrix = numpy.array(((c, -s), (s, c))) + return numpy.dot(rotation_matrix, vector) + + +def plot(vectors: list[numpy.ndarray]) -> None: + """ + Utility function to plot the vectors using matplotlib.pyplot + No doctest was implemented since this function does not have a return value + """ + # avoid stretched display of graph + axes = plt.gca() + axes.set_aspect("equal") + + # matplotlib.pyplot.plot takes a list of all x-coordinates and a list of all + # y-coordinates as inputs, which are constructed from the vector-list using + # zip() + x_coordinates, y_coordinates = zip(*vectors) + plt.plot(x_coordinates, y_coordinates) + plt.show() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + processed_vectors = iterate(INITIAL_VECTORS, 5) + plot(processed_vectors) From 2a7351e3d645110ca161fb701dac8bc90f5b9c79 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 20 Feb 2021 22:10:23 +0000 Subject: [PATCH 158/195] [mypy] Add/fix type annotations for quick_sort(#4085) (#4215) Co-authored-by: goodm2 <4qjpngu8mem8cz> --- sorts/quick_sort.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sorts/quick_sort.py b/sorts/quick_sort.py index c6687a7fa8d5..6f51f6eca7db 100644 --- a/sorts/quick_sort.py +++ b/sorts/quick_sort.py @@ -7,6 +7,7 @@ For manual testing run: python3 quick_sort.py """ +from typing import List def quick_sort(collection: list) -> list: @@ -26,8 +27,8 @@ def quick_sort(collection: list) -> list: if len(collection) < 2: return collection pivot = collection.pop() # Use the last element as the first pivot - greater = [] # All elements greater than pivot - lesser = [] # All elements less than or equal to pivot + greater: List[int] = [] # All elements greater than pivot + lesser: List[int] = [] # All elements less than or equal to pivot for element in collection: (greater if element > pivot else lesser).append(element) return quick_sort(lesser) + [pivot] + quick_sort(greater) From a7b6b24889445219832b2b760ba96bdadff2bb28 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 22 Feb 2021 05:24:29 +0530 Subject: [PATCH 159/195] [mypy] Add/fix type annotations for "conways_game_of_life.py" & "one_dimensional.py" (#4216) Related Issue: #4052 --- cellular_automata/conways_game_of_life.py | 2 +- cellular_automata/one_dimensional.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py index 321baa3a3794..dc349b7ac507 100644 --- a/cellular_automata/conways_game_of_life.py +++ b/cellular_automata/conways_game_of_life.py @@ -7,7 +7,7 @@ from typing import List -from PIL import Image +from PIL import Image # type: ignore # Define glider example GLIDER = [ diff --git a/cellular_automata/one_dimensional.py b/cellular_automata/one_dimensional.py index da77e444502f..5de2c5b994e3 100644 --- a/cellular_automata/one_dimensional.py +++ b/cellular_automata/one_dimensional.py @@ -6,7 +6,7 @@ from __future__ import annotations -from PIL import Image +from PIL import Image # type: ignore # Define the first generation of cells # fmt: off From 17287754e5b669a0ea1c1e27dcb1aefa724ff83c Mon Sep 17 00:00:00 2001 From: Hao LI <8520588+Leo-LiHao@users.noreply.github.com> Date: Mon, 22 Feb 2021 07:58:17 +0800 Subject: [PATCH 160/195] add type hints for avl_tree (#4214) Co-authored-by: LiHao --- data_structures/binary_tree/avl_tree.py | 155 +++++++++++++----------- 1 file changed, 87 insertions(+), 68 deletions(-) diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index 3362610b9303..e0d3e4d438a8 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -8,84 +8,85 @@ import math import random +from typing import Any, List, Optional class my_queue: - def __init__(self): - self.data = [] - self.head = 0 - self.tail = 0 + def __init__(self) -> None: + self.data: List[Any] = [] + self.head: int = 0 + self.tail: int = 0 - def is_empty(self): + def is_empty(self) -> bool: return self.head == self.tail - def push(self, data): + def push(self, data: Any) -> None: self.data.append(data) self.tail = self.tail + 1 - def pop(self): + def pop(self) -> Any: ret = self.data[self.head] self.head = self.head + 1 return ret - def count(self): + def count(self) -> int: return self.tail - self.head - def print(self): + def print(self) -> None: print(self.data) print("**************") print(self.data[self.head : self.tail]) class my_node: - def __init__(self, data): + def __init__(self, data: Any) -> None: self.data = data - self.left = None - self.right = None - self.height = 1 + self.left: Optional[my_node] = None + self.right: Optional[my_node] = None + self.height: int = 1 - def get_data(self): + def get_data(self) -> Any: return self.data - def get_left(self): + def get_left(self) -> Optional["my_node"]: return self.left - def get_right(self): + def get_right(self) -> Optional["my_node"]: return self.right - def get_height(self): + def get_height(self) -> int: return self.height - def set_data(self, data): + def set_data(self, data: Any) -> None: self.data = data return - def set_left(self, node): + def set_left(self, node: Optional["my_node"]) -> None: self.left = node return - def set_right(self, node): + def set_right(self, node: Optional["my_node"]) -> None: self.right = node return - def set_height(self, height): + def set_height(self, height: int) -> None: self.height = height return -def get_height(node): +def get_height(node: Optional["my_node"]) -> int: if node is None: return 0 return node.get_height() -def my_max(a, b): +def my_max(a: int, b: int) -> int: if a > b: return a return b -def right_rotation(node): +def right_rotation(node: my_node) -> my_node: r""" A B / \ / \ @@ -98,6 +99,7 @@ def right_rotation(node): """ print("left rotation node:", node.get_data()) ret = node.get_left() + assert ret is not None node.set_left(ret.get_right()) ret.set_right(node) h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 @@ -107,12 +109,13 @@ def right_rotation(node): return ret -def left_rotation(node): +def left_rotation(node: my_node) -> my_node: """ a mirror symmetry rotation of the left_rotation """ print("right rotation node:", node.get_data()) ret = node.get_right() + assert ret is not None node.set_right(ret.get_left()) ret.set_left(node) h1 = my_max(get_height(node.get_right()), get_height(node.get_left())) + 1 @@ -122,7 +125,7 @@ def left_rotation(node): return ret -def lr_rotation(node): +def lr_rotation(node: my_node) -> my_node: r""" A A Br / \ / \ / \ @@ -133,16 +136,20 @@ def lr_rotation(node): UB Bl RR = right_rotation LR = left_rotation """ - node.set_left(left_rotation(node.get_left())) + left_child = node.get_left() + assert left_child is not None + node.set_left(left_rotation(left_child)) return right_rotation(node) -def rl_rotation(node): - node.set_right(right_rotation(node.get_right())) +def rl_rotation(node: my_node) -> my_node: + right_child = node.get_right() + assert right_child is not None + node.set_right(right_rotation(right_child)) return left_rotation(node) -def insert_node(node, data): +def insert_node(node: Optional["my_node"], data: Any) -> Optional["my_node"]: if node is None: return my_node(data) if data < node.get_data(): @@ -150,8 +157,10 @@ def insert_node(node, data): if ( get_height(node.get_left()) - get_height(node.get_right()) == 2 ): # an unbalance detected + left_child = node.get_left() + assert left_child is not None if ( - data < node.get_left().get_data() + data < left_child.get_data() ): # new node is the left child of the left child node = right_rotation(node) else: @@ -159,7 +168,9 @@ def insert_node(node, data): else: node.set_right(insert_node(node.get_right(), data)) if get_height(node.get_right()) - get_height(node.get_left()) == 2: - if data < node.get_right().get_data(): + right_child = node.get_right() + assert right_child is not None + if data < right_child.get_data(): node = rl_rotation(node) else: node = left_rotation(node) @@ -168,52 +179,59 @@ def insert_node(node, data): return node -def get_rightMost(root): - while root.get_right() is not None: - root = root.get_right() +def get_rightMost(root: my_node) -> Any: + while True: + right_child = root.get_right() + if right_child is None: + break + root = right_child return root.get_data() -def get_leftMost(root): - while root.get_left() is not None: - root = root.get_left() +def get_leftMost(root: my_node) -> Any: + while True: + left_child = root.get_left() + if left_child is None: + break + root = left_child return root.get_data() -def del_node(root, data): +def del_node(root: my_node, data: Any) -> Optional["my_node"]: + left_child = root.get_left() + right_child = root.get_right() if root.get_data() == data: - if root.get_left() is not None and root.get_right() is not None: - temp_data = get_leftMost(root.get_right()) + if left_child is not None and right_child is not None: + temp_data = get_leftMost(right_child) root.set_data(temp_data) - root.set_right(del_node(root.get_right(), temp_data)) - elif root.get_left() is not None: - root = root.get_left() + root.set_right(del_node(right_child, temp_data)) + elif left_child is not None: + root = left_child + elif right_child is not None: + root = right_child else: - root = root.get_right() + return None elif root.get_data() > data: - if root.get_left() is None: + if left_child is None: print("No such data") return root else: - root.set_left(del_node(root.get_left(), data)) - elif root.get_data() < data: - if root.get_right() is None: + root.set_left(del_node(left_child, data)) + else: # root.get_data() < data + if right_child is None: return root else: - root.set_right(del_node(root.get_right(), data)) - if root is None: - return root - if get_height(root.get_right()) - get_height(root.get_left()) == 2: - if get_height(root.get_right().get_right()) > get_height( - root.get_right().get_left() - ): + root.set_right(del_node(right_child, data)) + + if get_height(right_child) - get_height(left_child) == 2: + assert right_child is not None + if get_height(right_child.get_right()) > get_height(right_child.get_left()): root = left_rotation(root) else: root = rl_rotation(root) - elif get_height(root.get_right()) - get_height(root.get_left()) == -2: - if get_height(root.get_left().get_left()) > get_height( - root.get_left().get_right() - ): + elif get_height(right_child) - get_height(left_child) == -2: + assert left_child is not None + if get_height(left_child.get_left()) > get_height(left_child.get_right()): root = right_rotation(root) else: root = lr_rotation(root) @@ -256,25 +274,26 @@ class AVLtree: ************************************* """ - def __init__(self): - self.root = None + def __init__(self) -> None: + self.root: Optional[my_node] = None - def get_height(self): - # print("yyy") + def get_height(self) -> int: return get_height(self.root) - def insert(self, data): + def insert(self, data: Any) -> None: print("insert:" + str(data)) self.root = insert_node(self.root, data) - def del_node(self, data): + def del_node(self, data: Any) -> None: print("delete:" + str(data)) if self.root is None: print("Tree is empty!") return self.root = del_node(self.root, data) - def __str__(self): # a level traversale, gives a more intuitive look on the tree + def __str__( + self, + ) -> str: # a level traversale, gives a more intuitive look on the tree output = "" q = my_queue() q.push(self.root) @@ -308,7 +327,7 @@ def __str__(self): # a level traversale, gives a more intuitive look on the tre return output -def _test(): +def _test() -> None: import doctest doctest.testmod() From 3fa9c9f83220c3df65c0d434b978b3bb292f3dc3 Mon Sep 17 00:00:00 2001 From: CarsonHam Date: Mon, 22 Feb 2021 23:53:49 -0600 Subject: [PATCH 161/195] Change occurrences of str.format to f-strings (#4118) * f-string update rsa_cipher.py * f-string update rsa_key_generator.py * f-string update burrows_wheeler.py * f-string update non_recursive_segment_tree.py * f-string update red_black_tree.py * f-string update deque_doubly.py * f-string update climbing_stairs.py * f-string update iterating_through_submasks.py * f-string update knn_sklearn.py * f-string update 3n_plus_1.py * f-string update quadratic_equations_complex_numbers.py * f-string update nth_fibonacci_using_matrix_exponentiation.py * f-string update sherman_morrison.py * f-string update levenshtein_distance.py * fix lines that were too long --- ciphers/rsa_cipher.py | 2 +- ciphers/rsa_key_generator.py | 4 ++-- compression/burrows_wheeler.py | 13 +++++++------ .../binary_tree/non_recursive_segment_tree.py | 2 +- data_structures/binary_tree/red_black_tree.py | 8 +++++--- data_structures/linked_list/deque_doubly.py | 4 ++-- dynamic_programming/climbing_stairs.py | 5 +++-- dynamic_programming/iterating_through_submasks.py | 5 +++-- machine_learning/knn_sklearn.py | 4 ++-- maths/3n_plus_1.py | 2 +- maths/quadratic_equations_complex_numbers.py | 4 ++-- matrix/nth_fibonacci_using_matrix_exponentiation.py | 10 +++++----- matrix/sherman_morrison.py | 6 ++---- strings/levenshtein_distance.py | 6 +----- 14 files changed, 37 insertions(+), 38 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 57c916a44d4b..0df37d6ea3ff 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -118,7 +118,7 @@ def encryptAndWriteToFile( for i in range(len(encryptedBlocks)): encryptedBlocks[i] = str(encryptedBlocks[i]) encryptedContent = ",".join(encryptedBlocks) - encryptedContent = "{}_{}_{}".format(len(message), blockSize, encryptedContent) + encryptedContent = f"{len(message)}_{blockSize}_{encryptedContent}" with open(messageFilename, "w") as fo: fo.write(encryptedContent) return encryptedContent diff --git a/ciphers/rsa_key_generator.py b/ciphers/rsa_key_generator.py index 5693aa637ee9..e456d9d9f6f1 100644 --- a/ciphers/rsa_key_generator.py +++ b/ciphers/rsa_key_generator.py @@ -49,11 +49,11 @@ def makeKeyFiles(name: int, keySize: int) -> None: publicKey, privateKey = generateKey(keySize) print("\nWriting public key to file %s_pubkey.txt..." % name) with open("%s_pubkey.txt" % name, "w") as out_file: - out_file.write("{},{},{}".format(keySize, publicKey[0], publicKey[1])) + out_file.write(f"{keySize},{publicKey[0]},{publicKey[1]}") print("Writing private key to file %s_privkey.txt..." % name) with open("%s_privkey.txt" % name, "w") as out_file: - out_file.write("{},{},{}".format(keySize, privateKey[0], privateKey[1])) + out_file.write(f"{keySize},{privateKey[0]},{privateKey[1]}") if __name__ == "__main__": diff --git a/compression/burrows_wheeler.py b/compression/burrows_wheeler.py index 1a6610915e65..7d705af7428e 100644 --- a/compression/burrows_wheeler.py +++ b/compression/burrows_wheeler.py @@ -157,11 +157,12 @@ def reverse_bwt(bwt_string: str, idx_original_string: int) -> str: entry_msg = "Provide a string that I will generate its BWT transform: " s = input(entry_msg).strip() result = bwt_transform(s) - bwt_output_msg = "Burrows Wheeler transform for string '{}' results in '{}'" - print(bwt_output_msg.format(s, result["bwt_string"])) + print( + f"Burrows Wheeler transform for string '{s}' results " + f"in '{result['bwt_string']}'" + ) original_string = reverse_bwt(result["bwt_string"], result["idx_original_string"]) - fmt = ( - "Reversing Burrows Wheeler transform for entry '{}' we get original" - " string '{}'" + print( + f"Reversing Burrows Wheeler transform for entry '{result['bwt_string']}' " + f"we get original string '{original_string}'" ) - print(fmt.format(result["bwt_string"], original_string)) diff --git a/data_structures/binary_tree/non_recursive_segment_tree.py b/data_structures/binary_tree/non_recursive_segment_tree.py index 064e5aded7b4..c914079e0a8d 100644 --- a/data_structures/binary_tree/non_recursive_segment_tree.py +++ b/data_structures/binary_tree/non_recursive_segment_tree.py @@ -49,7 +49,7 @@ def __init__(self, arr: list[T], fnc: Callable[[T, T], T]) -> None: :param arr: list of elements for the segment tree :param fnc: commutative function for combine two elements - >>> SegmentTree(['a', 'b', 'c'], lambda a, b: '{}{}'.format(a, b)).query(0, 2) + >>> SegmentTree(['a', 'b', 'c'], lambda a, b: f'{a}{b}').query(0, 2) 'abc' >>> SegmentTree([(1, 2), (2, 3), (3, 4)], ... lambda a, b: (a[0] + b[0], a[1] + b[1])).query(0, 2) diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index 5d721edfa45b..de971a712fc1 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -475,11 +475,13 @@ def __repr__(self) -> str: from pprint import pformat if self.left is None and self.right is None: - return "'{} {}'".format(self.label, (self.color and "red") or "blk") + return f"'{self.label} {(self.color and 'red') or 'blk'}'" return pformat( { - "%s %s" - % (self.label, (self.color and "red") or "blk"): (self.left, self.right) + f"{self.label} {(self.color and 'red') or 'blk'}": ( + self.left, + self.right, + ) }, indent=1, ) diff --git a/data_structures/linked_list/deque_doubly.py b/data_structures/linked_list/deque_doubly.py index 894f91d561cc..c9ae8b3d1ba2 100644 --- a/data_structures/linked_list/deque_doubly.py +++ b/data_structures/linked_list/deque_doubly.py @@ -20,8 +20,8 @@ def __init__(self, link_p, element, link_n): self._next = link_n def has_next_and_prev(self): - return " Prev -> {}, Next -> {}".format( - self._prev is not None, self._next is not None + return ( + f" Prev -> {self._prev is not None}, Next -> {self._next is not None}" ) def __init__(self): diff --git a/dynamic_programming/climbing_stairs.py b/dynamic_programming/climbing_stairs.py index 79605261f981..048d57aed1be 100644 --- a/dynamic_programming/climbing_stairs.py +++ b/dynamic_programming/climbing_stairs.py @@ -25,8 +25,9 @@ def climb_stairs(n: int) -> int: ... AssertionError: n needs to be positive integer, your input -7 """ - fmt = "n needs to be positive integer, your input {}" - assert isinstance(n, int) and n > 0, fmt.format(n) + assert ( + isinstance(n, int) and n > 0 + ), f"n needs to be positive integer, your input {n}" if n == 1: return 1 dp = [0] * (n + 1) diff --git a/dynamic_programming/iterating_through_submasks.py b/dynamic_programming/iterating_through_submasks.py index 855af61d6707..21c64dba4ecc 100644 --- a/dynamic_programming/iterating_through_submasks.py +++ b/dynamic_programming/iterating_through_submasks.py @@ -37,8 +37,9 @@ def list_of_submasks(mask: int) -> list[int]: """ - fmt = "mask needs to be positive integer, your input {}" - assert isinstance(mask, int) and mask > 0, fmt.format(mask) + assert ( + isinstance(mask, int) and mask > 0 + ), f"mask needs to be positive integer, your input {mask}" """ first submask iterated will be mask itself then operation will be performed diff --git a/machine_learning/knn_sklearn.py b/machine_learning/knn_sklearn.py index 9a9114102ff3..4a621a4244b6 100644 --- a/machine_learning/knn_sklearn.py +++ b/machine_learning/knn_sklearn.py @@ -26,6 +26,6 @@ prediction = knn.predict(X_new) print( - "\nNew array: \n {}" - "\n\nTarget Names Prediction: \n {}".format(X_new, iris["target_names"][prediction]) + f"\nNew array: \n {X_new}\n\nTarget Names Prediction: \n" + f" {iris['target_names'][prediction]}" ) diff --git a/maths/3n_plus_1.py b/maths/3n_plus_1.py index 28c9fd7b426f..e455a158e619 100644 --- a/maths/3n_plus_1.py +++ b/maths/3n_plus_1.py @@ -9,7 +9,7 @@ def n31(a: int) -> tuple[list[int], int]: """ if not isinstance(a, int): - raise TypeError("Must be int, not {}".format(type(a).__name__)) + raise TypeError(f"Must be int, not {type(a).__name__}") if a < 1: raise ValueError(f"Given integer must be greater than 1, not {a}") diff --git a/maths/quadratic_equations_complex_numbers.py b/maths/quadratic_equations_complex_numbers.py index 01a411bc560d..1035171e4ec3 100644 --- a/maths/quadratic_equations_complex_numbers.py +++ b/maths/quadratic_equations_complex_numbers.py @@ -30,8 +30,8 @@ def quadratic_roots(a: int, b: int, c: int) -> tuple[complex, complex]: def main(): - solutions = quadratic_roots(a=5, b=6, c=1) - print("The solutions are: {} and {}".format(*solutions)) + solution1, solution2 = quadratic_roots(a=5, b=6, c=1) + print(f"The solutions are: {solution1} and {solution2}") if __name__ == "__main__": diff --git a/matrix/nth_fibonacci_using_matrix_exponentiation.py b/matrix/nth_fibonacci_using_matrix_exponentiation.py index 296c36e88691..8c39de0f23b6 100644 --- a/matrix/nth_fibonacci_using_matrix_exponentiation.py +++ b/matrix/nth_fibonacci_using_matrix_exponentiation.py @@ -71,13 +71,13 @@ def nth_fibonacci_bruteforce(n): def main(): - fmt = ( - "{} fibonacci number using matrix exponentiation is {} and using bruteforce " - "is {}\n" - ) for ordinal in "0th 1st 2nd 3rd 10th 100th 1000th".split(): n = int("".join(c for c in ordinal if c in "0123456789")) # 1000th --> 1000 - print(fmt.format(ordinal, nth_fibonacci_matrix(n), nth_fibonacci_bruteforce(n))) + print( + f"{ordinal} fibonacci number using matrix exponentiation is " + f"{nth_fibonacci_matrix(n)} and using bruteforce is " + f"{nth_fibonacci_bruteforce(n)}\n" + ) # from timeit import timeit # print(timeit("nth_fibonacci_matrix(1000000)", # "from main import nth_fibonacci_matrix", number=5)) diff --git a/matrix/sherman_morrison.py b/matrix/sherman_morrison.py index 4920ec6c13db..3466b3d4a01f 100644 --- a/matrix/sherman_morrison.py +++ b/matrix/sherman_morrison.py @@ -175,9 +175,7 @@ def __mul__(self, another): result[r, c] += self[r, i] * another[i, c] return result else: - raise TypeError( - "Unsupported type given for another ({})".format(type(another)) - ) + raise TypeError(f"Unsupported type given for another ({type(another)})") def transpose(self): """ @@ -260,7 +258,7 @@ def test1(): print(f"v is {v}") print("uv^T is %s" % (u * v.transpose())) # Sherman Morrison - print("(a + uv^T)^(-1) is {}".format(ainv.ShermanMorrison(u, v))) + print(f"(a + uv^T)^(-1) is {ainv.ShermanMorrison(u, v)}") def test2(): import doctest diff --git a/strings/levenshtein_distance.py b/strings/levenshtein_distance.py index 54948a96670b..540a21c93da3 100644 --- a/strings/levenshtein_distance.py +++ b/strings/levenshtein_distance.py @@ -69,8 +69,4 @@ def levenshtein_distance(first_word: str, second_word: str) -> int: second_word = input("Enter the second word:\n").strip() result = levenshtein_distance(first_word, second_word) - print( - "Levenshtein distance between {} and {} is {}".format( - first_word, second_word, result - ) - ) + print(f"Levenshtein distance between {first_word} and {second_word} is {result}") From 8a91f48e1907927569f060e1cf9d79c399afaeff Mon Sep 17 00:00:00 2001 From: Leyza <56138111+Leyza@users.noreply.github.com> Date: Tue, 23 Feb 2021 01:15:00 -0500 Subject: [PATCH 162/195] Added binary shifts and twos complement functions to bit manipulation (#4068) * Added binary shifts and twos complement functions to bit manipulation package * Fixed problem representing 0 wrong * More testing * Fixed problems * Fixed formatting * More format fixes * Format fixes * Fixed docstrings and added url * Minor change to url --- bit_manipulation/binary_shifts.py | 111 +++++++++++++++++++++ bit_manipulation/binary_twos_complement.py | 43 ++++++++ 2 files changed, 154 insertions(+) create mode 100644 bit_manipulation/binary_shifts.py create mode 100644 bit_manipulation/binary_twos_complement.py diff --git a/bit_manipulation/binary_shifts.py b/bit_manipulation/binary_shifts.py new file mode 100644 index 000000000000..fe62880f941c --- /dev/null +++ b/bit_manipulation/binary_shifts.py @@ -0,0 +1,111 @@ +# Information on binary shifts: +# https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types +# https://www.interviewcake.com/concept/java/bit-shift + + +def logical_left_shift(number: int, shift_amount: int) -> str: + """ + Take in 2 positive integers. + 'number' is the integer to be logically left shifted 'shift_amount' times. + i.e. (number << shift_amount) + Return the shifted binary representation. + + >>> logical_left_shift(0, 1) + '0b00' + >>> logical_left_shift(1, 1) + '0b10' + >>> logical_left_shift(1, 5) + '0b100000' + >>> logical_left_shift(17, 2) + '0b1000100' + >>> logical_left_shift(1983, 4) + '0b111101111110000' + >>> logical_left_shift(1, -1) + Traceback (most recent call last): + ... + ValueError: both inputs must be positive integers + """ + if number < 0 or shift_amount < 0: + raise ValueError("both inputs must be positive integers") + + binary_number = str(bin(number)) + binary_number += "0" * shift_amount + return binary_number + + +def logical_right_shift(number: int, shift_amount: int) -> str: + """ + Take in positive 2 integers. + 'number' is the integer to be logically right shifted 'shift_amount' times. + i.e. (number >>> shift_amount) + Return the shifted binary representation. + + >>> logical_right_shift(0, 1) + '0b0' + >>> logical_right_shift(1, 1) + '0b0' + >>> logical_right_shift(1, 5) + '0b0' + >>> logical_right_shift(17, 2) + '0b100' + >>> logical_right_shift(1983, 4) + '0b1111011' + >>> logical_right_shift(1, -1) + Traceback (most recent call last): + ... + ValueError: both inputs must be positive integers + """ + if number < 0 or shift_amount < 0: + raise ValueError("both inputs must be positive integers") + + binary_number = str(bin(number))[2:] + if shift_amount >= len(binary_number): + return "0b0" + shifted_binary_number = binary_number[: len(binary_number) - shift_amount] + return "0b" + shifted_binary_number + + +def arithmetic_right_shift(number: int, shift_amount: int) -> str: + """ + Take in 2 integers. + 'number' is the integer to be arithmetically right shifted 'shift_amount' times. + i.e. (number >> shift_amount) + Return the shifted binary representation. + + >>> arithmetic_right_shift(0, 1) + '0b00' + >>> arithmetic_right_shift(1, 1) + '0b00' + >>> arithmetic_right_shift(-1, 1) + '0b11' + >>> arithmetic_right_shift(17, 2) + '0b000100' + >>> arithmetic_right_shift(-17, 2) + '0b111011' + >>> arithmetic_right_shift(-1983, 4) + '0b111110000100' + """ + if number >= 0: # Get binary representation of positive number + binary_number = "0" + str(bin(number)).strip("-")[2:] + else: # Get binary (2's complement) representation of negative number + binary_number_length = len(bin(number)[3:]) # Find 2's complement of number + binary_number = bin(abs(number) - (1 << binary_number_length))[3:] + binary_number = ( + ("1" + "0" * (binary_number_length - len(binary_number)) + binary_number) + if number < 0 + else "0" + ) + + if shift_amount >= len(binary_number): + return "0b" + binary_number[0] * len(binary_number) + return ( + "0b" + + binary_number[0] * shift_amount + + binary_number[: len(binary_number) - shift_amount] + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/bit_manipulation/binary_twos_complement.py b/bit_manipulation/binary_twos_complement.py new file mode 100644 index 000000000000..2c064ec142d7 --- /dev/null +++ b/bit_manipulation/binary_twos_complement.py @@ -0,0 +1,43 @@ +# Information on 2's complement: https://en.wikipedia.org/wiki/Two%27s_complement + + +def twos_complement(number: int) -> str: + """ + Take in a negative integer 'number'. + Return the two's complement representation of 'number'. + + >>> twos_complement(0) + '0b0' + >>> twos_complement(-1) + '0b11' + >>> twos_complement(-5) + '0b1011' + >>> twos_complement(-17) + '0b101111' + >>> twos_complement(-207) + '0b100110001' + >>> twos_complement(1) + Traceback (most recent call last): + ... + ValueError: input must be a negative integer + """ + if number > 0: + raise ValueError("input must be a negative integer") + binary_number_length = len(bin(number)[3:]) + twos_complement_number = bin(abs(number) - (1 << binary_number_length))[3:] + twos_complement_number = ( + ( + "1" + + "0" * (binary_number_length - len(twos_complement_number)) + + twos_complement_number + ) + if number < 0 + else "0" + ) + return "0b" + twos_complement_number + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ed836a186758f6f14b9c055283bc6eebab50915b Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 23 Feb 2021 09:02:30 +0000 Subject: [PATCH 163/195] [mypy]Correction of all errors in the sorts directory (#4224) * [mypy] Add/fix type annotations for recursive_insertion_sort(#4085) * [mypy] Add/fix type annotations for bucket_sort(#4085) * [mypy] Reworked code for cocktail_shaker_sort so that missing return statement error is resolved(#4085) * [mypy] Add/fix type annotations for patience_sort(#4085) * [mypy] Add/fix type annotations for radix_sort(#4085) Co-authored-by: goodm2 <4qjpngu8mem8cz> --- sorts/bucket_sort.py | 3 ++- sorts/cocktail_shaker_sort.py | 3 ++- sorts/patience_sort.py | 3 ++- sorts/radix_sort.py | 2 +- sorts/recursive_insertion_sort.py | 8 +++++--- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/sorts/bucket_sort.py b/sorts/bucket_sort.py index a0566be662e3..1ac76774f4ba 100644 --- a/sorts/bucket_sort.py +++ b/sorts/bucket_sort.py @@ -27,6 +27,7 @@ Source: https://en.wikipedia.org/wiki/Bucket_sort """ +from typing import List def bucket_sort(my_list: list) -> list: @@ -51,7 +52,7 @@ def bucket_sort(my_list: list) -> list: return [] min_value, max_value = min(my_list), max(my_list) bucket_count = int(max_value - min_value) + 1 - buckets = [[] for _ in range(bucket_count)] + buckets: List[list] = [[] for _ in range(bucket_count)] for i in range(len(my_list)): buckets[(int(my_list[i] - min_value) // bucket_count)].append(my_list[i]) diff --git a/sorts/cocktail_shaker_sort.py b/sorts/cocktail_shaker_sort.py index 42015abc5f97..b738ff31d768 100644 --- a/sorts/cocktail_shaker_sort.py +++ b/sorts/cocktail_shaker_sort.py @@ -33,7 +33,8 @@ def cocktail_shaker_sort(unsorted: list) -> list: swapped = True if not swapped: - return unsorted + break + return unsorted if __name__ == "__main__": diff --git a/sorts/patience_sort.py b/sorts/patience_sort.py index f4e35d9a0ac6..87f5a4078612 100644 --- a/sorts/patience_sort.py +++ b/sorts/patience_sort.py @@ -1,6 +1,7 @@ from bisect import bisect_left from functools import total_ordering from heapq import merge +from typing import List """ A pure Python implementation of the patience sort algorithm @@ -43,7 +44,7 @@ def patience_sort(collection: list) -> list: >>> patience_sort([-3, -17, -48]) [-48, -17, -3] """ - stacks = [] + stacks: List[Stack] = [] # sort into stacks for element in collection: new_stacks = Stack([element]) diff --git a/sorts/radix_sort.py b/sorts/radix_sort.py index 57dbbaa79076..b802b5278119 100644 --- a/sorts/radix_sort.py +++ b/sorts/radix_sort.py @@ -30,7 +30,7 @@ def radix_sort(list_of_ints: List[int]) -> List[int]: max_digit = max(list_of_ints) while placement <= max_digit: # declare and initialize empty buckets - buckets = [list() for _ in range(RADIX)] + buckets: List[list] = [list() for _ in range(RADIX)] # split list_of_ints between the buckets for i in list_of_ints: tmp = int((i / placement) % RADIX) diff --git a/sorts/recursive_insertion_sort.py b/sorts/recursive_insertion_sort.py index 66dd08157df1..89f88b4a961b 100644 --- a/sorts/recursive_insertion_sort.py +++ b/sorts/recursive_insertion_sort.py @@ -4,6 +4,8 @@ from __future__ import annotations +from typing import List + def rec_insertion_sort(collection: list, n: int): """ @@ -70,6 +72,6 @@ def insert_next(collection: list, index: int): if __name__ == "__main__": numbers = input("Enter integers separated by spaces: ") - numbers = [int(num) for num in numbers.split()] - rec_insertion_sort(numbers, len(numbers)) - print(numbers) + number_list: List[int] = [int(num) for num in numbers.split()] + rec_insertion_sort(number_list, len(number_list)) + print(number_list) From a4944f1af89aa7db6681f9a0a0edbf42ffb2c75e Mon Sep 17 00:00:00 2001 From: algobytewise Date: Tue, 23 Feb 2021 14:45:04 +0530 Subject: [PATCH 164/195] mypy-fix for bezier_curve.py (#4220) --- graphics/bezier_curve.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphics/bezier_curve.py b/graphics/bezier_curve.py index 295ff47e8cdc..2bb764fdc916 100644 --- a/graphics/bezier_curve.py +++ b/graphics/bezier_curve.py @@ -2,7 +2,7 @@ # https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm from __future__ import annotations -from scipy.special import comb +from scipy.special import comb # type: ignore class BezierCurve: @@ -78,7 +78,7 @@ def plot_curve(self, step_size: float = 0.01): step_size: defines the step(s) at which to evaluate the Bezier curve. The smaller the step size, the finer the curve produced. """ - from matplotlib import pyplot as plt + from matplotlib import pyplot as plt # type: ignore to_plot_x: list[float] = [] # x coordinates of points to plot to_plot_y: list[float] = [] # y coordinates of points to plot From b0d3934cf1cf0b92e2ecc6d8d8bb2f41abe01673 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Tue, 23 Feb 2021 17:59:56 +0530 Subject: [PATCH 165/195] Add Mandelbrot algorithm --- graphics/mandelbrot.py | 150 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 graphics/mandelbrot.py diff --git a/graphics/mandelbrot.py b/graphics/mandelbrot.py new file mode 100644 index 000000000000..21a70a56f17b --- /dev/null +++ b/graphics/mandelbrot.py @@ -0,0 +1,150 @@ +""" +The Mandelbrot set is the set of complex numbers "c" for which the series +"z_(n+1) = z_n * z_n + c" does not diverge, i.e. remains bounded. Thus, a +complex number "c" is a member of the Mandelbrot set if, when starting with +"z_0 = 0" and applying the iteration repeatedly, the absolute value of +"z_n" remains bounded for all "n > 0". Complex numbers can be written as +"a + b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" +is the imaginary component, usually drawn on the y-axis. Most visualizations +of the Mandelbrot set use a color-coding to indicate after how many steps in +the series the numbers outside the set diverge. Images of the Mandelbrot set +exhibit an elaborate and infinitely complicated boundary that reveals +progressively ever-finer recursive detail at increasing magnifications, making +the boundary of the Mandelbrot set a fractal curve. +(description adapted from https://en.wikipedia.org/wiki/Mandelbrot_set ) +(see also https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set ) +""" + + +import colorsys + +from PIL import Image # type: ignore + + +def getDistance(x: float, y: float, max_step: int) -> float: + """ + Return the relative distance (= step/max_step) after which the complex number + constituted by this x-y-pair diverges. Members of the Mandelbrot set do not + diverge so their distance is 1. + + >>> getDistance(0, 0, 50) + 1.0 + >>> getDistance(0.5, 0.5, 50) + 0.061224489795918366 + >>> getDistance(2, 0, 50) + 0.0 + """ + a = x + b = y + for step in range(max_step): + a_new = a * a - b * b + x + b = 2 * a * b + y + a = a_new + + # divergence happens for all complex number with an absolute value + # greater than 4 + if a * a + b * b > 4: + break + return step / (max_step - 1) + + +def get_black_and_white_rgb(distance: float) -> tuple: + """ + Black&white color-coding that ignores the relative distance. The Mandelbrot + set is black, everything else is white. + + >>> get_black_and_white_rgb(0) + (255, 255, 255) + >>> get_black_and_white_rgb(0.5) + (255, 255, 255) + >>> get_black_and_white_rgb(1) + (0, 0, 0) + """ + if distance == 1: + return (0, 0, 0) + else: + return (255, 255, 255) + + +def get_color_coded_rgb(distance: float) -> tuple: + """ + Color-coding taking the relative distance into account. The Mandelbrot set + is black. + + >>> get_color_coded_rgb(0) + (255, 0, 0) + >>> get_color_coded_rgb(0.5) + (0, 255, 255) + >>> get_color_coded_rgb(1) + (0, 0, 0) + """ + if distance == 1: + return (0, 0, 0) + else: + return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(distance, 1, 1)) + + +def get_image( + image_width: int = 800, + image_height: int = 600, + figure_center_x: float = -0.6, + figure_center_y: float = 0, + figure_width: float = 3.2, + max_step: int = 50, + use_distance_color_coding: bool = True, +) -> Image.Image: + """ + Function to generate the image of the Mandelbrot set. Two types of coordinates + are used: image-coordinates that refer to the pixels and figure-coordinates + that refer to the complex numbers inside and outside the Mandelbrot set. The + figure-coordinates in the arguments of this function determine which section + of the Mandelbrot set is viewed. The main area of the Mandelbrot set is + roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates. + + >>> get_image().load()[0,0] + (255, 0, 0) + >>> get_image(use_distance_color_coding = False).load()[0,0] + (255, 255, 255) + """ + img = Image.new("RGB", (image_width, image_height)) + pixels = img.load() + + # loop through the image-coordinates + for image_x in range(image_width): + for image_y in range(image_height): + + # determine the figure-coordinates based on the image-coordinates + figure_height = figure_width / image_width * image_height + figure_x = figure_center_x + (image_x / image_width - 0.5) * figure_width + figure_y = figure_center_y + (image_y / image_height - 0.5) * figure_height + + distance = getDistance(figure_x, figure_y, max_step) + + # color the corresponding pixel based on the selected coloring-function + if use_distance_color_coding: + pixels[image_x, image_y] = get_color_coded_rgb(distance) + else: + pixels[image_x, image_y] = get_black_and_white_rgb(distance) + + return img + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + # colored version, full figure + img = get_image() + + # uncomment for colored version, different section, zoomed in + # img = get_image(figure_center_x = -0.6, figure_center_y = -0.4, + # figure_width = 0.8) + + # uncomment for black and white version, full figure + # img = get_image(use_distance_color_coding = False) + + # uncomment to save the image + # img.save("mandelbrot.png") + + img.show() From c4c4122163a6168248fc6e515698070d10245fa8 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Tue, 23 Feb 2021 18:04:42 +0530 Subject: [PATCH 166/195] snake_case-fix --- graphics/mandelbrot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics/mandelbrot.py b/graphics/mandelbrot.py index 21a70a56f17b..a0cb8fe88027 100644 --- a/graphics/mandelbrot.py +++ b/graphics/mandelbrot.py @@ -21,7 +21,7 @@ from PIL import Image # type: ignore -def getDistance(x: float, y: float, max_step: int) -> float: +def get_distance(x: float, y: float, max_step: int) -> float: """ Return the relative distance (= step/max_step) after which the complex number constituted by this x-y-pair diverges. Members of the Mandelbrot set do not From ee59235b3ee64192b488f5094f1cfa1dd89b96fd Mon Sep 17 00:00:00 2001 From: algobytewise Date: Tue, 23 Feb 2021 18:08:16 +0530 Subject: [PATCH 167/195] fixed-renaming --- graphics/mandelbrot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/graphics/mandelbrot.py b/graphics/mandelbrot.py index a0cb8fe88027..de795bb3fc6f 100644 --- a/graphics/mandelbrot.py +++ b/graphics/mandelbrot.py @@ -27,11 +27,11 @@ def get_distance(x: float, y: float, max_step: int) -> float: constituted by this x-y-pair diverges. Members of the Mandelbrot set do not diverge so their distance is 1. - >>> getDistance(0, 0, 50) + >>> get_distance(0, 0, 50) 1.0 - >>> getDistance(0.5, 0.5, 50) + >>> get_distance(0.5, 0.5, 50) 0.061224489795918366 - >>> getDistance(2, 0, 50) + >>> get_distance(2, 0, 50) 0.0 """ a = x @@ -118,7 +118,7 @@ def get_image( figure_x = figure_center_x + (image_x / image_width - 0.5) * figure_width figure_y = figure_center_y + (image_y / image_height - 0.5) * figure_height - distance = getDistance(figure_x, figure_y, max_step) + distance = get_distance(figure_x, figure_y, max_step) # color the corresponding pixel based on the selected coloring-function if use_distance_color_coding: From cac8118bbde6655b14d6651787476282eece5db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=9C=E8=BF=9C=E8=B6=85?= Date: Fri, 26 Feb 2021 09:01:50 +0800 Subject: [PATCH 168/195] Optimization shell sort (#4119) * optimization * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- sorts/shell_sort.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sorts/shell_sort.py b/sorts/shell_sort.py index 2e749e43d056..10ae9ba407ec 100644 --- a/sorts/shell_sort.py +++ b/sorts/shell_sort.py @@ -26,7 +26,8 @@ def shell_sort(collection): while j >= gap and collection[j - gap] > insert_value: collection[j] = collection[j - gap] j -= gap - collection[j] = insert_value + if j != i: + collection[j] = insert_value return collection From 5f87ae0caf7e6c18ef4b51d9111ea4e8b9c309ac Mon Sep 17 00:00:00 2001 From: algobytewise Date: Fri, 26 Feb 2021 19:00:35 +0530 Subject: [PATCH 169/195] [mypy] Added/fixed type annotations for "rotate_matrix.py" & "test_matrix_operation.py" (#4221) * [mypy] Added/fixed type annotations for "rotate_matrix.py" * [mypy] Added/fixed type annotations for "test_matrix_operation.py" --- matrix/rotate_matrix.py | 18 ++++++++++-------- matrix/tests/test_matrix_operation.py | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/matrix/rotate_matrix.py b/matrix/rotate_matrix.py index 6daf7e0cf2c5..f638597ae35d 100644 --- a/matrix/rotate_matrix.py +++ b/matrix/rotate_matrix.py @@ -5,8 +5,10 @@ https://stackoverflow.com/questions/42519/how-do-you-rotate-a-two-dimensional-array """ +from __future__ import annotations -def make_matrix(row_size: int = 4) -> [[int]]: + +def make_matrix(row_size: int = 4) -> list[list]: """ >>> make_matrix() [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] @@ -23,7 +25,7 @@ def make_matrix(row_size: int = 4) -> [[int]]: return [[1 + x + y * row_size for x in range(row_size)] for y in range(row_size)] -def rotate_90(matrix: [[]]) -> [[]]: +def rotate_90(matrix: list[list]) -> list[list]: """ >>> rotate_90(make_matrix()) [[4, 8, 12, 16], [3, 7, 11, 15], [2, 6, 10, 14], [1, 5, 9, 13]] @@ -35,7 +37,7 @@ def rotate_90(matrix: [[]]) -> [[]]: # OR.. transpose(reverse_column(matrix)) -def rotate_180(matrix: [[]]) -> [[]]: +def rotate_180(matrix: list[list]) -> list[list]: """ >>> rotate_180(make_matrix()) [[16, 15, 14, 13], [12, 11, 10, 9], [8, 7, 6, 5], [4, 3, 2, 1]] @@ -47,7 +49,7 @@ def rotate_180(matrix: [[]]) -> [[]]: # OR.. reverse_column(reverse_row(matrix)) -def rotate_270(matrix: [[]]) -> [[]]: +def rotate_270(matrix: list[list]) -> list[list]: """ >>> rotate_270(make_matrix()) [[13, 9, 5, 1], [14, 10, 6, 2], [15, 11, 7, 3], [16, 12, 8, 4]] @@ -59,22 +61,22 @@ def rotate_270(matrix: [[]]) -> [[]]: # OR.. transpose(reverse_row(matrix)) -def transpose(matrix: [[]]) -> [[]]: +def transpose(matrix: list[list]) -> list[list]: matrix[:] = [list(x) for x in zip(*matrix)] return matrix -def reverse_row(matrix: [[]]) -> [[]]: +def reverse_row(matrix: list[list]) -> list[list]: matrix[:] = matrix[::-1] return matrix -def reverse_column(matrix: [[]]) -> [[]]: +def reverse_column(matrix: list[list]) -> list[list]: matrix[:] = [x[::-1] for x in matrix] return matrix -def print_matrix(matrix: [[]]) -> [[]]: +def print_matrix(matrix: list[list]) -> None: for i in matrix: print(*i) diff --git a/matrix/tests/test_matrix_operation.py b/matrix/tests/test_matrix_operation.py index 3500dfeb0641..65b35fd7e78b 100644 --- a/matrix/tests/test_matrix_operation.py +++ b/matrix/tests/test_matrix_operation.py @@ -12,7 +12,7 @@ import sys import numpy as np -import pytest +import pytest # type: ignore # Custom/local libraries from matrix import matrix_operation as matop From de78acba72ca6c56de029555a122fdc62aeea88a Mon Sep 17 00:00:00 2001 From: Ayush Bisht <61404154+ayushbisht2001@users.noreply.github.com> Date: Tue, 2 Mar 2021 03:00:16 +0530 Subject: [PATCH 170/195] Add geometric_mean.py (#4244) * geometric_mean3 * update-GM.PY * update-AM.PY * Revert "update-AM.PY" This reverts commit 11792ec9747bec5d00e51edcd462bf87150cdba9. --- maths/series/geometric_mean.py | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 maths/series/geometric_mean.py diff --git a/maths/series/geometric_mean.py b/maths/series/geometric_mean.py new file mode 100644 index 000000000000..50ae54ad6574 --- /dev/null +++ b/maths/series/geometric_mean.py @@ -0,0 +1,75 @@ +""" +GEOMETRIC MEAN : https://en.wikipedia.org/wiki/Geometric_mean +""" + + +def is_geometric_series(series: list) -> bool: + """ + checking whether the input series is geometric series or not + + >>> is_geometric_series([2, 4, 8]) + True + >>> is_geometric_series([3, 6, 12, 24]) + True + >>> is_geometric_series([1, 2, 3]) + False + >>> is_geometric_series([0, 0, 3]) + False + + """ + if len(series) == 1: + return True + try: + common_ratio = series[1] / series[0] + for index in range(len(series) - 1): + if series[index + 1] / series[index] != common_ratio: + return False + except ZeroDivisionError: + return False + return True + + +def geometric_mean(series: list) -> float: + """ + return the geometric mean of series + + >>> geometric_mean([2, 4, 8]) + 3.9999999999999996 + >>> geometric_mean([3, 6, 12, 24]) + 8.48528137423857 + >>> geometric_mean([4, 8, 16]) + 7.999999999999999 + >>> geometric_mean(4) + Traceback (most recent call last): + ... + ValueError: Input series is not valid, valid series - [2, 4, 8] + >>> geometric_mean([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: Input list is not a geometric series + >>> geometric_mean([0, 2, 3]) + Traceback (most recent call last): + ... + ValueError: Input list is not a geometric series + >>> geometric_mean([]) + Traceback (most recent call last): + ... + ValueError: Input list must be a non empty list + + """ + if not isinstance(series, list): + raise ValueError("Input series is not valid, valid series - [2, 4, 8]") + if len(series) == 0: + raise ValueError("Input list must be a non empty list") + if not is_geometric_series(series): + raise ValueError("Input list is not a geometric series") + answer = 1 + for value in series: + answer *= value + return pow(answer, 1 / len(series)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 66096c403ddb37dcc6d2d944174364d90fabde62 Mon Sep 17 00:00:00 2001 From: ulwlu Date: Tue, 2 Mar 2021 21:24:41 +0900 Subject: [PATCH 171/195] Add graham scan algorithm (#4205) * Add graham scan algorithm * Fix argument name p with point * Add tests in inner function * updating DIRECTORY.md * Fix graham scan for isort --profile=black Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 7 ++ other/graham_scan.py | 171 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 other/graham_scan.py diff --git a/DIRECTORY.md b/DIRECTORY.md index d487b39490ed..61e20eeb571d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -30,6 +30,8 @@ * [Binary Count Trailing Zeros](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_count_trailing_zeros.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) + * [Count Number Of One Bits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/count_number_of_one_bits.py) + * [Reverse Bits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/reverse_bits.py) * [Single Bit Manipulation Operations](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/single_bit_manipulation_operations.py) ## Blockchain @@ -122,6 +124,7 @@ * [Fenwick Tree](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/fenwick_tree.py) * [Lazy Segment Tree](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/lazy_segment_tree.py) * [Lowest Common Ancestor](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/lowest_common_ancestor.py) + * [Merge Two Binary Trees](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/merge_two_binary_trees.py) * [Non Recursive Segment Tree](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/non_recursive_segment_tree.py) * [Number Of Possible Binary Trees](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/number_of_possible_binary_trees.py) * [Red Black Tree](https://github.com/TheAlgorithms/Python/blob/master/data_structures/binary_tree/red_black_tree.py) @@ -274,6 +277,7 @@ ## Graphics * [Bezier Curve](https://github.com/TheAlgorithms/Python/blob/master/graphics/bezier_curve.py) + * [Koch Snowflake](https://github.com/TheAlgorithms/Python/blob/master/graphics/koch_snowflake.py) * [Vector3 For 2D Rendering](https://github.com/TheAlgorithms/Python/blob/master/graphics/vector3_for_2d_rendering.py) ## Graphs @@ -520,6 +524,7 @@ * [Frequency Finder](https://github.com/TheAlgorithms/Python/blob/master/other/frequency_finder.py) * [Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/other/game_of_life.py) * [Gauss Easter](https://github.com/TheAlgorithms/Python/blob/master/other/gauss_easter.py) + * [Graham Scan](https://github.com/TheAlgorithms/Python/blob/master/other/graham_scan.py) * [Greedy](https://github.com/TheAlgorithms/Python/blob/master/other/greedy.py) * [Integeration By Simpson Approx](https://github.com/TheAlgorithms/Python/blob/master/other/integeration_by_simpson_approx.py) * [Largest Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/other/largest_subarray_sum.py) @@ -840,6 +845,7 @@ * [Merge Insertion Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_insertion_sort.py) * [Merge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/merge_sort.py) * [Natural Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/natural_sort.py) + * [Odd Even Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_sort.py) * [Odd Even Transposition Parallel](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_parallel.py) * [Odd Even Transposition Single Threaded](https://github.com/TheAlgorithms/Python/blob/master/sorts/odd_even_transposition_single_threaded.py) * [Pancake Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/pancake_sort.py) @@ -856,6 +862,7 @@ * [Recursive Quick Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/recursive_quick_sort.py) * [Selection Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/selection_sort.py) * [Shell Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/shell_sort.py) + * [Slowsort](https://github.com/TheAlgorithms/Python/blob/master/sorts/slowsort.py) * [Stooge Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/stooge_sort.py) * [Strand Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/strand_sort.py) * [Tim Sort](https://github.com/TheAlgorithms/Python/blob/master/sorts/tim_sort.py) diff --git a/other/graham_scan.py b/other/graham_scan.py new file mode 100644 index 000000000000..67c5cd8ab9d8 --- /dev/null +++ b/other/graham_scan.py @@ -0,0 +1,171 @@ +""" +This is a pure Python implementation of the merge-insertion sort algorithm +Source: https://en.wikipedia.org/wiki/Graham_scan + +For doctests run following command: +python3 -m doctest -v graham_scan.py +""" + +from __future__ import annotations + +from collections import deque +from enum import Enum +from math import atan2, degrees +from sys import maxsize + + +def graham_scan(points: list[list[int, int]]) -> list[list[int, int]]: + """Pure implementation of graham scan algorithm in Python + + :param points: The unique points on coordinates. + :return: The points on convex hell. + + Examples: + >>> graham_scan([(9, 6), (3, 1), (0, 0), (5, 5), (5, 2), (7, 0), (3, 3), (1, 4)]) + [(0, 0), (7, 0), (9, 6), (5, 5), (1, 4)] + + >>> graham_scan([(0, 0), (1, 0), (1, 1), (0, 1)]) + [(0, 0), (1, 0), (1, 1), (0, 1)] + + >>> graham_scan([(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)]) + [(0, 0), (1, 1), (2, 2), (3, 3), (-1, 2)] + + >>> graham_scan([(-100, 20), (99, 3), (1, 10000001), (5133186, -25), (-66, -4)]) + [(5133186, -25), (1, 10000001), (-100, 20), (-66, -4)] + """ + + if len(points) <= 2: + # There is no convex hull + raise ValueError("graham_scan: argument must contain more than 3 points.") + if len(points) == 3: + return points + # find the lowest and the most left point + minidx = 0 + miny, minx = maxsize, maxsize + for i, point in enumerate(points): + x = point[0] + y = point[1] + if y < miny: + miny = y + minx = x + minidx = i + if y == miny: + if x < minx: + minx = x + minidx = i + + # remove the lowest and the most left point from points for preparing for sort + points.pop(minidx) + + def angle_comparer(point: list[int, int], minx: int, miny: int) -> float: + """Return the angle toward to point from (minx, miny) + + :param point: The target point + minx: The starting point's x + miny: The starting point's y + :return: the angle + + Examples: + >>> angle_comparer([1,1], 0, 0) + 45.0 + + >>> angle_comparer([100,1], 10, 10) + -5.710593137499642 + + >>> angle_comparer([5,5], 2, 3) + 33.690067525979785 + """ + # sort the points accorgind to the angle from the lowest and the most left point + x = point[0] + y = point[1] + angle = degrees(atan2(y - miny, x - minx)) + return angle + + sorted_points = sorted(points, key=lambda point: angle_comparer(point, minx, miny)) + # This insert actually costs complexity, + # and you should insteadly add (minx, miny) into stack later. + # I'm using insert just for easy understanding. + sorted_points.insert(0, (minx, miny)) + + # traversal from the lowest and the most left point in anti-clockwise direction + # if direction gets right, the previous point is not the convex hull. + class Direction(Enum): + left = 1 + straight = 2 + right = 3 + + def check_direction( + starting: list[int, int], via: list[int, int], target: list[int, int] + ) -> Direction: + """Return the direction toward to the line from via to target from starting + + :param starting: The starting point + via: The via point + target: The target point + :return: the Direction + + Examples: + >>> check_direction([1,1], [2,2], [3,3]) + Direction.straight + + >>> check_direction([60,1], [-50,199], [30,2]) + Direction.left + + >>> check_direction([0,0], [5,5], [10,0]) + Direction.right + """ + x0, y0 = starting + x1, y1 = via + x2, y2 = target + via_angle = degrees(atan2(y1 - y0, x1 - x0)) + if via_angle < 0: + via_angle += 360 + target_angle = degrees(atan2(y2 - y0, x2 - x0)) + if target_angle < 0: + target_angle += 360 + # t- + # \ \ + # \ v + # \| + # s + # via_angle is always lower than target_angle, if direction is left. + # If they are same, it means they are on a same line of convex hull. + if target_angle > via_angle: + return Direction.left + if target_angle == via_angle: + return Direction.straight + if target_angle < via_angle: + return Direction.right + + stack = deque() + stack.append(sorted_points[0]) + stack.append(sorted_points[1]) + stack.append(sorted_points[2]) + # In any ways, the first 3 points line are towards left. + # Because we sort them the angle from minx, miny. + current_direction = Direction.left + + for i in range(3, len(sorted_points)): + while True: + starting = stack[-2] + via = stack[-1] + target = sorted_points[i] + next_direction = check_direction(starting, via, target) + + if next_direction == Direction.left: + current_direction = Direction.left + break + if next_direction == Direction.straight: + if current_direction == Direction.left: + # We keep current_direction as left. + # Because if the straight line keeps as straight, + # we want to know if this straight line is towards left. + break + elif current_direction == Direction.right: + # If the straight line is towards right, + # every previous points on those straigh line is not convex hull. + stack.pop() + if next_direction == Direction.right: + stack.pop() + stack.append(sorted_points[i]) + return list(stack) From afe38184b82e62d31649261a56ffb67a82aa202b Mon Sep 17 00:00:00 2001 From: fpringle Date: Sat, 6 Mar 2021 14:29:52 +0100 Subject: [PATCH 172/195] Added solution for Project Euler problem 109 (#4080) * Added solution for Project Euler problem 109 * New subscriptable builtin types * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 6 ++ project_euler/problem_109/__init__.py | 0 project_euler/problem_109/sol1.py | 89 +++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 project_euler/problem_109/__init__.py create mode 100644 project_euler/problem_109/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 61e20eeb571d..dfb673dea829 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -29,6 +29,8 @@ * [Binary Count Setbits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_count_setbits.py) * [Binary Count Trailing Zeros](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_count_trailing_zeros.py) * [Binary Or Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_or_operator.py) + * [Binary Shifts](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_shifts.py) + * [Binary Twos Complement](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_twos_complement.py) * [Binary Xor Operator](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/binary_xor_operator.py) * [Count Number Of One Bits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/count_number_of_one_bits.py) * [Reverse Bits](https://github.com/TheAlgorithms/Python/blob/master/bit_manipulation/reverse_bits.py) @@ -278,6 +280,7 @@ ## Graphics * [Bezier Curve](https://github.com/TheAlgorithms/Python/blob/master/graphics/bezier_curve.py) * [Koch Snowflake](https://github.com/TheAlgorithms/Python/blob/master/graphics/koch_snowflake.py) + * [Mandelbrot](https://github.com/TheAlgorithms/Python/blob/master/graphics/mandelbrot.py) * [Vector3 For 2D Rendering](https://github.com/TheAlgorithms/Python/blob/master/graphics/vector3_for_2d_rendering.py) ## Graphs @@ -469,6 +472,7 @@ * [Runge Kutta](https://github.com/TheAlgorithms/Python/blob/master/maths/runge_kutta.py) * [Segmented Sieve](https://github.com/TheAlgorithms/Python/blob/master/maths/segmented_sieve.py) * Series + * [Geometric Mean](https://github.com/TheAlgorithms/Python/blob/master/maths/series/geometric_mean.py) * [Geometric Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/geometric_series.py) * [Harmonic Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/harmonic_series.py) * [P Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/p_series.py) @@ -757,6 +761,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_102/sol1.py) * Problem 107 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_107/sol1.py) + * Problem 109 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_109/sol1.py) * Problem 112 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_112/sol1.py) * Problem 113 diff --git a/project_euler/problem_109/__init__.py b/project_euler/problem_109/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_109/sol1.py b/project_euler/problem_109/sol1.py new file mode 100644 index 000000000000..91c71eb9f4cb --- /dev/null +++ b/project_euler/problem_109/sol1.py @@ -0,0 +1,89 @@ +""" +In the game of darts a player throws three darts at a target board which is +split into twenty equal sized sections numbered one to twenty. + +The score of a dart is determined by the number of the region that the dart +lands in. A dart landing outside the red/green outer ring scores zero. The black +and cream regions inside this ring represent single scores. However, the red/green +outer ring and middle ring score double and treble scores respectively. + +At the centre of the board are two concentric circles called the bull region, or +bulls-eye. The outer bull is worth 25 points and the inner bull is a double, +worth 50 points. + +There are many variations of rules but in the most popular game the players will +begin with a score 301 or 501 and the first player to reduce their running total +to zero is a winner. However, it is normal to play a "doubles out" system, which +means that the player must land a double (including the double bulls-eye at the +centre of the board) on their final dart to win; any other dart that would reduce +their running total to one or lower means the score for that set of three darts +is "bust". + +When a player is able to finish on their current score it is called a "checkout" +and the highest checkout is 170: T20 T20 D25 (two treble 20s and double bull). + +There are exactly eleven distinct ways to checkout on a score of 6: + +D3 +D1 D2 +S2 D2 +D2 D1 +S4 D1 +S1 S1 D2 +S1 T1 D1 +S1 S3 D1 +D1 D1 D1 +D1 S2 D1 +S2 S2 D1 + +Note that D1 D2 is considered different to D2 D1 as they finish on different +doubles. However, the combination S1 T1 D1 is considered the same as T1 S1 D1. + +In addition we shall not include misses in considering combinations; for example, +D3 is the same as 0 D3 and 0 0 D3. + +Incredibly there are 42336 distinct ways of checking out in total. + +How many distinct ways can a player checkout with a score less than 100? + +Solution: + We first construct a list of the possible dart values, separated by type. + We then iterate through the doubles, followed by the possible 2 following throws. + If the total of these three darts is less than the given limit, we increment + the counter. +""" + +from itertools import combinations_with_replacement + + +def solution(limit: int = 100) -> int: + """ + Count the number of distinct ways a player can checkout with a score + less than limit. + >>> solution(171) + 42336 + >>> solution(50) + 12577 + """ + singles: list[int] = [x for x in range(1, 21)] + [25] + doubles: list[int] = [2 * x for x in range(1, 21)] + [50] + triples: list[int] = [3 * x for x in range(1, 21)] + all_values: list[int] = singles + doubles + triples + [0] + + num_checkouts: int = 0 + double: int + throw1: int + throw2: int + checkout_total: int + + for double in doubles: + for throw1, throw2 in combinations_with_replacement(all_values, 2): + checkout_total = double + throw1 + throw2 + if checkout_total < limit: + num_checkouts += 1 + + return num_checkouts + + +if __name__ == "__main__": + print(f"{solution() = }") From 4863405ac418b06fd696b1e4a86319954bbbeadd Mon Sep 17 00:00:00 2001 From: Ayush Bisht <61404154+ayushbisht2001@users.noreply.github.com> Date: Fri, 12 Mar 2021 12:55:54 +0530 Subject: [PATCH 173/195] Add arithmetic_mean.py (#4243) * arithmetic_mean * arithmetic_mean * checks * checked * Revert "checked" This reverts commit 3913a39ae2c4ee183443eed67ee7427e3d322ad4. * checks-3 * update-1 --- maths/series/arithmetic_mean.py | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 maths/series/arithmetic_mean.py diff --git a/maths/series/arithmetic_mean.py b/maths/series/arithmetic_mean.py new file mode 100644 index 000000000000..b5d64b63ac3f --- /dev/null +++ b/maths/series/arithmetic_mean.py @@ -0,0 +1,66 @@ +""" +ARITHMETIC MEAN : https://en.wikipedia.org/wiki/Arithmetic_mean + +""" + + +def is_arithmetic_series(series: list) -> bool: + """ + checking whether the input series is arithmetic series or not + + >>> is_arithmetic_series([2, 4, 6]) + True + >>> is_arithmetic_series([3, 6, 12, 24]) + False + >>> is_arithmetic_series([1, 2, 3]) + True + """ + if len(series) == 1: + return True + common_diff = series[1] - series[0] + for index in range(len(series) - 1): + if series[index + 1] - series[index] != common_diff: + return False + return True + + +def arithmetic_mean(series: list) -> float: + """ + return the arithmetic mean of series + + >>> arithmetic_mean([2, 4, 6]) + 4.0 + >>> arithmetic_mean([3, 6, 9, 12]) + 7.5 + >>> arithmetic_mean(4) + Traceback (most recent call last): + ... + ValueError: Input series is not valid, valid series - [2, 4, 6] + >>> arithmetic_mean([4, 8, 1]) + Traceback (most recent call last): + ... + ValueError: Input list is not an arithmetic series + >>> arithmetic_mean([1, 2, 3]) + 2.0 + >>> arithmetic_mean([]) + Traceback (most recent call last): + ... + ValueError: Input list must be a non empty list + + """ + if not isinstance(series, list): + raise ValueError("Input series is not valid, valid series - [2, 4, 6]") + if len(series) == 0: + raise ValueError("Input list must be a non empty list") + if not is_arithmetic_series(series): + raise ValueError("Input list is not an arithmetic series") + answer = 0 + for val in series: + answer += val + return answer / len(series) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1ac1981c0151d3627c49a88a404f231897496261 Mon Sep 17 00:00:00 2001 From: Shantanu Joshi <42472191+shan7030@users.noreply.github.com> Date: Thu, 18 Mar 2021 13:09:53 +0530 Subject: [PATCH 174/195] [mypy] Add/fix type annotations for electronics algorithms (#4247) * Fix mypy errors for scheduling/first_come_first_served * Fix mypy errors for scheduling/round_robin.py * Fix mypy errors for scheduling/shortest_job_first.py * Fix isort errors * Fix mypy errors for electronics/ohms_law.py * Fix mypy errors for electronics/electric_power.py * Fix black errors --- electronics/electric_power.py | 3 ++- electronics/ohms_law.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/electronics/electric_power.py b/electronics/electric_power.py index 768c3d5c7232..8f0293bd2d10 100644 --- a/electronics/electric_power.py +++ b/electronics/electric_power.py @@ -1,8 +1,9 @@ # https://en.m.wikipedia.org/wiki/Electric_power from collections import namedtuple +from typing import Tuple -def electric_power(voltage: float, current: float, power: float) -> float: +def electric_power(voltage: float, current: float, power: float) -> Tuple: """ This function can calculate any one of the three (voltage, current, power), fundamental value of electrical system. diff --git a/electronics/ohms_law.py b/electronics/ohms_law.py index a7b37b635397..c53619a10935 100644 --- a/electronics/ohms_law.py +++ b/electronics/ohms_law.py @@ -1,7 +1,8 @@ # https://en.wikipedia.org/wiki/Ohm%27s_law +from typing import Dict -def ohms_law(voltage: float, current: float, resistance: float) -> float: +def ohms_law(voltage: float, current: float, resistance: float) -> Dict[str, float]: """ Apply Ohm's Law, on any two given electrical values, which can be voltage, current, and resistance, and then in a Python dict return name/value pair of the zero value. From c9d9b6f0f8d0dfc7f01cf0aea9dc4c2e5ce16166 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Fri, 19 Mar 2021 10:57:32 +0530 Subject: [PATCH 175/195] mypy-fix for "covid_stats_via_xpath.py" (#4233) --- web_programming/covid_stats_via_xpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py index d22ed017878c..85ea5d940d85 100644 --- a/web_programming/covid_stats_via_xpath.py +++ b/web_programming/covid_stats_via_xpath.py @@ -7,7 +7,7 @@ from collections import namedtuple import requests -from lxml import html +from lxml import html # type: ignore covid_data = namedtuple("covid_data", "cases deaths recovered") From 682edb297f182bcb7579ec64e48474518e8d4626 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Fri, 19 Mar 2021 15:59:54 +0530 Subject: [PATCH 176/195] Include mypy instructions in CONTRIBUTING.md (#4271) * reupload * Include mypy instructions * delete file * fixed trailing whitespaces * options before file path Co-authored-by: Christian Clauss --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4c81a5ecd98..76ee1312f345 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -155,6 +155,8 @@ We want your work to be readable by others; therefore, we encourage you to note return a + b ``` + Instructions on how to install mypy can be found [here](https://github.com/python/mypy). Please use the command `mypy --ignore-missing-imports .` to test all files or `mypy --ignore-missing-imports path/to/file.py` to test a specific file. + - [__List comprehensions and generators__](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) are preferred over the use of `lambda`, `map`, `filter`, `reduce` but the important thing is to demonstrate the power of Python in code that is easy to read and maintain. - Avoid importing external libraries for basic algorithms. Only use those libraries for complicated algorithms. From 22b196b362e90897b897c867ded9dd4116735082 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 20 Mar 2021 06:09:56 +0100 Subject: [PATCH 177/195] GitHub Actions: fast-fail on black formatting issues (#4268) * GitHub Actions: fast-fail on black formatting issues Give fast feedback to contributors https://github.com/psf/black#github-actions * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/workflows/pre-commit.yml | 1 + DIRECTORY.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 96175cfecea5..17fdad1204e9 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,6 +14,7 @@ jobs: ~/.cache/pip key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} - uses: actions/setup-python@v2 + - uses: psf/black@stable - name: Install pre-commit run: | python -m pip install --upgrade pip diff --git a/DIRECTORY.md b/DIRECTORY.md index dfb673dea829..136825e41976 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -472,6 +472,7 @@ * [Runge Kutta](https://github.com/TheAlgorithms/Python/blob/master/maths/runge_kutta.py) * [Segmented Sieve](https://github.com/TheAlgorithms/Python/blob/master/maths/segmented_sieve.py) * Series + * [Arithmetic Mean](https://github.com/TheAlgorithms/Python/blob/master/maths/series/arithmetic_mean.py) * [Geometric Mean](https://github.com/TheAlgorithms/Python/blob/master/maths/series/geometric_mean.py) * [Geometric Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/geometric_series.py) * [Harmonic Series](https://github.com/TheAlgorithms/Python/blob/master/maths/series/harmonic_series.py) From 7f37ea1e7f4dacb6e362e710873a47a4fd3c05f4 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 20 Mar 2021 06:12:17 +0100 Subject: [PATCH 178/195] Update our pre-commit dependencies (#4273) * .pre-commit-config.yaml: mypy directories that pass * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .pre-commit-config.yaml | 12 ++++++------ machine_learning/k_nearest_neighbours.py | 2 +- maths/polynomial_evaluation.py | 4 ++-- searches/hill_climbing.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3288e1c5eef..d6be7f60f714 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: check-executables-have-shebangs - id: check-yaml @@ -13,17 +13,17 @@ repos: )$ - id: requirements-txt-fixer - repo: https://github.com/psf/black - rev: stable + rev: 20.8b1 hooks: - id: black - repo: https://github.com/PyCQA/isort - rev: 5.5.3 + rev: 5.7.0 hooks: - id: isort args: - --profile=black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 + rev: 3.9.0 hooks: - id: flake8 args: @@ -38,11 +38,11 @@ repos: # args: # - --ignore-missing-imports - repo: https://github.com/codespell-project/codespell - rev: v1.17.1 + rev: v2.0.0 hooks: - id: codespell args: - - --ignore-words-list=ans,fo,followings,hist,iff,mater,secant,som,tim + - --ignore-words-list=ans,crate,fo,followings,hist,iff,mater,secant,som,tim - --skip="./.*,./other/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" - --quiet-level=2 exclude: | diff --git a/machine_learning/k_nearest_neighbours.py b/machine_learning/k_nearest_neighbours.py index e90ea09a58c1..2a90cfe5987a 100644 --- a/machine_learning/k_nearest_neighbours.py +++ b/machine_learning/k_nearest_neighbours.py @@ -32,7 +32,7 @@ def classifier(train_data, train_target, classes, point, k=5): :train_data: Set of points that are classified into two or more classes :train_target: List of classes in the order of train_data points :classes: Labels of the classes - :point: The data point that needs to be classifed + :point: The data point that needs to be classified >>> X_train = [[0, 0], [1, 0], [0, 1], [0.5, 0.5], [3, 3], [2, 3], [3, 2]] >>> y_train = [0, 0, 0, 0, 1, 1, 1] diff --git a/maths/polynomial_evaluation.py b/maths/polynomial_evaluation.py index e929a2d02972..68ff97ddd25d 100644 --- a/maths/polynomial_evaluation.py +++ b/maths/polynomial_evaluation.py @@ -5,7 +5,7 @@ def evaluate_poly(poly: Sequence[float], x: float) -> float: """Evaluate a polynomial f(x) at specified point x and return the value. Arguments: - poly -- the coeffiecients of a polynomial as an iterable in order of + poly -- the coefficients of a polynomial as an iterable in order of ascending degree x -- the point at which to evaluate the polynomial @@ -26,7 +26,7 @@ def horner(poly: Sequence[float], x: float) -> float: https://en.wikipedia.org/wiki/Horner's_method Arguments: - poly -- the coeffiecients of a polynomial as an iterable in order of + poly -- the coefficients of a polynomial as an iterable in order of ascending degree x -- the point at which to evaluate the polynomial diff --git a/searches/hill_climbing.py b/searches/hill_climbing.py index 70622ebefb4e..bb24e781a6c1 100644 --- a/searches/hill_climbing.py +++ b/searches/hill_climbing.py @@ -60,7 +60,7 @@ def get_neighbors(self): def __hash__(self): """ - hash the string represetation of the current search state. + hash the string representation of the current search state. """ return hash(str(self)) From fb65742868c7d231b5d203b5e298e3b66e6b44d2 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Mar 2021 11:19:30 +0530 Subject: [PATCH 179/195] New fractals folder (#4277) * reupload * delete file * Move koch_snowflake.py to fractals-folder * Move mandelbrot.py to fractals-folder * Move sierpinski_triangle.py to fractals-folder --- {graphics => fractals}/koch_snowflake.py | 0 {graphics => fractals}/mandelbrot.py | 0 {other => fractals}/sierpinski_triangle.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {graphics => fractals}/koch_snowflake.py (100%) rename {graphics => fractals}/mandelbrot.py (100%) rename {other => fractals}/sierpinski_triangle.py (100%) diff --git a/graphics/koch_snowflake.py b/fractals/koch_snowflake.py similarity index 100% rename from graphics/koch_snowflake.py rename to fractals/koch_snowflake.py diff --git a/graphics/mandelbrot.py b/fractals/mandelbrot.py similarity index 100% rename from graphics/mandelbrot.py rename to fractals/mandelbrot.py diff --git a/other/sierpinski_triangle.py b/fractals/sierpinski_triangle.py similarity index 100% rename from other/sierpinski_triangle.py rename to fractals/sierpinski_triangle.py From ad00c5eb85f194acf50a2c9b8214fca13abb5a8b Mon Sep 17 00:00:00 2001 From: fpringle Date: Sat, 20 Mar 2021 06:59:48 +0100 Subject: [PATCH 180/195] feat: Add solution for Project Euler Problem 121 (#4261) * Added solution for Project Euler problem 121 * Updated typing for 3.9 * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 2 + project_euler/problem_121/__init__.py | 0 project_euler/problem_121/sol1.py | 64 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 project_euler/problem_121/__init__.py create mode 100644 project_euler/problem_121/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 136825e41976..070119f2f674 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -772,6 +772,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_119/sol1.py) * Problem 120 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_120/sol1.py) + * Problem 121 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_121/sol1.py) * Problem 123 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_123/sol1.py) * Problem 125 diff --git a/project_euler/problem_121/__init__.py b/project_euler/problem_121/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_121/sol1.py b/project_euler/problem_121/sol1.py new file mode 100644 index 000000000000..93679cf4a897 --- /dev/null +++ b/project_euler/problem_121/sol1.py @@ -0,0 +1,64 @@ +""" +A bag contains one red disc and one blue disc. In a game of chance a player takes a +disc at random and its colour is noted. After each turn the disc is returned to the +bag, an extra red disc is added, and another disc is taken at random. + +The player pays £1 to play and wins if they have taken more blue discs than red +discs at the end of the game. + +If the game is played for four turns, the probability of a player winning is exactly +11/120, and so the maximum prize fund the banker should allocate for winning in this +game would be £10 before they would expect to incur a loss. Note that any payout will +be a whole number of pounds and also includes the original £1 paid to play the game, +so in the example given the player actually wins £9. + +Find the maximum prize fund that should be allocated to a single game in which +fifteen turns are played. + + +Solution: + For each 15-disc sequence of red and blue for which there are more red than blue, + we calculate the probability of that sequence and add it to the total probability + of the player winning. The inverse of this probability gives an upper bound for + the prize if the banker wants to avoid an expected loss. +""" + +from itertools import product + + +def solution(num_turns: int = 15) -> int: + """ + Find the maximum prize fund that should be allocated to a single game in which + fifteen turns are played. + >>> solution(4) + 10 + >>> solution(10) + 225 + """ + total_prob: float = 0.0 + prob: float + num_blue: int + num_red: int + ind: int + col: int + series: tuple[int, ...] + + for series in product(range(2), repeat=num_turns): + num_blue = series.count(1) + num_red = num_turns - num_blue + if num_red >= num_blue: + continue + prob = 1.0 + for ind, col in enumerate(series, 2): + if col == 0: + prob *= (ind - 1) / ind + else: + prob *= 1 / ind + + total_prob += prob + + return int(1 / total_prob) + + +if __name__ == "__main__": + print(f"{solution() = }") From bc090d8db3085878dfccec42e03f4969abc07fca Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 20 Mar 2021 07:01:13 +0100 Subject: [PATCH 181/195] build.yml: Run mypy --ignore-missing-imports (#4276) * build.yml: Run mypy --ignore-missing-imports * pip install mypy * Remove failing directories * Add fractals and drop python-m --- .github/workflows/build.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e15d18ade8e..1e8d04126002 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,22 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools six wheel - python -m pip install pytest-cov -r requirements.txt + python -m pip install mypy pytest-cov -r requirements.txt + # FIXME: #4052 fix mypy errors in other directories and add them here + - run: mypy --ignore-missing-imports + backtracking + bit_manipulation + blockchain + boolean_algebra + cellular_automata + computer_vision + fractals + fuzzy_logic + genetic_algorithm + geodesy + knapsack + networking_flow + scheduling sorts - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} From ee6c459161c2b178990d88bc31f0d59058dc32d7 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 20 Mar 2021 11:41:10 +0530 Subject: [PATCH 182/195] refactor: Rename explicit_euler.py to euler_method.py (#4275) * reupload * Rename explicit_euler.py to euler_method.py * Delete file --- maths/{explicit_euler.py => euler_method.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename maths/{explicit_euler.py => euler_method.py} (100%) diff --git a/maths/explicit_euler.py b/maths/euler_method.py similarity index 100% rename from maths/explicit_euler.py rename to maths/euler_method.py From 7bd733b35989007d5adac5b9da0d4050a63f3f1e Mon Sep 17 00:00:00 2001 From: DevanshiPatel18 <61454611+DevanshiPatel18@users.noreply.github.com> Date: Sat, 20 Mar 2021 11:48:38 +0530 Subject: [PATCH 183/195] feat: Add greedy_coin_change.py algorithm (#3805) * Hacktoberfest: Add greedy_coin_change.py file added the file in greedy_methods folder to implement the same method Altered the code according to the changes that were requested. * Added doctests. doctests added to the function find_minimum_change. * Added Greedy change file in Maths folder. * updating DIRECTORY.md * Deleted Greedy Method Folder * updating DIRECTORY.md * Update greedy_coin_change.py * fix: black formatting issues Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Dhruv Manilawala --- DIRECTORY.md | 9 ++-- maths/greedy_coin_change.py | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 maths/greedy_coin_change.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 070119f2f674..2f57a9db5769 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -267,6 +267,11 @@ * Tests * [Test Send File](https://github.com/TheAlgorithms/Python/blob/master/file_transfer/tests/test_send_file.py) +## Fractals + * [Koch Snowflake](https://github.com/TheAlgorithms/Python/blob/master/fractals/koch_snowflake.py) + * [Mandelbrot](https://github.com/TheAlgorithms/Python/blob/master/fractals/mandelbrot.py) + * [Sierpinski Triangle](https://github.com/TheAlgorithms/Python/blob/master/fractals/sierpinski_triangle.py) + ## Fuzzy Logic * [Fuzzy Operations](https://github.com/TheAlgorithms/Python/blob/master/fuzzy_logic/fuzzy_operations.py) @@ -279,8 +284,6 @@ ## Graphics * [Bezier Curve](https://github.com/TheAlgorithms/Python/blob/master/graphics/bezier_curve.py) - * [Koch Snowflake](https://github.com/TheAlgorithms/Python/blob/master/graphics/koch_snowflake.py) - * [Mandelbrot](https://github.com/TheAlgorithms/Python/blob/master/graphics/mandelbrot.py) * [Vector3 For 2D Rendering](https://github.com/TheAlgorithms/Python/blob/master/graphics/vector3_for_2d_rendering.py) ## Graphs @@ -432,6 +435,7 @@ * [Gamma](https://github.com/TheAlgorithms/Python/blob/master/maths/gamma.py) * [Gaussian](https://github.com/TheAlgorithms/Python/blob/master/maths/gaussian.py) * [Greatest Common Divisor](https://github.com/TheAlgorithms/Python/blob/master/maths/greatest_common_divisor.py) + * [Greedy Coin Change](https://github.com/TheAlgorithms/Python/blob/master/maths/greedy_coin_change.py) * [Hardy Ramanujanalgo](https://github.com/TheAlgorithms/Python/blob/master/maths/hardy_ramanujanalgo.py) * [Is Square Free](https://github.com/TheAlgorithms/Python/blob/master/maths/is_square_free.py) * [Jaccard Similarity](https://github.com/TheAlgorithms/Python/blob/master/maths/jaccard_similarity.py) @@ -547,7 +551,6 @@ * [Primelib](https://github.com/TheAlgorithms/Python/blob/master/other/primelib.py) * [Scoring Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/scoring_algorithm.py) * [Sdes](https://github.com/TheAlgorithms/Python/blob/master/other/sdes.py) - * [Sierpinski Triangle](https://github.com/TheAlgorithms/Python/blob/master/other/sierpinski_triangle.py) * [Tower Of Hanoi](https://github.com/TheAlgorithms/Python/blob/master/other/tower_of_hanoi.py) * [Triplet Sum](https://github.com/TheAlgorithms/Python/blob/master/other/triplet_sum.py) * [Two Pointer](https://github.com/TheAlgorithms/Python/blob/master/other/two_pointer.py) diff --git a/maths/greedy_coin_change.py b/maths/greedy_coin_change.py new file mode 100644 index 000000000000..5a7d9e8d84ae --- /dev/null +++ b/maths/greedy_coin_change.py @@ -0,0 +1,102 @@ +""" +Test cases: +Do you want to enter your denominations ? (Y/N) :N +Enter the change you want to make in Indian Currency: 987 +Following is minimal change for 987 : +500 100 100 100 100 50 20 10 5 2 + +Do you want to enter your denominations ? (Y/N) :Y +Enter number of denomination:10 +1 +5 +10 +20 +50 +100 +200 +500 +1000 +2000 +Enter the change you want to make: 18745 +Following is minimal change for 18745 : +2000 2000 2000 2000 2000 2000 2000 2000 2000 500 200 20 20 5 + +Do you want to enter your denominations ? (Y/N) :N +Enter the change you want to make: 0 +The total value cannot be zero or negative. +Do you want to enter your denominations ? (Y/N) :N +Enter the change you want to make: -98 +The total value cannot be zero or negative. + +Do you want to enter your denominations ? (Y/N) :Y +Enter number of denomination:5 +1 +5 +100 +500 +1000 +Enter the change you want to make: 456 +Following is minimal change for 456 : +100 100 100 100 5 5 5 5 5 5 5 5 5 5 5 1 +""" + + +def find_minimum_change(denominations: list[int], value: int) -> list[int]: + """ + Find the minimum change from the given denominations and value + >>> find_minimum_change([1, 5, 10, 20, 50, 100, 200, 500, 1000,2000], 18745) + [2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 500, 200, 20, 20, 5] + >>> find_minimum_change([1, 2, 5, 10, 20, 50, 100, 500, 2000], 987) + [500, 100, 100, 100, 100, 50, 20, 10, 5, 2] + >>> find_minimum_change([1, 2, 5, 10, 20, 50, 100, 500, 2000], 0) + [] + >>> find_minimum_change([1, 2, 5, 10, 20, 50, 100, 500, 2000], -98) + [] + >>> find_minimum_change([1, 5, 100, 500, 1000], 456) + [100, 100, 100, 100, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1] + """ + total_value = int(value) + + # Initialize Result + answer = [] + + # Traverse through all denomination + for denomination in reversed(denominations): + + # Find denominations + while int(total_value) >= int(denomination): + total_value -= int(denomination) + answer.append(denomination) # Append the "answers" array + + return answer + + +# Driver Code +if __name__ == "__main__": + + denominations = list() + value = 0 + + if ( + input("Do you want to enter your denominations ? (yY/n): ").strip().lower() + == "y" + ): + n = int(input("Enter the number of denominations you want to add: ").strip()) + + for i in range(0, n): + denominations.append(int(input(f"Denomination {i}: ").strip())) + value = input("Enter the change you want to make in Indian Currency: ").strip() + else: + # All denominations of Indian Currency if user does not enter + denominations = [1, 2, 5, 10, 20, 50, 100, 500, 2000] + value = input("Enter the change you want to make: ").strip() + + if int(value) == 0 or int(value) < 0: + print("The total value cannot be zero or negative.") + + else: + print(f"Following is minimal change for {value}: ") + answer = find_minimum_change(denominations, value) + # Print result + for i in range(len(answer)): + print(answer[i], end=" ") From bc06b6de2deaaaab681b3fdf523e71f00001a361 Mon Sep 17 00:00:00 2001 From: "Novice :)" <72334601+noviicee@users.noreply.github.com> Date: Sat, 20 Mar 2021 12:02:16 +0530 Subject: [PATCH 184/195] [mypy] Fix type annotations for cellular_automata (#4236) * [mypy] Fix type annotations for cellullar_automata * mypy --ignore-missing-imports * mypy --ignore-missing-imports * Blank lines * Blank lines Co-authored-by: Christian Clauss --- cellular_automata/conways_game_of_life.py | 2 +- cellular_automata/one_dimensional.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cellular_automata/conways_game_of_life.py b/cellular_automata/conways_game_of_life.py index dc349b7ac507..321baa3a3794 100644 --- a/cellular_automata/conways_game_of_life.py +++ b/cellular_automata/conways_game_of_life.py @@ -7,7 +7,7 @@ from typing import List -from PIL import Image # type: ignore +from PIL import Image # Define glider example GLIDER = [ diff --git a/cellular_automata/one_dimensional.py b/cellular_automata/one_dimensional.py index 5de2c5b994e3..da77e444502f 100644 --- a/cellular_automata/one_dimensional.py +++ b/cellular_automata/one_dimensional.py @@ -6,7 +6,7 @@ from __future__ import annotations -from PIL import Image # type: ignore +from PIL import Image # Define the first generation of cells # fmt: off From 96f73d68ceebc722524227cd83452e03f23528d5 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sun, 21 Mar 2021 16:35:10 +0530 Subject: [PATCH 185/195] Move files to strings folder (#4283) * Move files to strings-folder * moved the file "words" back to the original folder * moved "anagram.py" also back * fix the codespell ignore-list --- .pre-commit-config.yaml | 4 ++-- {other => strings}/autocomplete_using_trie.py | 0 {other => strings}/detecting_english_programmatically.py | 0 {other => strings}/dictionary.txt | 0 {other => strings}/frequency_finder.py | 0 {other => strings}/palindrome.py | 0 {other => strings}/word_patterns.py | 0 7 files changed, 2 insertions(+), 2 deletions(-) rename {other => strings}/autocomplete_using_trie.py (100%) rename {other => strings}/detecting_english_programmatically.py (100%) rename {other => strings}/dictionary.txt (100%) rename {other => strings}/frequency_finder.py (100%) rename {other => strings}/palindrome.py (100%) rename {other => strings}/word_patterns.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6be7f60f714..ee422e61a03b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,11 +43,11 @@ repos: - id: codespell args: - --ignore-words-list=ans,crate,fo,followings,hist,iff,mater,secant,som,tim - - --skip="./.*,./other/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" + - --skip="./.*,./strings/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" - --quiet-level=2 exclude: | (?x)^( - other/dictionary.txt | + strings/dictionary.txt | other/words | project_euler/problem_022/p022_names.txt )$ diff --git a/other/autocomplete_using_trie.py b/strings/autocomplete_using_trie.py similarity index 100% rename from other/autocomplete_using_trie.py rename to strings/autocomplete_using_trie.py diff --git a/other/detecting_english_programmatically.py b/strings/detecting_english_programmatically.py similarity index 100% rename from other/detecting_english_programmatically.py rename to strings/detecting_english_programmatically.py diff --git a/other/dictionary.txt b/strings/dictionary.txt similarity index 100% rename from other/dictionary.txt rename to strings/dictionary.txt diff --git a/other/frequency_finder.py b/strings/frequency_finder.py similarity index 100% rename from other/frequency_finder.py rename to strings/frequency_finder.py diff --git a/other/palindrome.py b/strings/palindrome.py similarity index 100% rename from other/palindrome.py rename to strings/palindrome.py diff --git a/other/word_patterns.py b/strings/word_patterns.py similarity index 100% rename from other/word_patterns.py rename to strings/word_patterns.py From e6268287d49b2c8a0ecb904f3d1181c013acc82c Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Mon, 22 Mar 2021 12:29:51 +0530 Subject: [PATCH 186/195] fix(mypy): Fix annotations for 13 cipher algorithms (#4278) * Initial fix for mypy errors in some cipher algorithms * fix(mypy): Update type hints * fix(mypy): Update type hints for enigma_machine2.py * Update as per the suggestion Co-authored-by: Christian Clauss Co-authored-by: Christian Clauss --- ciphers/a1z26.py | 8 ++--- ciphers/affine_cipher.py | 42 +++++++++++----------- ciphers/atbash.py | 4 +-- ciphers/base32.py | 2 +- ciphers/base85.py | 2 +- ciphers/beaufort_cipher.py | 2 +- ciphers/brute_force_caesar_cipher.py | 2 +- ciphers/cryptomath_module.py | 4 +-- ciphers/decrypt_caesar_with_chi_squared.py | 30 ++++++++-------- ciphers/diffie.py | 25 ++++++++----- ciphers/elgamal_key_generator.py | 36 +++++++++---------- ciphers/enigma_machine2.py | 24 ++++++++----- ciphers/rsa_key_generator.py | 9 +++-- 13 files changed, 101 insertions(+), 89 deletions(-) diff --git a/ciphers/a1z26.py b/ciphers/a1z26.py index 92710ec44b0e..e6684fb1e6fc 100644 --- a/ciphers/a1z26.py +++ b/ciphers/a1z26.py @@ -7,7 +7,7 @@ """ -def encode(plain: str) -> list: +def encode(plain: str) -> list[int]: """ >>> encode("myname") [13, 25, 14, 1, 13, 5] @@ -15,7 +15,7 @@ def encode(plain: str) -> list: return [ord(elem) - 96 for elem in plain] -def decode(encoded: list) -> str: +def decode(encoded: list[int]) -> str: """ >>> decode([13, 25, 14, 1, 13, 5]) 'myname' @@ -23,8 +23,8 @@ def decode(encoded: list) -> str: return "".join(chr(elem + 96) for elem in encoded) -def main(): - encoded = encode(input("->").strip().lower()) +def main() -> None: + encoded = encode(input("-> ").strip().lower()) print("Encoded: ", encoded) print("Decoded:", decode(encoded)) diff --git a/ciphers/affine_cipher.py b/ciphers/affine_cipher.py index cf8c0d5f4c1d..d3b806ba1eeb 100644 --- a/ciphers/affine_cipher.py +++ b/ciphers/affine_cipher.py @@ -9,26 +9,6 @@ ) -def main(): - """ - >>> key = get_random_key() - >>> msg = "This is a test!" - >>> decrypt_message(key, encrypt_message(key, msg)) == msg - True - """ - message = input("Enter message: ").strip() - key = int(input("Enter key [2000 - 9000]: ").strip()) - mode = input("Encrypt/Decrypt [E/D]: ").strip().lower() - - if mode.startswith("e"): - mode = "encrypt" - translated = encrypt_message(key, message) - elif mode.startswith("d"): - mode = "decrypt" - translated = decrypt_message(key, message) - print(f"\n{mode.title()}ed text: \n{translated}") - - def check_keys(keyA: int, keyB: int, mode: str) -> None: if mode == "encrypt": if keyA == 1: @@ -80,7 +60,7 @@ def decrypt_message(key: int, message: str) -> str: keyA, keyB = divmod(key, len(SYMBOLS)) check_keys(keyA, keyB, "decrypt") plainText = "" - modInverseOfkeyA = cryptomath.findModInverse(keyA, len(SYMBOLS)) + modInverseOfkeyA = cryptomath.find_mod_inverse(keyA, len(SYMBOLS)) for symbol in message: if symbol in SYMBOLS: symIndex = SYMBOLS.find(symbol) @@ -98,6 +78,26 @@ def get_random_key() -> int: return keyA * len(SYMBOLS) + keyB +def main() -> None: + """ + >>> key = get_random_key() + >>> msg = "This is a test!" + >>> decrypt_message(key, encrypt_message(key, msg)) == msg + True + """ + message = input("Enter message: ").strip() + key = int(input("Enter key [2000 - 9000]: ").strip()) + mode = input("Encrypt/Decrypt [E/D]: ").strip().lower() + + if mode.startswith("e"): + mode = "encrypt" + translated = encrypt_message(key, message) + elif mode.startswith("d"): + mode = "decrypt" + translated = decrypt_message(key, message) + print(f"\n{mode.title()}ed text: \n{translated}") + + if __name__ == "__main__": import doctest diff --git a/ciphers/atbash.py b/ciphers/atbash.py index c17d1e34f37a..5c2aea610bff 100644 --- a/ciphers/atbash.py +++ b/ciphers/atbash.py @@ -61,6 +61,6 @@ def benchmark() -> None: if __name__ == "__main__": - for sequence in ("ABCDEFGH", "123GGjj", "testStringtest", "with space"): - print(f"{sequence} encrypted in atbash: {atbash(sequence)}") + for example in ("ABCDEFGH", "123GGjj", "testStringtest", "with space"): + print(f"{example} encrypted in atbash: {atbash(example)}") benchmark() diff --git a/ciphers/base32.py b/ciphers/base32.py index 5bba8c4dd685..da289a7210e8 100644 --- a/ciphers/base32.py +++ b/ciphers/base32.py @@ -1,7 +1,7 @@ import base64 -def main(): +def main() -> None: inp = input("->") encoded = inp.encode("utf-8") # encoded the input (we need a bytes like object) b32encoded = base64.b32encode(encoded) # b32encoded the encoded string diff --git a/ciphers/base85.py b/ciphers/base85.py index ebfd0480f794..9740299b9771 100644 --- a/ciphers/base85.py +++ b/ciphers/base85.py @@ -1,7 +1,7 @@ import base64 -def main(): +def main() -> None: inp = input("->") encoded = inp.encode("utf-8") # encoded the input (we need a bytes like object) a85encoded = base64.a85encode(encoded) # a85encoded the encoded string diff --git a/ciphers/beaufort_cipher.py b/ciphers/beaufort_cipher.py index c885dec74001..8eae847a7ff7 100644 --- a/ciphers/beaufort_cipher.py +++ b/ciphers/beaufort_cipher.py @@ -66,7 +66,7 @@ def original_text(cipher_text: str, key_new: str) -> str: return or_txt -def main(): +def main() -> None: message = "THE GERMAN ATTACK" key = "SECRET" key_new = generate_key(message, key) diff --git a/ciphers/brute_force_caesar_cipher.py b/ciphers/brute_force_caesar_cipher.py index 13a165245403..8ab6e77307b4 100644 --- a/ciphers/brute_force_caesar_cipher.py +++ b/ciphers/brute_force_caesar_cipher.py @@ -43,7 +43,7 @@ def decrypt(message: str) -> None: print(f"Decryption using Key #{key}: {translated}") -def main(): +def main() -> None: message = input("Encrypted message: ") message = message.upper() decrypt(message) diff --git a/ciphers/cryptomath_module.py b/ciphers/cryptomath_module.py index ffeac1617f64..be8764ff38c3 100644 --- a/ciphers/cryptomath_module.py +++ b/ciphers/cryptomath_module.py @@ -4,9 +4,9 @@ def gcd(a: int, b: int) -> int: return b -def findModInverse(a: int, m: int) -> int: +def find_mod_inverse(a: int, m: int) -> int: if gcd(a, m) != 1: - return None + raise ValueError(f"mod inverse of {a!r} and {m!r} does not exist") u1, u2, u3 = 1, 0, a v1, v2, v3 = 0, 1, m while v3 != 0: diff --git a/ciphers/decrypt_caesar_with_chi_squared.py b/ciphers/decrypt_caesar_with_chi_squared.py index 41b4a12ba453..e7faeae73773 100644 --- a/ciphers/decrypt_caesar_with_chi_squared.py +++ b/ciphers/decrypt_caesar_with_chi_squared.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 -from typing import Tuple +from typing import Optional def decrypt_caesar_with_chi_squared( ciphertext: str, - cipher_alphabet: str = None, - frequencies_dict: str = None, + cipher_alphabet: Optional[list[str]] = None, + frequencies_dict: Optional[dict[str, float]] = None, case_sensetive: bool = False, -) -> Tuple[int, float, str]: +) -> tuple[int, float, str]: """ Basic Usage =========== @@ -123,9 +123,9 @@ def decrypt_caesar_with_chi_squared( AttributeError: 'int' object has no attribute 'lower' """ alphabet_letters = cipher_alphabet or [chr(i) for i in range(97, 123)] - frequencies_dict = frequencies_dict or {} - if frequencies_dict == {}: + # If the argument is None or the user provided an empty dictionary + if not frequencies_dict: # Frequencies of letters in the english language (how much they show up) frequencies = { "a": 0.08497, @@ -163,7 +163,7 @@ def decrypt_caesar_with_chi_squared( ciphertext = ciphertext.lower() # Chi squared statistic values - chi_squared_statistic_values = {} + chi_squared_statistic_values: dict[int, tuple[float, str]] = {} # cycle through all of the shifts for shift in range(len(alphabet_letters)): @@ -215,22 +215,22 @@ def decrypt_caesar_with_chi_squared( chi_squared_statistic += chi_letter_value # Add the data to the chi_squared_statistic_values dictionary - chi_squared_statistic_values[shift] = [ + chi_squared_statistic_values[shift] = ( chi_squared_statistic, decrypted_with_shift, - ] + ) # Get the most likely cipher by finding the cipher with the smallest chi squared # statistic - most_likely_cipher = min( + most_likely_cipher: int = min( chi_squared_statistic_values, key=chi_squared_statistic_values.get - ) + ) # type: ignore # First argument to `min` is not optional # Get all the data from the most likely cipher (key, decoded message) - most_likely_cipher_chi_squared_value = chi_squared_statistic_values[ - most_likely_cipher - ][0] - decoded_most_likely_cipher = chi_squared_statistic_values[most_likely_cipher][1] + ( + most_likely_cipher_chi_squared_value, + decoded_most_likely_cipher, + ) = chi_squared_statistic_values[most_likely_cipher] # Return the data on the most likely shift return ( diff --git a/ciphers/diffie.py b/ciphers/diffie.py index 44b12bf9d103..a23a8104afe2 100644 --- a/ciphers/diffie.py +++ b/ciphers/diffie.py @@ -1,4 +1,7 @@ -def find_primitive(n: int) -> int: +from typing import Optional + + +def find_primitive(n: int) -> Optional[int]: for r in range(1, n): li = [] for x in range(n - 1): @@ -8,18 +11,22 @@ def find_primitive(n: int) -> int: li.append(val) else: return r + return None if __name__ == "__main__": q = int(input("Enter a prime number q: ")) a = find_primitive(q) - a_private = int(input("Enter private key of A: ")) - a_public = pow(a, a_private, q) - b_private = int(input("Enter private key of B: ")) - b_public = pow(a, b_private, q) + if a is None: + print(f"Cannot find the primitive for the value: {a!r}") + else: + a_private = int(input("Enter private key of A: ")) + a_public = pow(a, a_private, q) + b_private = int(input("Enter private key of B: ")) + b_public = pow(a, b_private, q) - a_secret = pow(b_public, a_private, q) - b_secret = pow(a_public, b_private, q) + a_secret = pow(b_public, a_private, q) + b_secret = pow(a_public, b_private, q) - print("The key value generated by A is: ", a_secret) - print("The key value generated by B is: ", b_secret) + print("The key value generated by A is: ", a_secret) + print("The key value generated by B is: ", b_secret) diff --git a/ciphers/elgamal_key_generator.py b/ciphers/elgamal_key_generator.py index 52cf69074187..f557b0e0dc91 100644 --- a/ciphers/elgamal_key_generator.py +++ b/ciphers/elgamal_key_generator.py @@ -2,24 +2,18 @@ import random import sys -from . import cryptomath_module as cryptoMath -from . import rabin_miller as rabinMiller +from . import cryptomath_module as cryptomath +from . import rabin_miller min_primitive_root = 3 -def main(): - print("Making key files...") - makeKeyFiles("elgamal", 2048) - print("Key files generation successful") - - # I have written my code naively same as definition of primitive root # however every time I run this program, memory exceeded... # so I used 4.80 Algorithm in # Handbook of Applied Cryptography(CRC Press, ISBN : 0-8493-8523-7, October 1996) # and it seems to run nicely! -def primitiveRoot(p_val: int) -> int: +def primitive_root(p_val: int) -> int: print("Generating primitive root of p") while True: g = random.randrange(3, p_val) @@ -30,20 +24,20 @@ def primitiveRoot(p_val: int) -> int: return g -def generateKey(keySize: int) -> ((int, int, int, int), (int, int)): +def generate_key(key_size: int) -> tuple[tuple[int, int, int, int], tuple[int, int]]: print("Generating prime p...") - p = rabinMiller.generateLargePrime(keySize) # select large prime number. - e_1 = primitiveRoot(p) # one primitive root on modulo p. + p = rabin_miller.generateLargePrime(key_size) # select large prime number. + e_1 = primitive_root(p) # one primitive root on modulo p. d = random.randrange(3, p) # private_key -> have to be greater than 2 for safety. - e_2 = cryptoMath.findModInverse(pow(e_1, d, p), p) + e_2 = cryptomath.find_mod_inverse(pow(e_1, d, p), p) - publicKey = (keySize, e_1, e_2, p) - privateKey = (keySize, d) + public_key = (key_size, e_1, e_2, p) + private_key = (key_size, d) - return publicKey, privateKey + return public_key, private_key -def makeKeyFiles(name: str, keySize: int): +def make_key_files(name: str, keySize: int) -> None: if os.path.exists("%s_pubkey.txt" % name) or os.path.exists( "%s_privkey.txt" % name ): @@ -55,7 +49,7 @@ def makeKeyFiles(name: str, keySize: int): ) sys.exit() - publicKey, privateKey = generateKey(keySize) + publicKey, privateKey = generate_key(keySize) print("\nWriting public key to file %s_pubkey.txt..." % name) with open("%s_pubkey.txt" % name, "w") as fo: fo.write( @@ -67,5 +61,11 @@ def makeKeyFiles(name: str, keySize: int): fo.write("%d,%d" % (privateKey[0], privateKey[1])) +def main() -> None: + print("Making key files...") + make_key_files("elgamal", 2048) + print("Key files generation successful") + + if __name__ == "__main__": main() diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 4344db0056fd..f4ce5a075f46 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -15,6 +15,10 @@ Created by TrapinchO """ +RotorPositionT = tuple[int, int, int] +RotorSelectionT = tuple[str, str, str] + + # used alphabet -------------------------- # from string.ascii_uppercase abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -63,7 +67,9 @@ rotor9 = "KOAEGVDHXPQZMLFTYWJNBRCIUS" -def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: +def _validator( + rotpos: RotorPositionT, rotsel: RotorSelectionT, pb: str +) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]: """ Checks if the values can be used for the 'enigma' function @@ -99,12 +105,12 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: ) # Validates string and returns dict - pb = _plugboard(pb) + pbdict = _plugboard(pb) - return rotpos, rotsel, pb + return rotpos, rotsel, pbdict -def _plugboard(pbstring: str) -> dict: +def _plugboard(pbstring: str) -> dict[str, str]: """ https://en.wikipedia.org/wiki/Enigma_machine#Plugboard @@ -145,17 +151,17 @@ def _plugboard(pbstring: str) -> dict: # Created the dictionary pb = {} - for i in range(0, len(pbstring) - 1, 2): - pb[pbstring[i]] = pbstring[i + 1] - pb[pbstring[i + 1]] = pbstring[i] + for j in range(0, len(pbstring) - 1, 2): + pb[pbstring[j]] = pbstring[j + 1] + pb[pbstring[j + 1]] = pbstring[j] return pb def enigma( text: str, - rotor_position: tuple, - rotor_selection: tuple = (rotor1, rotor2, rotor3), + rotor_position: RotorPositionT, + rotor_selection: RotorSelectionT = (rotor1, rotor2, rotor3), plugb: str = "", ) -> str: """ diff --git a/ciphers/rsa_key_generator.py b/ciphers/rsa_key_generator.py index e456d9d9f6f1..584066d8970f 100644 --- a/ciphers/rsa_key_generator.py +++ b/ciphers/rsa_key_generator.py @@ -1,19 +1,18 @@ import os import random import sys -from typing import Tuple from . import cryptomath_module as cryptoMath from . import rabin_miller as rabinMiller -def main(): +def main() -> None: print("Making key files...") makeKeyFiles("rsa", 1024) print("Key files generation successful.") -def generateKey(keySize: int) -> Tuple[Tuple[int, int], Tuple[int, int]]: +def generateKey(keySize: int) -> tuple[tuple[int, int], tuple[int, int]]: print("Generating prime p...") p = rabinMiller.generateLargePrime(keySize) print("Generating prime q...") @@ -27,14 +26,14 @@ def generateKey(keySize: int) -> Tuple[Tuple[int, int], Tuple[int, int]]: break print("Calculating d that is mod inverse of e...") - d = cryptoMath.findModInverse(e, (p - 1) * (q - 1)) + d = cryptoMath.find_mod_inverse(e, (p - 1) * (q - 1)) publicKey = (n, e) privateKey = (n, d) return (publicKey, privateKey) -def makeKeyFiles(name: int, keySize: int) -> None: +def makeKeyFiles(name: str, keySize: int) -> None: if os.path.exists("%s_pubkey.txt" % (name)) or os.path.exists( "%s_privkey.txt" % (name) ): From 7f4e3308836be62188ab235c088137ba3762d854 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 22 Mar 2021 15:22:26 +0530 Subject: [PATCH 187/195] move-files-and-2-renames (#4285) --- {other => maths}/binary_exponentiation_2.py | 0 .../binary_exponentiation_3.py | 0 {other => maths}/euclidean_gcd.py | 0 .../integration_by_simpson_approx.py | 0 {other => maths}/largest_subarray_sum.py | 0 {other => maths}/max_sum_sliding_window.py | 90 ++++----- {other => maths}/median_of_two_arrays.py | 0 {other => maths}/primelib.py | 0 {other => maths}/triplet_sum.py | 178 +++++++++--------- {other => maths}/two_pointer.py | 0 {other => maths}/two_sum.py | 0 11 files changed, 134 insertions(+), 134 deletions(-) rename {other => maths}/binary_exponentiation_2.py (100%) rename other/binary_exponentiation.py => maths/binary_exponentiation_3.py (100%) rename {other => maths}/euclidean_gcd.py (100%) rename other/integeration_by_simpson_approx.py => maths/integration_by_simpson_approx.py (100%) rename {other => maths}/largest_subarray_sum.py (100%) rename {other => maths}/max_sum_sliding_window.py (96%) rename {other => maths}/median_of_two_arrays.py (100%) rename {other => maths}/primelib.py (100%) rename {other => maths}/triplet_sum.py (96%) rename {other => maths}/two_pointer.py (100%) rename {other => maths}/two_sum.py (100%) diff --git a/other/binary_exponentiation_2.py b/maths/binary_exponentiation_2.py similarity index 100% rename from other/binary_exponentiation_2.py rename to maths/binary_exponentiation_2.py diff --git a/other/binary_exponentiation.py b/maths/binary_exponentiation_3.py similarity index 100% rename from other/binary_exponentiation.py rename to maths/binary_exponentiation_3.py diff --git a/other/euclidean_gcd.py b/maths/euclidean_gcd.py similarity index 100% rename from other/euclidean_gcd.py rename to maths/euclidean_gcd.py diff --git a/other/integeration_by_simpson_approx.py b/maths/integration_by_simpson_approx.py similarity index 100% rename from other/integeration_by_simpson_approx.py rename to maths/integration_by_simpson_approx.py diff --git a/other/largest_subarray_sum.py b/maths/largest_subarray_sum.py similarity index 100% rename from other/largest_subarray_sum.py rename to maths/largest_subarray_sum.py diff --git a/other/max_sum_sliding_window.py b/maths/max_sum_sliding_window.py similarity index 96% rename from other/max_sum_sliding_window.py rename to maths/max_sum_sliding_window.py index 4be7d786f215..593cb5c8bd67 100644 --- a/other/max_sum_sliding_window.py +++ b/maths/max_sum_sliding_window.py @@ -1,45 +1,45 @@ -""" -Given an array of integer elements and an integer 'k', we are required to find the -maximum sum of 'k' consecutive elements in the array. - -Instead of using a nested for loop, in a Brute force approach we will use a technique -called 'Window sliding technique' where the nested loops can be converted to a single -loop to reduce time complexity. -""" -from typing import List - - -def max_sum_in_array(array: List[int], k: int) -> int: - """ - Returns the maximum sum of k consecutive elements - >>> arr = [1, 4, 2, 10, 2, 3, 1, 0, 20] - >>> k = 4 - >>> max_sum_in_array(arr, k) - 24 - >>> k = 10 - >>> max_sum_in_array(arr,k) - Traceback (most recent call last): - ... - ValueError: Invalid Input - >>> arr = [1, 4, 2, 10, 2, 13, 1, 0, 2] - >>> k = 4 - >>> max_sum_in_array(arr, k) - 27 - """ - if len(array) < k or k < 0: - raise ValueError("Invalid Input") - max_sum = current_sum = sum(array[:k]) - for i in range(len(array) - k): - current_sum = current_sum - array[i] + array[i + k] - max_sum = max(max_sum, current_sum) - return max_sum - - -if __name__ == "__main__": - from doctest import testmod - from random import randint - - testmod() - array = [randint(-1000, 1000) for i in range(100)] - k = randint(0, 110) - print(f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array,k)}") +""" +Given an array of integer elements and an integer 'k', we are required to find the +maximum sum of 'k' consecutive elements in the array. + +Instead of using a nested for loop, in a Brute force approach we will use a technique +called 'Window sliding technique' where the nested loops can be converted to a single +loop to reduce time complexity. +""" +from typing import List + + +def max_sum_in_array(array: List[int], k: int) -> int: + """ + Returns the maximum sum of k consecutive elements + >>> arr = [1, 4, 2, 10, 2, 3, 1, 0, 20] + >>> k = 4 + >>> max_sum_in_array(arr, k) + 24 + >>> k = 10 + >>> max_sum_in_array(arr,k) + Traceback (most recent call last): + ... + ValueError: Invalid Input + >>> arr = [1, 4, 2, 10, 2, 13, 1, 0, 2] + >>> k = 4 + >>> max_sum_in_array(arr, k) + 27 + """ + if len(array) < k or k < 0: + raise ValueError("Invalid Input") + max_sum = current_sum = sum(array[:k]) + for i in range(len(array) - k): + current_sum = current_sum - array[i] + array[i + k] + max_sum = max(max_sum, current_sum) + return max_sum + + +if __name__ == "__main__": + from doctest import testmod + from random import randint + + testmod() + array = [randint(-1000, 1000) for i in range(100)] + k = randint(0, 110) + print(f"The maximum sum of {k} consecutive elements is {max_sum_in_array(array,k)}") diff --git a/other/median_of_two_arrays.py b/maths/median_of_two_arrays.py similarity index 100% rename from other/median_of_two_arrays.py rename to maths/median_of_two_arrays.py diff --git a/other/primelib.py b/maths/primelib.py similarity index 100% rename from other/primelib.py rename to maths/primelib.py diff --git a/other/triplet_sum.py b/maths/triplet_sum.py similarity index 96% rename from other/triplet_sum.py rename to maths/triplet_sum.py index 0e78bb52bb72..22fab17d30c2 100644 --- a/other/triplet_sum.py +++ b/maths/triplet_sum.py @@ -1,89 +1,89 @@ -""" -Given an array of integers and another integer target, -we are required to find a triplet from the array such that it's sum is equal to -the target. -""" -from __future__ import annotations - -from itertools import permutations -from random import randint -from timeit import repeat - - -def make_dataset() -> tuple[list[int], int]: - arr = [randint(-1000, 1000) for i in range(10)] - r = randint(-5000, 5000) - return (arr, r) - - -dataset = make_dataset() - - -def triplet_sum1(arr: list[int], target: int) -> tuple[int, int, int]: - """ - Returns a triplet in the array with sum equal to target, - else (0, 0, 0). - >>> triplet_sum1([13, 29, 7, 23, 5], 35) - (5, 7, 23) - >>> triplet_sum1([37, 9, 19, 50, 44], 65) - (9, 19, 37) - >>> arr = [6, 47, 27, 1, 15] - >>> target = 11 - >>> triplet_sum1(arr, target) - (0, 0, 0) - """ - for triplet in permutations(arr, 3): - if sum(triplet) == target: - return tuple(sorted(triplet)) - return (0, 0, 0) - - -def triplet_sum2(arr: list[int], target: int) -> tuple[int, int, int]: - """ - Returns a triplet in the array with sum equal to target, - else (0, 0, 0). - >>> triplet_sum2([13, 29, 7, 23, 5], 35) - (5, 7, 23) - >>> triplet_sum2([37, 9, 19, 50, 44], 65) - (9, 19, 37) - >>> arr = [6, 47, 27, 1, 15] - >>> target = 11 - >>> triplet_sum2(arr, target) - (0, 0, 0) - """ - arr.sort() - n = len(arr) - for i in range(n - 1): - left, right = i + 1, n - 1 - while left < right: - if arr[i] + arr[left] + arr[right] == target: - return (arr[i], arr[left], arr[right]) - elif arr[i] + arr[left] + arr[right] < target: - left += 1 - elif arr[i] + arr[left] + arr[right] > target: - right -= 1 - return (0, 0, 0) - - -def solution_times() -> tuple[float, float]: - setup_code = """ -from __main__ import dataset, triplet_sum1, triplet_sum2 -""" - test_code1 = """ -triplet_sum1(*dataset) -""" - test_code2 = """ -triplet_sum2(*dataset) -""" - times1 = repeat(setup=setup_code, stmt=test_code1, repeat=5, number=10000) - times2 = repeat(setup=setup_code, stmt=test_code2, repeat=5, number=10000) - return (min(times1), min(times2)) - - -if __name__ == "__main__": - from doctest import testmod - - testmod() - times = solution_times() - print(f"The time for naive implementation is {times[0]}.") - print(f"The time for optimized implementation is {times[1]}.") +""" +Given an array of integers and another integer target, +we are required to find a triplet from the array such that it's sum is equal to +the target. +""" +from __future__ import annotations + +from itertools import permutations +from random import randint +from timeit import repeat + + +def make_dataset() -> tuple[list[int], int]: + arr = [randint(-1000, 1000) for i in range(10)] + r = randint(-5000, 5000) + return (arr, r) + + +dataset = make_dataset() + + +def triplet_sum1(arr: list[int], target: int) -> tuple[int, int, int]: + """ + Returns a triplet in the array with sum equal to target, + else (0, 0, 0). + >>> triplet_sum1([13, 29, 7, 23, 5], 35) + (5, 7, 23) + >>> triplet_sum1([37, 9, 19, 50, 44], 65) + (9, 19, 37) + >>> arr = [6, 47, 27, 1, 15] + >>> target = 11 + >>> triplet_sum1(arr, target) + (0, 0, 0) + """ + for triplet in permutations(arr, 3): + if sum(triplet) == target: + return tuple(sorted(triplet)) + return (0, 0, 0) + + +def triplet_sum2(arr: list[int], target: int) -> tuple[int, int, int]: + """ + Returns a triplet in the array with sum equal to target, + else (0, 0, 0). + >>> triplet_sum2([13, 29, 7, 23, 5], 35) + (5, 7, 23) + >>> triplet_sum2([37, 9, 19, 50, 44], 65) + (9, 19, 37) + >>> arr = [6, 47, 27, 1, 15] + >>> target = 11 + >>> triplet_sum2(arr, target) + (0, 0, 0) + """ + arr.sort() + n = len(arr) + for i in range(n - 1): + left, right = i + 1, n - 1 + while left < right: + if arr[i] + arr[left] + arr[right] == target: + return (arr[i], arr[left], arr[right]) + elif arr[i] + arr[left] + arr[right] < target: + left += 1 + elif arr[i] + arr[left] + arr[right] > target: + right -= 1 + return (0, 0, 0) + + +def solution_times() -> tuple[float, float]: + setup_code = """ +from __main__ import dataset, triplet_sum1, triplet_sum2 +""" + test_code1 = """ +triplet_sum1(*dataset) +""" + test_code2 = """ +triplet_sum2(*dataset) +""" + times1 = repeat(setup=setup_code, stmt=test_code1, repeat=5, number=10000) + times2 = repeat(setup=setup_code, stmt=test_code2, repeat=5, number=10000) + return (min(times1), min(times2)) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + times = solution_times() + print(f"The time for naive implementation is {times[0]}.") + print(f"The time for optimized implementation is {times[1]}.") diff --git a/other/two_pointer.py b/maths/two_pointer.py similarity index 100% rename from other/two_pointer.py rename to maths/two_pointer.py diff --git a/other/two_sum.py b/maths/two_sum.py similarity index 100% rename from other/two_sum.py rename to maths/two_sum.py From fe1f7007be7b2b8c3ebd46cd734c41afe7fe945b Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 22 Mar 2021 15:24:04 +0530 Subject: [PATCH 188/195] Move files to various folders (#4286) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move files to cellular_automata * Rename other/davis–putnam–logemann–loveland.py to backtracking/davis–putnam–logemann–loveland.py * Rename other/markov_chain.py to graphs/markov_chain.py * undid rename: need to fix mypy first --- {other => cellular_automata}/game_of_life.py | 0 {other => graphs}/markov_chain.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {other => cellular_automata}/game_of_life.py (100%) rename {other => graphs}/markov_chain.py (100%) diff --git a/other/game_of_life.py b/cellular_automata/game_of_life.py similarity index 100% rename from other/game_of_life.py rename to cellular_automata/game_of_life.py diff --git a/other/markov_chain.py b/graphs/markov_chain.py similarity index 100% rename from other/markov_chain.py rename to graphs/markov_chain.py From f1030ea137f5877ab188fdb4be3868cd7ae3592f Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 22 Mar 2021 16:10:23 +0530 Subject: [PATCH 189/195] Moved "other/anagrams.py" to the string folder (#4289) * move&rename, changed code accordingly * adjusted codespell ignore-list --- .pre-commit-config.yaml | 4 ++-- {other => strings}/anagrams.py | 2 +- other/words => strings/words.txt | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename {other => strings}/anagrams.py (95%) rename other/words => strings/words.txt (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee422e61a03b..b48da86ee57d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,12 +43,12 @@ repos: - id: codespell args: - --ignore-words-list=ans,crate,fo,followings,hist,iff,mater,secant,som,tim - - --skip="./.*,./strings/dictionary.txt,./other/words,./project_euler/problem_022/p022_names.txt" + - --skip="./.*,./strings/dictionary.txt,./strings/words.txt,./project_euler/problem_022/p022_names.txt" - --quiet-level=2 exclude: | (?x)^( strings/dictionary.txt | - other/words | + strings/words.txt | project_euler/problem_022/p022_names.txt )$ - repo: local diff --git a/other/anagrams.py b/strings/anagrams.py similarity index 95% rename from other/anagrams.py rename to strings/anagrams.py index 0be013d5bc47..1a7c675d6719 100644 --- a/other/anagrams.py +++ b/strings/anagrams.py @@ -6,7 +6,7 @@ start_time = time.time() print("creating word list...") path = os.path.split(os.path.realpath(__file__)) -with open(path[0] + "/words") as f: +with open(path[0] + "/words.txt") as f: word_list = sorted(list({word.strip().lower() for word in f})) diff --git a/other/words b/strings/words.txt similarity index 100% rename from other/words rename to strings/words.txt From 911694c58a3871520c2b818049cf432bdc704af9 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 22 Mar 2021 23:54:05 +0530 Subject: [PATCH 190/195] [mypy] fix compression folder (#4290) * Update lempel_ziv.py * Update build.yml * updating DIRECTORY.md * fix doctest in 2_hidden_layers_neural_network.py * one more doctest * simplified tests Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/workflows/build.yml | 1 + DIRECTORY.md | 40 +++++++++---------- compression/lempel_ziv.py | 2 +- .../2_hidden_layers_neural_network.py | 8 ++-- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e8d04126002..c85b82330c67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ jobs: blockchain boolean_algebra cellular_automata + compression computer_vision fractals fuzzy_logic diff --git a/DIRECTORY.md b/DIRECTORY.md index 2f57a9db5769..f5297db05fe1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -46,6 +46,7 @@ ## Cellular Automata * [Conways Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/conways_game_of_life.py) + * [Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/game_of_life.py) * [One Dimensional](https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/one_dimensional.py) ## Ciphers @@ -322,6 +323,7 @@ * [Kahns Algorithm Long](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_long.py) * [Kahns Algorithm Topo](https://github.com/TheAlgorithms/Python/blob/master/graphs/kahns_algorithm_topo.py) * [Karger](https://github.com/TheAlgorithms/Python/blob/master/graphs/karger.py) + * [Markov Chain](https://github.com/TheAlgorithms/Python/blob/master/graphs/markov_chain.py) * [Minimum Spanning Tree Boruvka](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_boruvka.py) * [Minimum Spanning Tree Kruskal](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal.py) * [Minimum Spanning Tree Kruskal2](https://github.com/TheAlgorithms/Python/blob/master/graphs/minimum_spanning_tree_kruskal2.py) @@ -407,6 +409,8 @@ * [Basic Maths](https://github.com/TheAlgorithms/Python/blob/master/maths/basic_maths.py) * [Binary Exp Mod](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exp_mod.py) * [Binary Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exponentiation.py) + * [Binary Exponentiation 2](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exponentiation_2.py) + * [Binary Exponentiation 3](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exponentiation_3.py) * [Binomial Coefficient](https://github.com/TheAlgorithms/Python/blob/master/maths/binomial_coefficient.py) * [Binomial Distribution](https://github.com/TheAlgorithms/Python/blob/master/maths/binomial_distribution.py) * [Bisection](https://github.com/TheAlgorithms/Python/blob/master/maths/bisection.py) @@ -417,8 +421,9 @@ * [Decimal Isolate](https://github.com/TheAlgorithms/Python/blob/master/maths/decimal_isolate.py) * [Entropy](https://github.com/TheAlgorithms/Python/blob/master/maths/entropy.py) * [Euclidean Distance](https://github.com/TheAlgorithms/Python/blob/master/maths/euclidean_distance.py) + * [Euclidean Gcd](https://github.com/TheAlgorithms/Python/blob/master/maths/euclidean_gcd.py) + * [Euler Method](https://github.com/TheAlgorithms/Python/blob/master/maths/euler_method.py) * [Eulers Totient](https://github.com/TheAlgorithms/Python/blob/master/maths/eulers_totient.py) - * [Explicit Euler](https://github.com/TheAlgorithms/Python/blob/master/maths/explicit_euler.py) * [Extended Euclidean Algorithm](https://github.com/TheAlgorithms/Python/blob/master/maths/extended_euclidean_algorithm.py) * [Factorial Iterative](https://github.com/TheAlgorithms/Python/blob/master/maths/factorial_iterative.py) * [Factorial Python](https://github.com/TheAlgorithms/Python/blob/master/maths/factorial_python.py) @@ -437,6 +442,7 @@ * [Greatest Common Divisor](https://github.com/TheAlgorithms/Python/blob/master/maths/greatest_common_divisor.py) * [Greedy Coin Change](https://github.com/TheAlgorithms/Python/blob/master/maths/greedy_coin_change.py) * [Hardy Ramanujanalgo](https://github.com/TheAlgorithms/Python/blob/master/maths/hardy_ramanujanalgo.py) + * [Integration By Simpson Approx](https://github.com/TheAlgorithms/Python/blob/master/maths/integration_by_simpson_approx.py) * [Is Square Free](https://github.com/TheAlgorithms/Python/blob/master/maths/is_square_free.py) * [Jaccard Similarity](https://github.com/TheAlgorithms/Python/blob/master/maths/jaccard_similarity.py) * [Kadanes](https://github.com/TheAlgorithms/Python/blob/master/maths/kadanes.py) @@ -444,11 +450,14 @@ * [Krishnamurthy Number](https://github.com/TheAlgorithms/Python/blob/master/maths/krishnamurthy_number.py) * [Kth Lexicographic Permutation](https://github.com/TheAlgorithms/Python/blob/master/maths/kth_lexicographic_permutation.py) * [Largest Of Very Large Numbers](https://github.com/TheAlgorithms/Python/blob/master/maths/largest_of_very_large_numbers.py) + * [Largest Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/maths/largest_subarray_sum.py) * [Least Common Multiple](https://github.com/TheAlgorithms/Python/blob/master/maths/least_common_multiple.py) * [Line Length](https://github.com/TheAlgorithms/Python/blob/master/maths/line_length.py) * [Lucas Lehmer Primality Test](https://github.com/TheAlgorithms/Python/blob/master/maths/lucas_lehmer_primality_test.py) * [Lucas Series](https://github.com/TheAlgorithms/Python/blob/master/maths/lucas_series.py) * [Matrix Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/maths/matrix_exponentiation.py) + * [Max Sum Sliding Window](https://github.com/TheAlgorithms/Python/blob/master/maths/max_sum_sliding_window.py) + * [Median Of Two Arrays](https://github.com/TheAlgorithms/Python/blob/master/maths/median_of_two_arrays.py) * [Miller Rabin](https://github.com/TheAlgorithms/Python/blob/master/maths/miller_rabin.py) * [Mobius Function](https://github.com/TheAlgorithms/Python/blob/master/maths/mobius_function.py) * [Modular Exponential](https://github.com/TheAlgorithms/Python/blob/master/maths/modular_exponential.py) @@ -467,6 +476,7 @@ * [Prime Factors](https://github.com/TheAlgorithms/Python/blob/master/maths/prime_factors.py) * [Prime Numbers](https://github.com/TheAlgorithms/Python/blob/master/maths/prime_numbers.py) * [Prime Sieve Eratosthenes](https://github.com/TheAlgorithms/Python/blob/master/maths/prime_sieve_eratosthenes.py) + * [Primelib](https://github.com/TheAlgorithms/Python/blob/master/maths/primelib.py) * [Pythagoras](https://github.com/TheAlgorithms/Python/blob/master/maths/pythagoras.py) * [Qr Decomposition](https://github.com/TheAlgorithms/Python/blob/master/maths/qr_decomposition.py) * [Quadratic Equations Complex Numbers](https://github.com/TheAlgorithms/Python/blob/master/maths/quadratic_equations_complex_numbers.py) @@ -491,6 +501,9 @@ * [Sum Of Geometric Progression](https://github.com/TheAlgorithms/Python/blob/master/maths/sum_of_geometric_progression.py) * [Test Prime Check](https://github.com/TheAlgorithms/Python/blob/master/maths/test_prime_check.py) * [Trapezoidal Rule](https://github.com/TheAlgorithms/Python/blob/master/maths/trapezoidal_rule.py) + * [Triplet Sum](https://github.com/TheAlgorithms/Python/blob/master/maths/triplet_sum.py) + * [Two Pointer](https://github.com/TheAlgorithms/Python/blob/master/maths/two_pointer.py) + * [Two Sum](https://github.com/TheAlgorithms/Python/blob/master/maths/two_sum.py) * [Ugly Numbers](https://github.com/TheAlgorithms/Python/blob/master/maths/ugly_numbers.py) * [Volume](https://github.com/TheAlgorithms/Python/blob/master/maths/volume.py) * [Zellers Congruence](https://github.com/TheAlgorithms/Python/blob/master/maths/zellers_congruence.py) @@ -520,42 +533,23 @@ ## Other * [Activity Selection](https://github.com/TheAlgorithms/Python/blob/master/other/activity_selection.py) - * [Anagrams](https://github.com/TheAlgorithms/Python/blob/master/other/anagrams.py) - * [Autocomplete Using Trie](https://github.com/TheAlgorithms/Python/blob/master/other/autocomplete_using_trie.py) - * [Binary Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/other/binary_exponentiation.py) - * [Binary Exponentiation 2](https://github.com/TheAlgorithms/Python/blob/master/other/binary_exponentiation_2.py) * [Davis–Putnam–Logemann–Loveland](https://github.com/TheAlgorithms/Python/blob/master/other/davis–putnam–logemann–loveland.py) - * [Detecting English Programmatically](https://github.com/TheAlgorithms/Python/blob/master/other/detecting_english_programmatically.py) * [Dijkstra Bankers Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/dijkstra_bankers_algorithm.py) * [Doomsday](https://github.com/TheAlgorithms/Python/blob/master/other/doomsday.py) - * [Euclidean Gcd](https://github.com/TheAlgorithms/Python/blob/master/other/euclidean_gcd.py) * [Fischer Yates Shuffle](https://github.com/TheAlgorithms/Python/blob/master/other/fischer_yates_shuffle.py) - * [Frequency Finder](https://github.com/TheAlgorithms/Python/blob/master/other/frequency_finder.py) - * [Game Of Life](https://github.com/TheAlgorithms/Python/blob/master/other/game_of_life.py) * [Gauss Easter](https://github.com/TheAlgorithms/Python/blob/master/other/gauss_easter.py) * [Graham Scan](https://github.com/TheAlgorithms/Python/blob/master/other/graham_scan.py) * [Greedy](https://github.com/TheAlgorithms/Python/blob/master/other/greedy.py) - * [Integeration By Simpson Approx](https://github.com/TheAlgorithms/Python/blob/master/other/integeration_by_simpson_approx.py) - * [Largest Subarray Sum](https://github.com/TheAlgorithms/Python/blob/master/other/largest_subarray_sum.py) * [Least Recently Used](https://github.com/TheAlgorithms/Python/blob/master/other/least_recently_used.py) * [Lfu Cache](https://github.com/TheAlgorithms/Python/blob/master/other/lfu_cache.py) * [Linear Congruential Generator](https://github.com/TheAlgorithms/Python/blob/master/other/linear_congruential_generator.py) * [Lru Cache](https://github.com/TheAlgorithms/Python/blob/master/other/lru_cache.py) * [Magicdiamondpattern](https://github.com/TheAlgorithms/Python/blob/master/other/magicdiamondpattern.py) - * [Markov Chain](https://github.com/TheAlgorithms/Python/blob/master/other/markov_chain.py) - * [Max Sum Sliding Window](https://github.com/TheAlgorithms/Python/blob/master/other/max_sum_sliding_window.py) - * [Median Of Two Arrays](https://github.com/TheAlgorithms/Python/blob/master/other/median_of_two_arrays.py) * [Nested Brackets](https://github.com/TheAlgorithms/Python/blob/master/other/nested_brackets.py) - * [Palindrome](https://github.com/TheAlgorithms/Python/blob/master/other/palindrome.py) * [Password Generator](https://github.com/TheAlgorithms/Python/blob/master/other/password_generator.py) - * [Primelib](https://github.com/TheAlgorithms/Python/blob/master/other/primelib.py) * [Scoring Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/scoring_algorithm.py) * [Sdes](https://github.com/TheAlgorithms/Python/blob/master/other/sdes.py) * [Tower Of Hanoi](https://github.com/TheAlgorithms/Python/blob/master/other/tower_of_hanoi.py) - * [Triplet Sum](https://github.com/TheAlgorithms/Python/blob/master/other/triplet_sum.py) - * [Two Pointer](https://github.com/TheAlgorithms/Python/blob/master/other/two_pointer.py) - * [Two Sum](https://github.com/TheAlgorithms/Python/blob/master/other/two_sum.py) - * [Word Patterns](https://github.com/TheAlgorithms/Python/blob/master/other/word_patterns.py) ## Project Euler * Problem 001 @@ -885,11 +879,15 @@ ## Strings * [Aho Corasick](https://github.com/TheAlgorithms/Python/blob/master/strings/aho_corasick.py) + * [Anagrams](https://github.com/TheAlgorithms/Python/blob/master/strings/anagrams.py) + * [Autocomplete Using Trie](https://github.com/TheAlgorithms/Python/blob/master/strings/autocomplete_using_trie.py) * [Boyer Moore Search](https://github.com/TheAlgorithms/Python/blob/master/strings/boyer_moore_search.py) * [Can String Be Rearranged As Palindrome](https://github.com/TheAlgorithms/Python/blob/master/strings/can_string_be_rearranged_as_palindrome.py) * [Capitalize](https://github.com/TheAlgorithms/Python/blob/master/strings/capitalize.py) * [Check Anagrams](https://github.com/TheAlgorithms/Python/blob/master/strings/check_anagrams.py) * [Check Pangram](https://github.com/TheAlgorithms/Python/blob/master/strings/check_pangram.py) + * [Detecting English Programmatically](https://github.com/TheAlgorithms/Python/blob/master/strings/detecting_english_programmatically.py) + * [Frequency Finder](https://github.com/TheAlgorithms/Python/blob/master/strings/frequency_finder.py) * [Is Palindrome](https://github.com/TheAlgorithms/Python/blob/master/strings/is_palindrome.py) * [Jaro Winkler](https://github.com/TheAlgorithms/Python/blob/master/strings/jaro_winkler.py) * [Knuth Morris Pratt](https://github.com/TheAlgorithms/Python/blob/master/strings/knuth_morris_pratt.py) @@ -898,6 +896,7 @@ * [Manacher](https://github.com/TheAlgorithms/Python/blob/master/strings/manacher.py) * [Min Cost String Conversion](https://github.com/TheAlgorithms/Python/blob/master/strings/min_cost_string_conversion.py) * [Naive String Search](https://github.com/TheAlgorithms/Python/blob/master/strings/naive_string_search.py) + * [Palindrome](https://github.com/TheAlgorithms/Python/blob/master/strings/palindrome.py) * [Prefix Function](https://github.com/TheAlgorithms/Python/blob/master/strings/prefix_function.py) * [Rabin Karp](https://github.com/TheAlgorithms/Python/blob/master/strings/rabin_karp.py) * [Remove Duplicate](https://github.com/TheAlgorithms/Python/blob/master/strings/remove_duplicate.py) @@ -907,6 +906,7 @@ * [Swap Case](https://github.com/TheAlgorithms/Python/blob/master/strings/swap_case.py) * [Upper](https://github.com/TheAlgorithms/Python/blob/master/strings/upper.py) * [Word Occurrence](https://github.com/TheAlgorithms/Python/blob/master/strings/word_occurrence.py) + * [Word Patterns](https://github.com/TheAlgorithms/Python/blob/master/strings/word_patterns.py) * [Z Function](https://github.com/TheAlgorithms/Python/blob/master/strings/z_function.py) ## Traversals diff --git a/compression/lempel_ziv.py b/compression/lempel_ziv.py index 2d0601b27b34..6743dc42d56e 100644 --- a/compression/lempel_ziv.py +++ b/compression/lempel_ziv.py @@ -26,7 +26,7 @@ def read_file_binary(file_path: str) -> str: def add_key_to_lexicon( - lexicon: dict, curr_string: str, index: int, last_match_id: int + lexicon: dict, curr_string: str, index: int, last_match_id: str ) -> None: """ Adds new strings (curr_string + "0", curr_string + "1") to the lexicon diff --git a/neural_network/2_hidden_layers_neural_network.py b/neural_network/2_hidden_layers_neural_network.py index baa4316200d9..1cf78ec4c7c0 100644 --- a/neural_network/2_hidden_layers_neural_network.py +++ b/neural_network/2_hidden_layers_neural_network.py @@ -196,8 +196,8 @@ def predict(self, input: numpy.ndarray) -> int: >>> output_val = numpy.array(([0], [1], [1]), dtype=float) >>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) >>> nn.train(output_val, 1000, False) - >>> nn.predict([0,1,0]) - 1 + >>> nn.predict([0,1,0]) in (0, 1) + True """ # Input values for which the predictions are to be made. @@ -260,8 +260,8 @@ def example() -> int: In this example the output is divided into 2 classes i.e. binary classification, the two classes are represented by '0' and '1'. - >>> example() - 1 + >>> example() in (0, 1) + True """ # Input values. input = numpy.array( From b2bec7e0a6bd61b7c45a46951faf3f4784c179aa Mon Sep 17 00:00:00 2001 From: algobytewise Date: Tue, 23 Mar 2021 21:21:50 +0530 Subject: [PATCH 191/195] [mypy] fix small folders (#4292) * add final else-statement * fix file_transfer * fix quantum folder * fix divide_and_conquer-folder * Update build.yml * updating DIRECTORY.md * Update ripple_adder_classic.py * Update .github/workflows/build.yml * removed imports from typing * removed conversion to string * Revert "removed conversion to string" This reverts commit 2f7c4731d103f24c73fb98c9a6898525998774c5. * implemented suggested changes * Update receive_file.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- .github/workflows/build.yml | 7 ++++++- divide_and_conquer/max_difference_pair.py | 5 +---- divide_and_conquer/strassen_matrix_multiplication.py | 2 +- electronics/electric_power.py | 2 ++ electronics/ohms_law.py | 2 ++ file_transfer/receive_file.py | 2 +- file_transfer/send_file.py | 2 +- quantum/ripple_adder_classic.py | 2 +- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c85b82330c67..74b885b90343 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,13 +30,18 @@ jobs: cellular_automata compression computer_vision + divide_and_conquer + electronics + file_transfer fractals fuzzy_logic genetic_algorithm geodesy knapsack networking_flow - scheduling sorts + quantum + scheduling + sorts - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} diff --git a/divide_and_conquer/max_difference_pair.py b/divide_and_conquer/max_difference_pair.py index b976aca43137..ffc4b76a7154 100644 --- a/divide_and_conquer/max_difference_pair.py +++ b/divide_and_conquer/max_difference_pair.py @@ -1,7 +1,4 @@ -from typing import List - - -def max_difference(a: List[int]) -> (int, int): +def max_difference(a: list[int]) -> tuple[int, int]: """ We are given an array A[1..n] of integers, n >= 1. We want to find a pair of indices (i, j) such that diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py index 29a174daebf9..ca10e04abcbc 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -121,7 +121,7 @@ def strassen(matrix1: list, matrix2: list) -> list: dimension2 = matrix_dimensions(matrix2) if dimension1[0] == dimension1[1] and dimension2[0] == dimension2[1]: - return matrix1, matrix2 + return [matrix1, matrix2] maximum = max(max(dimension1), max(dimension2)) maxim = int(math.pow(2, math.ceil(math.log2(maximum)))) diff --git a/electronics/electric_power.py b/electronics/electric_power.py index 8f0293bd2d10..e4e685bbd0f0 100644 --- a/electronics/electric_power.py +++ b/electronics/electric_power.py @@ -42,6 +42,8 @@ def electric_power(voltage: float, current: float, power: float) -> Tuple: return result("current", power / voltage) elif power == 0: return result("power", float(round(abs(voltage * current), 2))) + else: + raise ValueError("Exactly one argument must be 0") if __name__ == "__main__": diff --git a/electronics/ohms_law.py b/electronics/ohms_law.py index c53619a10935..41bffa9f87c8 100644 --- a/electronics/ohms_law.py +++ b/electronics/ohms_law.py @@ -32,6 +32,8 @@ def ohms_law(voltage: float, current: float, resistance: float) -> Dict[str, flo return {"current": voltage / resistance} elif resistance == 0: return {"resistance": voltage / current} + else: + raise ValueError("Exactly one argument must be 0") if __name__ == "__main__": diff --git a/file_transfer/receive_file.py b/file_transfer/receive_file.py index cfba6ed88484..37a503036dc2 100644 --- a/file_transfer/receive_file.py +++ b/file_transfer/receive_file.py @@ -13,7 +13,7 @@ print("Receiving data...") while True: data = sock.recv(1024) - print(f"data={data}") + print(f"{data = }") if not data: break out_file.write(data) # Write data to a file diff --git a/file_transfer/send_file.py b/file_transfer/send_file.py index 5b53471dfb50..1c56e48f47a1 100644 --- a/file_transfer/send_file.py +++ b/file_transfer/send_file.py @@ -13,7 +13,7 @@ def send_file(filename: str = "mytext.txt", testing: bool = False) -> None: conn, addr = sock.accept() # Establish connection with client. print(f"Got connection from {addr}") data = conn.recv(1024) - print(f"Server received {data}") + print(f"Server received: {data = }") with open(filename, "rb") as in_file: data = in_file.read(1024) diff --git a/quantum/ripple_adder_classic.py b/quantum/ripple_adder_classic.py index f5b0a980c8e2..dc0c2103b2e5 100644 --- a/quantum/ripple_adder_classic.py +++ b/quantum/ripple_adder_classic.py @@ -6,7 +6,7 @@ from qiskit.providers import BaseBackend -def store_two_classics(val1: int, val2: int) -> (QuantumCircuit, str, str): +def store_two_classics(val1: int, val2: int) -> tuple[QuantumCircuit, str, str]: """ Generates a Quantum Circuit which stores two classical integers Returns the circuit and binary representation of the integers From 9b1aaa8768e989d35cf7ce146f12a7aea6e6db27 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Fri, 26 Mar 2021 16:51:16 +0530 Subject: [PATCH 192/195] [mypy] fix small folders 2 (#4293) * Update perceptron.py * Update binary_tree_traversals.py * fix machine_learning * Update build.yml * Update perceptron.py * Update machine_learning/forecasting/run.py Co-authored-by: Christian Clauss --- .github/workflows/build.yml | 3 +++ machine_learning/forecasting/run.py | 3 +-- machine_learning/k_means_clust.py | 2 +- machine_learning/word_frequency_functions.py | 2 +- neural_network/perceptron.py | 21 +++++++++++++------- traversals/binary_tree_traversals.py | 4 ++-- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74b885b90343..87cc8b67341d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,10 +38,13 @@ jobs: genetic_algorithm geodesy knapsack + machine_learning networking_flow + neural_network quantum scheduling sorts + traversals - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} diff --git a/machine_learning/forecasting/run.py b/machine_learning/forecasting/run.py index 0e11f958825f..b11a230129eb 100644 --- a/machine_learning/forecasting/run.py +++ b/machine_learning/forecasting/run.py @@ -29,8 +29,7 @@ def linear_regression_prediction( >>> abs(n - 5.0) < 1e-6 # Checking precision because of floating point errors True """ - x = [[1, item, train_mtch[i]] for i, item in enumerate(train_dt)] - x = np.array(x) + x = np.array([[1, item, train_mtch[i]] for i, item in enumerate(train_dt)]) y = np.array(train_usr) beta = np.dot(np.dot(np.linalg.inv(np.dot(x.transpose(), x)), x.transpose()), y) return abs(beta[0] + test_dt[0] * beta[1] + test_mtch[0] + beta[2]) diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index f155d4845f41..c45be8a4c064 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -200,7 +200,7 @@ def kmeans( def ReportGenerator( - df: pd.DataFrame, ClusteringVariables: np.array, FillMissingReport=None + df: pd.DataFrame, ClusteringVariables: np.ndarray, FillMissingReport=None ) -> pd.DataFrame: """ Function generates easy-erading clustering report. It takes 2 arguments as an input: diff --git a/machine_learning/word_frequency_functions.py b/machine_learning/word_frequency_functions.py index 9cf7b694c6be..3e8faf39cf07 100644 --- a/machine_learning/word_frequency_functions.py +++ b/machine_learning/word_frequency_functions.py @@ -61,7 +61,7 @@ def term_frequency(term: str, document: str) -> int: return len([word for word in tokenize_document if word.lower() == term.lower()]) -def document_frequency(term: str, corpus: str) -> int: +def document_frequency(term: str, corpus: str) -> tuple[int, int]: """ Calculate the number of documents in a corpus that contain a given term diff --git a/neural_network/perceptron.py b/neural_network/perceptron.py index 23b409b227c4..063be5ea554c 100644 --- a/neural_network/perceptron.py +++ b/neural_network/perceptron.py @@ -11,7 +11,14 @@ class Perceptron: - def __init__(self, sample, target, learning_rate=0.01, epoch_number=1000, bias=-1): + def __init__( + self, + sample: list[list[float]], + target: list[int], + learning_rate: float = 0.01, + epoch_number: int = 1000, + bias: float = -1, + ) -> None: """ Initializes a Perceptron network for oil analysis :param sample: sample dataset of 3 parameters with shape [30,3] @@ -46,7 +53,7 @@ def __init__(self, sample, target, learning_rate=0.01, epoch_number=1000, bias=- self.bias = bias self.number_sample = len(sample) self.col_sample = len(sample[0]) # number of columns in dataset - self.weight = [] + self.weight: list = [] def training(self) -> None: """ @@ -94,7 +101,7 @@ def training(self) -> None: # if epoch_count > self.epoch_number or not error: break - def sort(self, sample) -> None: + def sort(self, sample: list[float]) -> None: """ :param sample: example row to classify as P1 or P2 :return: None @@ -221,11 +228,11 @@ def sign(self, u: float) -> int: print("Finished training perceptron") print("Enter values to predict or q to exit") while True: - sample = [] + sample: list = [] for i in range(len(samples[0])): - observation = input("value: ").strip() - if observation == "q": + user_input = input("value: ").strip() + if user_input == "q": break - observation = float(observation) + observation = float(user_input) sample.insert(i, observation) network.sort(sample) diff --git a/traversals/binary_tree_traversals.py b/traversals/binary_tree_traversals.py index cb471ba55bac..f919a2962354 100644 --- a/traversals/binary_tree_traversals.py +++ b/traversals/binary_tree_traversals.py @@ -188,7 +188,7 @@ def pre_order_iter(node: TreeNode) -> None: """ if not isinstance(node, TreeNode) or not node: return - stack: List[TreeNode] = [] + stack: list[TreeNode] = [] n = node while n or stack: while n: # start from root node, find its left child @@ -218,7 +218,7 @@ def in_order_iter(node: TreeNode) -> None: """ if not isinstance(node, TreeNode) or not node: return - stack: List[TreeNode] = [] + stack: list[TreeNode] = [] n = node while n or stack: while n: From 0edaa83c42ffe427a3b69d8581d7178c8802c043 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sat, 27 Mar 2021 14:48:48 +0530 Subject: [PATCH 193/195] Move: traversals/binary_tree_traversals.py --> searches/binary_tree_traversal.py (#4295) * Rename traversals/binary_tree_traversals.py to searches/binary_tree_traversal.py * updating DIRECTORY.md * Delete traversals directory * Update build.yml Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- .github/workflows/build.yml | 1 - DIRECTORY.md | 4 +--- .../binary_tree_traversal.py | 0 traversals/__init__.py | 0 4 files changed, 1 insertion(+), 4 deletions(-) rename traversals/binary_tree_traversals.py => searches/binary_tree_traversal.py (100%) delete mode 100644 traversals/__init__.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87cc8b67341d..7273119302e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,6 @@ jobs: quantum scheduling sorts - traversals - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} diff --git a/DIRECTORY.md b/DIRECTORY.md index f5297db05fe1..42a6c49c735f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -817,6 +817,7 @@ ## Searches * [Binary Search](https://github.com/TheAlgorithms/Python/blob/master/searches/binary_search.py) + * [Binary Tree Traversal](https://github.com/TheAlgorithms/Python/blob/master/searches/binary_tree_traversal.py) * [Double Linear Search](https://github.com/TheAlgorithms/Python/blob/master/searches/double_linear_search.py) * [Double Linear Search Recursion](https://github.com/TheAlgorithms/Python/blob/master/searches/double_linear_search_recursion.py) * [Fibonacci Search](https://github.com/TheAlgorithms/Python/blob/master/searches/fibonacci_search.py) @@ -909,9 +910,6 @@ * [Word Patterns](https://github.com/TheAlgorithms/Python/blob/master/strings/word_patterns.py) * [Z Function](https://github.com/TheAlgorithms/Python/blob/master/strings/z_function.py) -## Traversals - * [Binary Tree Traversals](https://github.com/TheAlgorithms/Python/blob/master/traversals/binary_tree_traversals.py) - ## Web Programming * [Co2 Emission](https://github.com/TheAlgorithms/Python/blob/master/web_programming/co2_emission.py) * [Covid Stats Via Xpath](https://github.com/TheAlgorithms/Python/blob/master/web_programming/covid_stats_via_xpath.py) diff --git a/traversals/binary_tree_traversals.py b/searches/binary_tree_traversal.py similarity index 100% rename from traversals/binary_tree_traversals.py rename to searches/binary_tree_traversal.py diff --git a/traversals/__init__.py b/traversals/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 From 6530b103a88cb794f1d8d550678635372d586b21 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 31 Mar 2021 05:02:25 +0200 Subject: [PATCH 194/195] mypy: Use a --exclude list (#4296) * mypy: Use a --exclude list * Graphics works on my machine * A few more... * A few more... * Update build.yml --- .github/workflows/build.yml | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7273119302e2..f544c02b1c35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,29 +21,9 @@ jobs: run: | python -m pip install --upgrade pip setuptools six wheel python -m pip install mypy pytest-cov -r requirements.txt - # FIXME: #4052 fix mypy errors in other directories and add them here + # FIXME: #4052 fix mypy errors in the exclude directories and remove them below - run: mypy --ignore-missing-imports - backtracking - bit_manipulation - blockchain - boolean_algebra - cellular_automata - compression - computer_vision - divide_and_conquer - electronics - file_transfer - fractals - fuzzy_logic - genetic_algorithm - geodesy - knapsack - machine_learning - networking_flow - neural_network - quantum - scheduling - sorts + --exclude '(arithmetic_analysis|ciphers|conversions|data_structures|digital_image_processing|dynamic_programming|graphs|hashes|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings|web_programming*)/$' . - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} From 700820ec5cd9411e38812de636092d2adc4f6792 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 31 Mar 2021 05:18:07 +0200 Subject: [PATCH 195/195] [mypy] Fix web_programming directory (#4297) * Update world_covid19_stats.py * Delete monkeytype_config.py * updating DIRECTORY.md * Apply pyannotate suggestions to emails_from_url.py * mypy web_programming/emails_from_url.py * super().__init__() * mypy --ignore-missing-imports web_programming/emails_from_url.py * Update emails_from_url.py * self.urls: list[str] = [] * mypy: Fix web_programming directory Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Dhruv Manilawala --- .github/workflows/build.yml | 2 +- web_programming/currency_converter.py | 2 +- web_programming/emails_from_url.py | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f544c02b1c35..76c6357fe0ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: python -m pip install mypy pytest-cov -r requirements.txt # FIXME: #4052 fix mypy errors in the exclude directories and remove them below - run: mypy --ignore-missing-imports - --exclude '(arithmetic_analysis|ciphers|conversions|data_structures|digital_image_processing|dynamic_programming|graphs|hashes|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings|web_programming*)/$' . + --exclude '(arithmetic_analysis|ciphers|conversions|data_structures|digital_image_processing|dynamic_programming|graphs|hashes|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings*)/$' . - name: Run tests run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. . - if: ${{ success() }} diff --git a/web_programming/currency_converter.py b/web_programming/currency_converter.py index 6aed2a5578a5..447595b0b646 100644 --- a/web_programming/currency_converter.py +++ b/web_programming/currency_converter.py @@ -9,7 +9,7 @@ URL_BASE = "https://www.amdoren.com/api/currency.php" TESTING = os.getenv("CI", False) -API_KEY = os.getenv("AMDOREN_API_KEY") +API_KEY = os.getenv("AMDOREN_API_KEY", "") if not API_KEY and not TESTING: raise KeyError("Please put your API key in an environment variable.") diff --git a/web_programming/emails_from_url.py b/web_programming/emails_from_url.py index 01dee274f015..0571ac3313a3 100644 --- a/web_programming/emails_from_url.py +++ b/web_programming/emails_from_url.py @@ -8,18 +8,19 @@ import re from html.parser import HTMLParser +from typing import Optional from urllib import parse import requests class Parser(HTMLParser): - def __init__(self, domain: str): - HTMLParser.__init__(self) - self.data = [] + def __init__(self, domain: str) -> None: + super().__init__() + self.urls: list[str] = [] self.domain = domain - def handle_starttag(self, tag: str, attrs: str) -> None: + def handle_starttag(self, tag: str, attrs: list[tuple[str, Optional[str]]]) -> None: """ This function parse html to take takes url from tags """ @@ -29,10 +30,10 @@ def handle_starttag(self, tag: str, attrs: str) -> None: for name, value in attrs: # If href is defined, and not empty nor # print it. if name == "href" and value != "#" and value != "": - # If not already in data. - if value not in self.data: + # If not already in urls. + if value not in self.urls: url = parse.urljoin(self.domain, value) - self.data.append(url) + self.urls.append(url) # Get main domain name (example.com) @@ -59,7 +60,7 @@ def get_sub_domain_name(url: str) -> str: return parse.urlparse(url).netloc -def emails_from_url(/service/url: str = "https://github.com/") -> list: +def emails_from_url(/service/url: str = "https://github.com/") -> list[str]: """ This function takes url and return all valid urls """ @@ -78,7 +79,7 @@ def emails_from_url(/service/url: str = "https://github.com/") -> list: # Get links and loop through valid_emails = set() - for link in parser.data: + for link in parser.urls: # open URL. # read = requests.get(link) try: