From 2df4a7e3f599d7fa5fe9af30bf66fcbccaf522c7 Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Sat, 22 Apr 2023 12:20:23 +0200 Subject: [PATCH 01/15] Add solution to project_euler/problem_060 --- project_euler/problem_060/sol1.py | 191 ++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 project_euler/problem_060/sol1.py diff --git a/project_euler/problem_060/sol1.py b/project_euler/problem_060/sol1.py new file mode 100644 index 000000000000..4e5dc2fe60d7 --- /dev/null +++ b/project_euler/problem_060/sol1.py @@ -0,0 +1,191 @@ +""" +Project Euler Problem 60: https://projecteuler.net/problem=60 + +The primes 3, 7, 109, and 673, are quite remarkable. By taking any two primes and +concatenating them in any order the result will always be prime. For example, +taking 7 and 109, both 7109 and 1097 are prime. The sum of these four primes, 792, +represents the lowest sum for a set of four primes with this property. + +Find the lowest sum for a set of five primes for which any two primes concatenate to +produce another prime. + +On the algorithm: +- Generate PRIMES_SIZE primes +- Iterate through the primes, starting from the smallest available. On the top-level of +the recursion, generate a list of primes that can be concatenated to the current prime. +This list will be used for the next recursive calls. +- Find all matches for that prime and store them in a list that will be used for the +next recursive calls +- Test matches for the next recursive calls, register the smallest sum found +- Concatenation is done with arithmetic operations. String processing is generally +slower. +""" +import collections.abc +import math +from time import time + +PRIMES_SIZE = 1.2e3 +COUNTER = False + + +def get_order(n: int) -> int: + """ + Get the order of a number n, in order to facilitate concatenation. + get_order(n) = x -> 10^x <= n < 10^(x+1) + + >>> get_order(1) + 0 + >>> get_order(10) + 1 + >>> get_order(12345) + 4 + """ + + return int(math.log(n, 10)) + + +def test_concatenate(num1: int, num2: int) -> bool: + """ + Test if two numbers concatenate to form a prime, for both possible arrangements. + Use arithmetic operations to concatenate. + + >> test_concatenate(3, 7) + True + >>> test_concatenate(2, 10) + False + >>> test_concatenate(673, 109) + True + """ + + if not is_prime(num1 * 10 ** (get_order(num2) + 1) + num2): + return False + + if not is_prime(num2 * 10 ** (get_order(num1) + 1) + num1): + return False + + return True + + +def is_prime(n) -> bool: + """ + Simple prime test + + >>> is_prime(2) + True + >>> is_prime(100000007) + True + >>> is_prime(10000007) + False + """ + for divider in range(2, int(n**0.5) + 1): + if int(n) % divider == 0: + return False + return True + + +def prime_generator() -> collections.abc.Iterator[int]: + """ + Custom prime generator for primes used in this problem + Skip 2 and 5: no primes end with 2 or 5 except for 2 and 5, so they're not useful + for this problem + + >>> [[next(x) for _ in range(5)] for x in [(prime_generator())]][0] + [3, 7, 11, 13, 17] + """ + + local_output: int = 3 + yield local_output + local_output = 5 + + while True: + local_output += 2 + if is_prime(local_output): + yield local_output + + +def solution_helper( + depth: int, start_idx: int, to_test: list[int], matches: list[int] +) -> list[int]: + """ + Recursive helper function for solution(), search for more primes from + matches[start_idx:] that can be concatenated to all primes from to_test until + recursion reaches depth 0. Return the list of primes if a solution is found, False + otherwise. + + >>> solution_helper(depth=3, start_idx=1, to_test=[3],matches=[3, 7, 100, 109, 673]) + [3, 7, 109, 673] + >>> solution_helper(depth=2, start_idx=0, to_test=[7], matches=[10, 20, 30, 40, 50]) + [] + """ + + if depth == 0: + return to_test + + for i in range(start_idx, len(matches)): + # Test all previous matches: + passes = True + for j in range(len(to_test)): + if not test_concatenate(matches[i], to_test[j]): + passes = False + break + if not passes: + continue + if output := solution_helper(depth - 1, i + 1, to_test + [matches[i]], matches): + return output + return [] + + +def solution(n_primes: int = 5) -> int: + """ + This function behaves similarly to solution_helper, but it is not recursive and + defines some variables that are used in the recursive calls. It also defines a list + of matches for every prime it tests, optimizing search time. + + >>> solution(n_primes=2) + 14 + >>> solution(n_primes=3) + 405 + >>> solution(n_primes=4) + 3146 + """ + + # Generate primes and start variables + start = time() + generator: collections.abc.Iterator[int] = prime_generator() + primes: list[int] = [] + output: int = int(5e4) # initialize with theoretical max value + for _ in range(int(PRIMES_SIZE)): + primes.append(next(generator)) + if COUNTER: + print(f"{int(PRIMES_SIZE)} primes generated in {time() - start}s") + + # Main loop + limit = output ** (1 / n_primes) * n_primes + for i in range(len(primes)): + # Break main loop if the current minimal number is larger than the 3rd root + # of the current output The reason for this is to not waste time trying to + # find solutions once the smallest prime in the search is already too large + # to be part of the solution + if primes[i] > limit: + break + + # Iterate larger primes, store in matches. This should optimize the nested loops + matches: list[int] = [] + prime: int = primes[i] + for j in range(i + 1, len(primes)): + if test_concatenate(prime, primes[j]): + matches.append(primes[j]) + + # Match every candidate with every other candidate until 5 are found + if found := solution_helper( + depth=n_primes - 1, start_idx=+1, to_test=[prime], matches=matches + ): + output = min(output, sum(found)) + + if COUNTER: + print(f"Done ({time() - start}s)") + return output + + +if __name__ == "__main__": + print(f"{solution() = }") From 8ccffdf98ef4e7f3942df25b06f204a6ab883e09 Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Sat, 22 Apr 2023 16:25:12 +0200 Subject: [PATCH 02/15] Reword algorith explanation on project_euler/problem_060 --- project_euler/problem_060/sol1.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/project_euler/problem_060/sol1.py b/project_euler/problem_060/sol1.py index 4e5dc2fe60d7..8120484abe8c 100644 --- a/project_euler/problem_060/sol1.py +++ b/project_euler/problem_060/sol1.py @@ -25,7 +25,7 @@ from time import time PRIMES_SIZE = 1.2e3 -COUNTER = False +COUNTER = True def get_order(n: int) -> int: @@ -162,10 +162,11 @@ def solution(n_primes: int = 5) -> int: # Main loop limit = output ** (1 / n_primes) * n_primes for i in range(len(primes)): - # Break main loop if the current minimal number is larger than the 3rd root - # of the current output The reason for this is to not waste time trying to - # find solutions once the smallest prime in the search is already too large - # to be part of the solution + # Break main loop if the current minimal number is larger than the nth root + # of the current output times n, with n being the amount of primes searched. + # The reason for this is to reduce the search space with a reasonable upper + # bound. Analysis with lower values for n_primes shows that this is a valid + # optimization. if primes[i] > limit: break @@ -188,4 +189,4 @@ def solution(n_primes: int = 5) -> int: if __name__ == "__main__": - print(f"{solution() = }") + print(f"{solution(4) = }") From 633593793a3dba2bea51c638983d3ac57e6b695a Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Sat, 22 Apr 2023 16:29:35 +0200 Subject: [PATCH 03/15] Disable timer on project_euler/problem_060/sol1.py --- project_euler/problem_060/sol1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_060/sol1.py b/project_euler/problem_060/sol1.py index 8120484abe8c..5542e47f24fa 100644 --- a/project_euler/problem_060/sol1.py +++ b/project_euler/problem_060/sol1.py @@ -25,7 +25,7 @@ from time import time PRIMES_SIZE = 1.2e3 -COUNTER = True +COUNTER = False def get_order(n: int) -> int: @@ -189,4 +189,4 @@ def solution(n_primes: int = 5) -> int: if __name__ == "__main__": - print(f"{solution(4) = }") + print(f"{solution() = }") From 8ab974590a548646637a310562f58bf259861609 Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Sat, 22 Apr 2023 16:34:52 +0200 Subject: [PATCH 04/15] Implement suggested formatting for project_euler/problem_060/sol1.py --- project_euler/problem_060/sol1.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/project_euler/problem_060/sol1.py b/project_euler/problem_060/sol1.py index 5542e47f24fa..e29b2dbe0cd7 100644 --- a/project_euler/problem_060/sol1.py +++ b/project_euler/problem_060/sol1.py @@ -28,7 +28,7 @@ COUNTER = False -def get_order(n: int) -> int: +def get_order(target: int) -> int: """ Get the order of a number n, in order to facilitate concatenation. get_order(n) = x -> 10^x <= n < 10^(x+1) @@ -41,7 +41,7 @@ def get_order(n: int) -> int: 4 """ - return int(math.log(n, 10)) + return int(math.log(target, 10)) def test_concatenate(num1: int, num2: int) -> bool: @@ -66,7 +66,7 @@ def test_concatenate(num1: int, num2: int) -> bool: return True -def is_prime(n) -> bool: +def is_prime(target: int) -> bool: """ Simple prime test @@ -77,8 +77,8 @@ def is_prime(n) -> bool: >>> is_prime(10000007) False """ - for divider in range(2, int(n**0.5) + 1): - if int(n) % divider == 0: + for divider in range(2, int(target**0.5) + 1): + if int(target) % divider == 0: return False return True From 2faf2e938ffec4ed1d4be599318354bf4922f313 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:58:07 +0530 Subject: [PATCH 05/15] Update linear_discriminant_analysis.py --- machine_learning/linear_discriminant_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index f4fb5ba76b64..1a5d15dca284 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -399,7 +399,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system("cls" if name == "nt" else "clear") + system('clear' if os.name == 'posix' else 'cls') if __name__ == "__main__": From bff2edf6f6de7e00e5b4b08d0a9d53af90a0e388 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:29:43 +0000 Subject: [PATCH 06/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- machine_learning/linear_discriminant_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 1a5d15dca284..45da556701a3 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -399,7 +399,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system('clear' if os.name == 'posix' else 'cls') + system("clear" if os.name == "posix" else "cls") if __name__ == "__main__": From 41220e746bfbbd5d987ea4c714013f40852d8b72 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 22 Apr 2023 21:00:37 +0530 Subject: [PATCH 07/15] Update linear_discriminant_analysis.py --- machine_learning/linear_discriminant_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 45da556701a3..4d65f1aa5336 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -399,7 +399,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system("clear" if os.name == "posix" else "cls") + system("clear" if name == "posix" else "cls") if __name__ == "__main__": From de13a9af856f0329ba21e48356fed7051f46dacf Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:48:35 +0530 Subject: [PATCH 08/15] Update rsa_cipher.py by replacing %s with {} --- ciphers/rsa_cipher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index de26992f5eeb..0e3df87cb891 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -76,7 +76,7 @@ def encrypt_and_write_to_file( key_size, n, e = read_key_file(key_filename) if key_size < block_size * 8: sys.exit( - "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher " + "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " "Either decrease the block size or use different keys." % (block_size * 8, key_size) @@ -101,7 +101,7 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: if key_size < block_size * 8: sys.exit( - "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher " + "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " "Did you specify the correct key file and encrypted file?" % (block_size * 8, key_size) From d91fc28cc22c8c4791ccbe87efe8ccfe5cb1b679 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:51:27 +0530 Subject: [PATCH 09/15] Update rsa_cipher.py --- ciphers/rsa_cipher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 0e3df87cb891..aa59630b5645 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -79,7 +79,7 @@ def encrypt_and_write_to_file( "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " "Either decrease the block size or use different keys." - % (block_size * 8, key_size) + .format(block_size * 8, key_size) ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] @@ -104,7 +104,7 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " "Did you specify the correct key file and encrypted file?" - % (block_size * 8, key_size) + .format(block_size * 8, key_size) ) encrypted_blocks = [] From 69015fb3ec9df08078511e254e4675ff1359510e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:21:57 +0000 Subject: [PATCH 10/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/rsa_cipher.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index aa59630b5645..9c41cdc5d472 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -78,8 +78,9 @@ def encrypt_and_write_to_file( sys.exit( "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " - "Either decrease the block size or use different keys." - .format(block_size * 8, key_size) + "Either decrease the block size or use different keys.".format( + block_size * 8, key_size + ) ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] @@ -103,8 +104,9 @@ def read_from_file_and_decrypt(message_filename: str, key_filename: str) -> str: sys.exit( "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " - "Did you specify the correct key file and encrypted file?" - .format(block_size * 8, key_size) + "Did you specify the correct key file and encrypted file?".format( + block_size * 8, key_size + ) ) encrypted_blocks = [] From 24a73e756419b1f9129e99eea9e203b8510d075b Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Sun, 23 Apr 2023 19:35:40 +0200 Subject: [PATCH 11/15] Remove system command to clear console on machine_learning/linear_discriminant_analysis.py --- machine_learning/linear_discriminant_analysis.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 4d65f1aa5336..b0ece521ecb9 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -44,7 +44,6 @@ """ from collections.abc import Callable from math import log -from os import name, system from random import gauss, seed from typing import TypeVar @@ -399,7 +398,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - system("clear" if name == "posix" else "cls") + # system("clear" if name == "posix" else "cls") if __name__ == "__main__": From 57ee3772a4b5833b77fed55e5dccabfdaf6f9400 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:58:30 +0530 Subject: [PATCH 12/15] Update linear_discriminant_analysis.py and rsa_cipher.py (#8680) * Update rsa_cipher.py by replacing %s with {} * Update rsa_cipher.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update linear_discriminant_analysis.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update linear_discriminant_analysis.py * Update machine_learning/linear_discriminant_analysis.py Co-authored-by: Christian Clauss * Update linear_discriminant_analysis.py * updated --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- machine_learning/linear_discriminant_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index b0ece521ecb9..5469c6decc9d 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -398,7 +398,7 @@ def main(): if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q": print("\n" + "GoodBye!".center(100, "-") + "\n") break - # system("clear" if name == "posix" else "cls") + system("clear" if name == "posix" else "cls") # noqa: S605 if __name__ == "__main__": From 296239187b1eeec9b3902466b543eec8df09c383 Mon Sep 17 00:00:00 2001 From: Rohan Anand <96521078+rohan472000@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:51:27 +0530 Subject: [PATCH 13/15] Update rsa_cipher.py --- ciphers/rsa_cipher.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 9c41cdc5d472..762337a2d119 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -78,9 +78,14 @@ def encrypt_and_write_to_file( sys.exit( "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " +<<<<<<< HEAD "Either decrease the block size or use different keys.".format( block_size * 8, key_size ) +======= + "Either decrease the block size or use different keys." + .format(block_size * 8, key_size) +>>>>>>> d91fc28 (Update rsa_cipher.py) ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] From c5960166000f04fdc85b4b4f70c31c17a32a7e25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Apr 2023 15:21:57 +0000 Subject: [PATCH 14/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/rsa_cipher.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ciphers/rsa_cipher.py b/ciphers/rsa_cipher.py index 762337a2d119..9c41cdc5d472 100644 --- a/ciphers/rsa_cipher.py +++ b/ciphers/rsa_cipher.py @@ -78,14 +78,9 @@ def encrypt_and_write_to_file( sys.exit( "ERROR: Block size is {} bits and key size is {} bits. The RSA cipher " "requires the block size to be equal to or greater than the key size. " -<<<<<<< HEAD "Either decrease the block size or use different keys.".format( block_size * 8, key_size ) -======= - "Either decrease the block size or use different keys." - .format(block_size * 8, key_size) ->>>>>>> d91fc28 (Update rsa_cipher.py) ) encrypted_blocks = [str(i) for i in encrypt_message(message, (n, e), block_size)] From 19086945b1b4bbee5e17262d027f5324f592990f Mon Sep 17 00:00:00 2001 From: Ignacio G Date: Mon, 24 Apr 2023 09:41:09 +0200 Subject: [PATCH 15/15] Rollback changes --- machine_learning/linear_discriminant_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index 5469c6decc9d..c0a477be10c7 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -44,6 +44,7 @@ """ from collections.abc import Callable from math import log +from os import name, system from random import gauss, seed from typing import TypeVar