From 43ea6d69272ee2da7d32c93a0ae45a113d95b80d Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Thu, 3 Sep 2020 16:36:47 +0430 Subject: [PATCH 1/6] writing test for min cost string conversion also modified the main script to be testable I also had to change the line that handled `delete` section Fixes #2128 --- strings/min_cost_string_conversion.py | 42 ++++++++++++++----- .../tests/test_min_cost_string_conversion.py | 36 ++++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 strings/tests/test_min_cost_string_conversion.py diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 34b42f3f0f64..f427426757d4 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -7,6 +7,9 @@ ---Delete character with cost cD ---Insert character with cost cI """ +import sys + +from tempfile import TemporaryFile def compute_transform_tables(X, Y, cC, cR, cD, cI): @@ -65,18 +68,19 @@ def assemble_transformation(ops, i, j): return seq -if __name__ == "__main__": - _, operations = compute_transform_tables("Python", "Algorithms", -1, 1, 2, 2) +def run_algorithm(str1, str2, copy, replace, delete, insert): + _, operations = compute_transform_tables( + str1, str2, copy, replace, delete, insert) m = len(operations) n = len(operations[0]) sequence = assemble_transformation(operations, m - 1, n - 1) - string = list("Python") + string = list(str1) i = 0 cost = 0 - with open("min_cost.txt", "w") as file: + with TemporaryFile("w") as file: for op in sequence: print("".join(string)) @@ -85,23 +89,24 @@ def assemble_transformation(ops, i, j): file.write("\t\t\t" + "".join(string)) file.write("\r\n") - cost -= 1 + cost += copy elif op[0] == "R": string[i] = op[2] - file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) + file.write("%-16s" % ("Replace %c" % + op[1] + " with " + str(op[2]))) file.write("\t\t" + "".join(string)) file.write("\r\n") - cost += 1 + cost += replace elif op[0] == "D": - string.pop(i) + string.pop() # putting `i` would raise out of range file.write("%-16s" % "Delete %c" % op[1]) file.write("\t\t\t" + "".join(string)) file.write("\r\n") - cost += 2 + cost += delete else: string.insert(i, op[1]) @@ -109,7 +114,7 @@ def assemble_transformation(ops, i, j): file.write("\t\t\t" + "".join(string)) file.write("\r\n") - cost += 2 + cost += insert i += 1 @@ -117,3 +122,20 @@ def assemble_transformation(ops, i, j): print("Cost: ", cost) file.write("\r\nMinimum cost: " + str(cost)) + + return cost + + +if __name__ == "__main__": # pragma: no cover + args_length = len(sys.argv) + + str1 = sys.argv[1] if args_length >= 2 else "Python" + str2 = sys.argv[2] if args_length >= 3 else "Algorithms" + + default_costs = [-1, 1, 2, 2] + user_costs = list(map(int, *sys.argv[3:])) if args_length >= 7 else [] + + create, replace, delete, insert = user_costs[:4] or default_costs + + min_cost = run_algorithm(str1, str2, create, replace, delete, insert) + print(f"Min Cost: {min_cost}") diff --git a/strings/tests/test_min_cost_string_conversion.py b/strings/tests/test_min_cost_string_conversion.py new file mode 100644 index 000000000000..218ab5bbe20a --- /dev/null +++ b/strings/tests/test_min_cost_string_conversion.py @@ -0,0 +1,36 @@ +from strings.min_cost_string_conversion import run_algorithm as calc_min_cost + + +def test_calculate_min_cost_conversion_correct_answer_empty_string(): + s2 = "short string" + copy, replace, delete, insert = 1, 2, 3, 4 + min_cost = calc_min_cost("", s2, copy, replace, delete, insert) + assert min_cost == len(s2) * insert + + +def test_calculate_min_cost_conversion_correct_answer_first_correct_case(): + s1, s2 = "geek", "gesek" + copy, replace, delete, insert = 2, -2, 3, 8 + min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) + assert min_cost == insert + + +def test_calculate_min_cost_conversion_correct_answer_second_correct_case(): + s1, s2 = "cat", "cut" + copy, replace, delete, insert = 2, 6, 3, 8 + min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) + assert min_cost == copy * 2 + replace + + +def test_calculate_min_cost_conversion_correct_answer_third_correct_case(): + s1, s2 = "sunday", "saturday" + copy, replace, delete, insert = 4, 1, 3, 4 + min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) + assert min_cost == replace * 6 + insert * 2 + + +def test_calculate_min_cost_conversion_correct_answer_fourth_correct_case(): + s1, s2 = "pythonista", "" + copy, replace, delete, insert = 4, 1, 3, 4 + min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) + assert min_cost == delete * len(s1) From 66f434216621ea583848921b98483a46a6d201ec Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Thu, 10 Sep 2020 15:58:34 +0430 Subject: [PATCH 2/6] string-conversion: modified costs, and passed arguments to `map` without unpacking also renamed a variable `create` to `copy` --- strings/min_cost_string_conversion.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index f427426757d4..5b9987d226c4 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -132,10 +132,10 @@ def run_algorithm(str1, str2, copy, replace, delete, insert): str1 = sys.argv[1] if args_length >= 2 else "Python" str2 = sys.argv[2] if args_length >= 3 else "Algorithms" - default_costs = [-1, 1, 2, 2] - user_costs = list(map(int, *sys.argv[3:])) if args_length >= 7 else [] + default_costs = [0, 1, 2, 3] + user_costs = list(map(int, sys.argv[3:])) if args_length >= 7 else [] - create, replace, delete, insert = user_costs[:4] or default_costs + copy, replace, delete, insert = user_costs[:4] or default_costs - min_cost = run_algorithm(str1, str2, create, replace, delete, insert) + min_cost = run_algorithm(str1, str2, copy, replace, delete, insert) print(f"Min Cost: {min_cost}") From 0ca9adaaf4230932f21be0a880aa9b57cbbd2c61 Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Thu, 10 Sep 2020 16:22:35 +0430 Subject: [PATCH 3/6] strings: modified cost of different operations when testing string-conversion --- strings/tests/test_min_cost_string_conversion.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/strings/tests/test_min_cost_string_conversion.py b/strings/tests/test_min_cost_string_conversion.py index 218ab5bbe20a..54af43a814db 100644 --- a/strings/tests/test_min_cost_string_conversion.py +++ b/strings/tests/test_min_cost_string_conversion.py @@ -2,17 +2,17 @@ def test_calculate_min_cost_conversion_correct_answer_empty_string(): - s2 = "short string" + s1, s2 = "", "short string" copy, replace, delete, insert = 1, 2, 3, 4 - min_cost = calc_min_cost("", s2, copy, replace, delete, insert) + min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) assert min_cost == len(s2) * insert def test_calculate_min_cost_conversion_correct_answer_first_correct_case(): s1, s2 = "geek", "gesek" - copy, replace, delete, insert = 2, -2, 3, 8 + copy, replace, delete, insert = 2, 4, 3, 8 min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) - assert min_cost == insert + assert min_cost == copy * 4 + insert def test_calculate_min_cost_conversion_correct_answer_second_correct_case(): @@ -24,9 +24,9 @@ def test_calculate_min_cost_conversion_correct_answer_second_correct_case(): def test_calculate_min_cost_conversion_correct_answer_third_correct_case(): s1, s2 = "sunday", "saturday" - copy, replace, delete, insert = 4, 1, 3, 4 + copy, replace, delete, insert = 10, 20, 30, 40 min_cost = calc_min_cost(s1, s2, copy, replace, delete, insert) - assert min_cost == replace * 6 + insert * 2 + assert min_cost == copy * 5 + insert * 2 + replace def test_calculate_min_cost_conversion_correct_answer_fourth_correct_case(): From 109fd0dbb6474678a18b291e645ef53efd8eb2bd Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Thu, 26 Nov 2020 10:51:09 +0330 Subject: [PATCH 4/6] min-string-conversion: add type hint --- strings/min_cost_string_conversion.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/strings/min_cost_string_conversion.py b/strings/min_cost_string_conversion.py index 5b9987d226c4..2dd7e43a9f4c 100644 --- a/strings/min_cost_string_conversion.py +++ b/strings/min_cost_string_conversion.py @@ -8,11 +8,12 @@ ---Insert character with cost cI """ import sys - from tempfile import TemporaryFile -def compute_transform_tables(X, Y, cC, cR, cD, cI): +def compute_transform_tables( + X: list[str], Y: list[str], cC: int, cR: int, cD: int, cI: int +): X = list(X) Y = list(Y) m = len(X) @@ -49,7 +50,7 @@ def compute_transform_tables(X, Y, cC, cR, cD, cI): return costs, ops -def assemble_transformation(ops, i, j): +def assemble_transformation(ops: list[list[str]], i: int, j: int): if i == 0 and j == 0: seq = [] return seq @@ -68,9 +69,10 @@ def assemble_transformation(ops, i, j): return seq -def run_algorithm(str1, str2, copy, replace, delete, insert): - _, operations = compute_transform_tables( - str1, str2, copy, replace, delete, insert) +def run_algorithm( + str1: str, str2: str, copy: int, replace: int, delete: int, insert: int +): + _, operations = compute_transform_tables(str1, str2, copy, replace, delete, insert) m = len(operations) n = len(operations[0]) @@ -93,8 +95,7 @@ def run_algorithm(str1, str2, copy, replace, delete, insert): elif op[0] == "R": string[i] = op[2] - file.write("%-16s" % ("Replace %c" % - op[1] + " with " + str(op[2]))) + file.write("%-16s" % ("Replace %c" % op[1] + " with " + str(op[2]))) file.write("\t\t" + "".join(string)) file.write("\r\n") From a889959427ceb5961b8e72b1c48c9287489cd0f0 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Thu, 26 Nov 2020 07:22:50 +0000 Subject: [PATCH 5/6] fixup! Format Python code with psf/black push --- arithmetic_analysis/newton_method.py | 6 +- arithmetic_analysis/newton_raphson.py | 2 +- backtracking/all_permutations.py | 8 +- backtracking/all_subsequences.py | 8 +- backtracking/sudoku.py | 2 +- backtracking/sum_of_subsets.py | 12 +- blockchain/modular_division.py | 10 +- ciphers/enigma_machine2.py | 102 +++-- ciphers/xor_cipher.py | 70 +-- conversions/decimal_to_any.py | 98 ++-- conversions/decimal_to_binary.py | 44 +- conversions/decimal_to_hexadecimal.py | 66 +-- data_structures/binary_tree/avl_tree.py | 2 +- data_structures/binary_tree/red_black_tree.py | 10 +- data_structures/binary_tree/treap.py | 10 +- data_structures/hashing/double_hash.py | 2 +- data_structures/hashing/hash_table.py | 2 +- .../hashing/number_theory/prime_numbers.py | 4 +- data_structures/hashing/quadratic_probing.py | 2 +- data_structures/linked_list/deque_doubly.py | 12 +- .../stacks/infix_to_postfix_conversion.py | 4 +- data_structures/stacks/stack.py | 2 +- digital_image_processing/index_calculation.py | 430 +++++++++--------- divide_and_conquer/closest_pair_of_points.py | 2 +- divide_and_conquer/max_subarray_sum.py | 6 +- .../strassen_matrix_multiplication.py | 4 +- dynamic_programming/rod_cutting.py | 200 ++++---- graphs/bfs_shortest_path.py | 56 +-- graphs/depth_first_search.py | 24 +- graphs/prim.py | 24 +- hashes/md5.py | 4 +- linear_algebra/src/lib.py | 130 +++--- linear_algebra/src/test_linear_algebra.py | 26 +- machine_learning/decision_tree.py | 3 +- machine_learning/k_means_clust.py | 12 +- .../linear_discriminant_analysis.py | 2 +- machine_learning/linear_regression.py | 8 +- maths/kth_lexicographic_permutation.py | 26 +- maths/newton_raphson.py | 4 +- maths/prime_sieve_eratosthenes.py | 4 +- maths/relu.py | 20 +- maths/softmax.py | 32 +- maths/sum_of_geometric_progression.py | 2 +- maths/zellers_congruence.py | 3 +- matrix/matrix_operation.py | 4 +- other/activity_selection.py | 2 +- other/game_of_life.py | 2 +- other/least_recently_used.py | 10 +- other/magicdiamondpattern.py | 6 +- other/primelib.py | 92 ++-- project_euler/problem_09/sol2.py | 3 +- project_euler/problem_10/sol3.py | 2 +- project_euler/problem_15/sol1.py | 50 +- project_euler/problem_27/problem_27_sol1.py | 22 +- project_euler/problem_56/sol1.py | 22 +- sorts/merge_sort.py | 3 + strings/boyer_moore_search.py | 2 +- traversals/binary_tree_traversals.py | 22 +- 58 files changed, 890 insertions(+), 852 deletions(-) diff --git a/arithmetic_analysis/newton_method.py b/arithmetic_analysis/newton_method.py index 542f994aaf19..97d5d3d3e470 100644 --- a/arithmetic_analysis/newton_method.py +++ b/arithmetic_analysis/newton_method.py @@ -7,7 +7,11 @@ # function is the f(x) and derivative is the f'(x) -def newton(function: RealFunc, derivative: RealFunc, starting_int: int,) -> float: +def newton( + function: RealFunc, + derivative: RealFunc, + starting_int: int, +) -> float: """ >>> newton(lambda x: x ** 3 - 2 * x - 5, lambda x: 3 * x ** 2 - 2, 3) 2.0945514815423474 diff --git a/arithmetic_analysis/newton_raphson.py b/arithmetic_analysis/newton_raphson.py index 890cff060ec8..948759a09a2a 100644 --- a/arithmetic_analysis/newton_raphson.py +++ b/arithmetic_analysis/newton_raphson.py @@ -9,7 +9,7 @@ def newton_raphson(func: str, a: int, precision: int = 10 ** -10) -> float: - """ Finds root from the point 'a' onwards by Newton-Raphson method + """Finds root from the point 'a' onwards by Newton-Raphson method >>> newton_raphson("sin(x)", 2) 3.1415926536808043 >>> newton_raphson("x**2 - 5*x +2", 0.4) diff --git a/backtracking/all_permutations.py b/backtracking/all_permutations.py index d144436033de..5244fef97f93 100644 --- a/backtracking/all_permutations.py +++ b/backtracking/all_permutations.py @@ -13,10 +13,10 @@ def generate_all_permutations(sequence): def create_state_space_tree(sequence, current_sequence, index, index_used): """ - Creates a state space tree to iterate through each branch using DFS. - We know that each state has exactly len(sequence) - index children. - It terminates when it reaches the end of the given sequence. - """ + Creates a state space tree to iterate through each branch using DFS. + We know that each state has exactly len(sequence) - index children. + It terminates when it reaches the end of the given sequence. + """ if index == len(sequence): print(current_sequence) diff --git a/backtracking/all_subsequences.py b/backtracking/all_subsequences.py index 8283386991d9..3851c4ab0118 100644 --- a/backtracking/all_subsequences.py +++ b/backtracking/all_subsequences.py @@ -13,10 +13,10 @@ def generate_all_subsequences(sequence): def create_state_space_tree(sequence, current_subsequence, index): """ - Creates a state space tree to iterate through each branch using DFS. - We know that each state has exactly two children. - It terminates when it reaches the end of the given sequence. - """ + Creates a state space tree to iterate through each branch using DFS. + We know that each state has exactly two children. + It terminates when it reaches the end of the given sequence. + """ if index == len(sequence): print(current_subsequence) diff --git a/backtracking/sudoku.py b/backtracking/sudoku.py index d864e2823a9b..b3d38b4cc7c7 100644 --- a/backtracking/sudoku.py +++ b/backtracking/sudoku.py @@ -105,7 +105,7 @@ def sudoku(grid): [7, 4, 5, 2, 8, 6, 3, 1, 9]] >>> sudoku(no_solution) False - """ + """ if is_completed(grid): return grid diff --git a/backtracking/sum_of_subsets.py b/backtracking/sum_of_subsets.py index c03df18ae743..425ddcff927e 100644 --- a/backtracking/sum_of_subsets.py +++ b/backtracking/sum_of_subsets.py @@ -19,13 +19,13 @@ def generate_sum_of_subsets_soln(nums, max_sum): def create_state_space_tree(nums, max_sum, num_index, path, result, remaining_nums_sum): """ - 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 - given below satisfy. - This algorithm follows depth-fist-search and backtracks when the node is not - branchable. + 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 + given below satisfy. + This algorithm follows depth-fist-search and backtracks when the node is not + branchable. - """ + """ if sum(path) > max_sum or (remaining_nums_sum + sum(path)) < max_sum: return if sum(path) == max_sum: diff --git a/blockchain/modular_division.py b/blockchain/modular_division.py index c09863a3c5f0..8fcf6e37cbed 100644 --- a/blockchain/modular_division.py +++ b/blockchain/modular_division.py @@ -74,13 +74,13 @@ def modular_division2(a, b, n): def extended_gcd(a, b): """ - >>> extended_gcd(10, 6) - (2, -1, 2) + >>> 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 + ** extended_gcd function is used when d = gcd(a,b) is required in output """ assert a >= 0 and b >= 0 diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 4c79e1c2fab9..0fbe97284d38 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -17,26 +17,50 @@ # used alphabet -------------------------- # from string.ascii_uppercase -abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # -------------------------- default selection -------------------------- # rotors -------------------------- -rotor1 = 'EGZWVONAHDCLFQMSIPJBYUKXTR' -rotor2 = 'FOBHMDKEXQNRAULPGSJVTYICZW' -rotor3 = 'ZJXESIUQLHAVRMDOYGTNFWPBKC' +rotor1 = "EGZWVONAHDCLFQMSIPJBYUKXTR" +rotor2 = "FOBHMDKEXQNRAULPGSJVTYICZW" +rotor3 = "ZJXESIUQLHAVRMDOYGTNFWPBKC" # reflector -------------------------- -reflector = {'A': 'N', 'N': 'A', 'B': 'O', 'O': 'B', 'C': 'P', 'P': 'C', 'D': 'Q', - 'Q': 'D', 'E': 'R', 'R': 'E', 'F': 'S', 'S': 'F', 'G': 'T', 'T': 'G', - 'H': 'U', 'U': 'H', 'I': 'V', 'V': 'I', 'J': 'W', 'W': 'J', 'K': 'X', - 'X': 'K', 'L': 'Y', 'Y': 'L', 'M': 'Z', 'Z': 'M'} +reflector = { + "A": "N", + "N": "A", + "B": "O", + "O": "B", + "C": "P", + "P": "C", + "D": "Q", + "Q": "D", + "E": "R", + "R": "E", + "F": "S", + "S": "F", + "G": "T", + "T": "G", + "H": "U", + "U": "H", + "I": "V", + "V": "I", + "J": "W", + "W": "J", + "K": "X", + "X": "K", + "L": "Y", + "Y": "L", + "M": "Z", + "Z": "M", +} # -------------------------- extra rotors -------------------------- -rotor4 = 'RMDJXFUWGISLHVTCQNKYPBEZOA' -rotor5 = 'SGLCPQWZHKXAREONTFBVIYJUDM' -rotor6 = 'HVSICLTYKQUBXDWAJZOMFGPREN' -rotor7 = 'RZWQHFMVDBKICJLNTUXAGYPSOE' -rotor8 = 'LFKIJODBEGAMQPXVUHYSTCZRWN' -rotor9 = 'KOAEGVDHXPQZMLFTYWJNBRCIUS' +rotor4 = "RMDJXFUWGISLHVTCQNKYPBEZOA" +rotor5 = "SGLCPQWZHKXAREONTFBVIYJUDM" +rotor6 = "HVSICLTYKQUBXDWAJZOMFGPREN" +rotor7 = "RZWQHFMVDBKICJLNTUXAGYPSOE" +rotor8 = "LFKIJODBEGAMQPXVUHYSTCZRWN" +rotor9 = "KOAEGVDHXPQZMLFTYWJNBRCIUS" def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: @@ -57,19 +81,22 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: unique_rotsel = len(set(rotsel)) if unique_rotsel < 3: - raise Exception(f'Please use 3 unique rotors (not {unique_rotsel})') + raise Exception(f"Please use 3 unique rotors (not {unique_rotsel})") # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if not 0 < rotorpos1 <= len(abc): - raise ValueError(f'First rotor position is not within range of 1..26 (' - f'{rotorpos1}') + raise ValueError( + f"First rotor position is not within range of 1..26 (" f"{rotorpos1}" + ) if not 0 < rotorpos2 <= len(abc): - raise ValueError(f'Second rotor position is not within range of 1..26 (' - f'{rotorpos2})') + raise ValueError( + f"Second rotor position is not within range of 1..26 (" f"{rotorpos2})" + ) if not 0 < rotorpos3 <= len(abc): - raise ValueError(f'Third rotor position is not within range of 1..26 (' - f'{rotorpos3})') + raise ValueError( + f"Third rotor position is not within range of 1..26 (" f"{rotorpos3})" + ) # Validates string and returns dict pb = _plugboard(pb) @@ -97,21 +124,21 @@ def _plugboard(pbstring: str) -> dict: # a) is type string # b) has even length (so pairs can be made) if not isinstance(pbstring, str): - raise TypeError(f'Plugboard setting isn\'t type string ({type(pbstring)})') + raise TypeError(f"Plugboard setting isn't type string ({type(pbstring)})") elif len(pbstring) % 2 != 0: - raise Exception(f'Odd number of symbols ({len(pbstring)})') - elif pbstring == '': + raise Exception(f"Odd number of symbols ({len(pbstring)})") + elif pbstring == "": return {} - pbstring.replace(' ', '') + pbstring.replace(" ", "") # Checks if all characters are unique tmppbl = set() for i in pbstring: if i not in abc: - raise Exception(f'\'{i}\' not in list of symbols') + raise Exception(f"'{i}' not in list of symbols") elif i in tmppbl: - raise Exception(f'Duplicate symbol ({i})') + raise Exception(f"Duplicate symbol ({i})") else: tmppbl.add(i) del tmppbl @@ -125,8 +152,12 @@ def _plugboard(pbstring: str) -> dict: return pb -def enigma(text: str, rotor_position: tuple, - rotor_selection: tuple = (rotor1, rotor2, rotor3), plugb: str = '') -> str: +def enigma( + text: str, + rotor_position: tuple, + rotor_selection: tuple = (rotor1, rotor2, rotor3), + plugb: str = "", +) -> str: """ The only difference with real-world enigma is that I allowed string input. All characters are converted to uppercase. (non-letter symbol are ignored) @@ -179,7 +210,8 @@ def enigma(text: str, rotor_position: tuple, text = text.upper() rotor_position, rotor_selection, plugboard = _validator( - rotor_position, rotor_selection, plugb.upper()) + rotor_position, rotor_selection, plugb.upper() + ) rotorpos1, rotorpos2, rotorpos3 = rotor_position rotor1, rotor2, rotor3 = rotor_selection @@ -245,12 +277,12 @@ def enigma(text: str, rotor_position: tuple, return "".join(result) -if __name__ == '__main__': - message = 'This is my Python script that emulates the Enigma machine from WWII.' +if __name__ == "__main__": + message = "This is my Python script that emulates the Enigma machine from WWII." rotor_pos = (1, 1, 1) - pb = 'pictures' + pb = "pictures" rotor_sel = (rotor2, rotor4, rotor8) en = enigma(message, rotor_pos, rotor_sel, pb) - print('Encrypted message:', en) - print('Decrypted message:', enigma(en, rotor_pos, rotor_sel, pb)) + print("Encrypted message:", en) + print("Decrypted message:", enigma(en, rotor_pos, rotor_sel, pb)) diff --git a/ciphers/xor_cipher.py b/ciphers/xor_cipher.py index 0fcfbb0b9ae2..3b045fdac64a 100644 --- a/ciphers/xor_cipher.py +++ b/ciphers/xor_cipher.py @@ -21,20 +21,20 @@ class XORCipher: def __init__(self, key=0): """ - simple constructor that receives a key or uses - default key = 0 - """ + simple constructor that receives a key or uses + default key = 0 + """ # private field self.__key = key def encrypt(self, content, key): """ - input: 'content' of type string and 'key' of type int - output: encrypted string 'content' as a list of chars - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: 'content' of type string and 'key' of type int + output: encrypted string 'content' as a list of chars + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(key, int) and isinstance(content, str) @@ -55,11 +55,11 @@ def encrypt(self, content, key): def decrypt(self, content, key): """ - input: 'content' of type list and 'key' of type int - output: decrypted string 'content' as a list of chars - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: 'content' of type list and 'key' of type int + output: decrypted string 'content' as a list of chars + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(key, int) and isinstance(content, list) @@ -80,11 +80,11 @@ def decrypt(self, content, key): def encrypt_string(self, content, key=0): """ - input: 'content' of type string and 'key' of type int - output: encrypted string 'content' - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: 'content' of type string and 'key' of type int + output: encrypted string 'content' + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(key, int) and isinstance(content, str) @@ -105,11 +105,11 @@ def encrypt_string(self, content, key=0): def decrypt_string(self, content, key=0): """ - input: 'content' of type string and 'key' of type int - output: decrypted string 'content' - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: 'content' of type string and 'key' of type int + output: decrypted string 'content' + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(key, int) and isinstance(content, str) @@ -130,12 +130,12 @@ def decrypt_string(self, content, key=0): def encrypt_file(self, file, key=0): """ - input: filename (str) and a key (int) - output: returns true if encrypt process was - successful otherwise false - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: filename (str) and a key (int) + output: returns true if encrypt process was + successful otherwise false + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(file, str) and isinstance(key, int) @@ -155,12 +155,12 @@ def encrypt_file(self, file, key=0): def decrypt_file(self, file, key): """ - input: filename (str) and a key (int) - output: returns true if decrypt process was - successful otherwise false - if key not passed the method uses the key by the constructor. - otherwise key = 1 - """ + input: filename (str) and a key (int) + output: returns true if decrypt process was + successful otherwise false + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ # precondition assert isinstance(file, str) and isinstance(key, int) diff --git a/conversions/decimal_to_any.py b/conversions/decimal_to_any.py index cbed1ac12214..3c72a7732ac6 100644 --- a/conversions/decimal_to_any.py +++ b/conversions/decimal_to_any.py @@ -3,55 +3,55 @@ def decimal_to_any(num: int, base: int) -> str: """ - Convert a positive integer to another base as str. - >>> decimal_to_any(0, 2) - '0' - >>> decimal_to_any(5, 4) - '11' - >>> decimal_to_any(20, 3) - '202' - >>> decimal_to_any(58, 16) - '3A' - >>> decimal_to_any(243, 17) - 'E5' - >>> decimal_to_any(34923, 36) - 'QY3' - >>> decimal_to_any(10, 11) - 'A' - >>> decimal_to_any(16, 16) - '10' - >>> decimal_to_any(36, 36) - '10' - >>> # negatives will error - >>> decimal_to_any(-45, 8) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ValueError: parameter must be positive int - >>> # floats will error - >>> decimal_to_any(34.4, 6) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: int() can't convert non-string with explicit base - >>> # a float base will error - >>> decimal_to_any(5, 2.5) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: 'float' object cannot be interpreted as an integer - >>> # a str base will error - >>> decimal_to_any(10, '16') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: 'str' object cannot be interpreted as an integer - >>> # a base less than 2 will error - >>> decimal_to_any(7, 0) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ValueError: base must be >= 2 - >>> # a base greater than 36 will error - >>> decimal_to_any(34, 37) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - ValueError: base must be <= 36 + Convert a positive integer to another base as str. + >>> decimal_to_any(0, 2) + '0' + >>> decimal_to_any(5, 4) + '11' + >>> decimal_to_any(20, 3) + '202' + >>> decimal_to_any(58, 16) + '3A' + >>> decimal_to_any(243, 17) + 'E5' + >>> decimal_to_any(34923, 36) + 'QY3' + >>> decimal_to_any(10, 11) + 'A' + >>> decimal_to_any(16, 16) + '10' + >>> decimal_to_any(36, 36) + '10' + >>> # negatives will error + >>> decimal_to_any(-45, 8) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: parameter must be positive int + >>> # floats will error + >>> decimal_to_any(34.4, 6) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: int() can't convert non-string with explicit base + >>> # a float base will error + >>> decimal_to_any(5, 2.5) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> # a str base will error + >>> decimal_to_any(10, '16') # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: 'str' object cannot be interpreted as an integer + >>> # a base less than 2 will error + >>> decimal_to_any(7, 0) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: base must be >= 2 + >>> # a base greater than 36 will error + >>> decimal_to_any(34, 37) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: base must be <= 36 """ if isinstance(num, float): raise TypeError("int() can't convert non-string with explicit base") diff --git a/conversions/decimal_to_binary.py b/conversions/decimal_to_binary.py index 8fcf226c346c..7e83aee4f7a5 100644 --- a/conversions/decimal_to_binary.py +++ b/conversions/decimal_to_binary.py @@ -4,28 +4,28 @@ def decimal_to_binary(num: int) -> str: """ - Convert an Integer Decimal Number to a Binary Number as str. - >>> decimal_to_binary(0) - '0b0' - >>> decimal_to_binary(2) - '0b10' - >>> decimal_to_binary(7) - '0b111' - >>> decimal_to_binary(35) - '0b100011' - >>> # negatives work too - >>> decimal_to_binary(-2) - '-0b10' - >>> # other floats will error - >>> decimal_to_binary(16.16) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: 'float' object cannot be interpreted as an integer - >>> # strings will error as well - >>> decimal_to_binary('0xfffff') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: 'str' object cannot be interpreted as an integer + Convert an Integer Decimal Number to a Binary Number as str. + >>> decimal_to_binary(0) + '0b0' + >>> decimal_to_binary(2) + '0b10' + >>> decimal_to_binary(7) + '0b111' + >>> decimal_to_binary(35) + '0b100011' + >>> # negatives work too + >>> decimal_to_binary(-2) + '-0b10' + >>> # other floats will error + >>> decimal_to_binary(16.16) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> # strings will error as well + >>> decimal_to_binary('0xfffff') # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: 'str' object cannot be interpreted as an integer """ if type(num) == float: diff --git a/conversions/decimal_to_hexadecimal.py b/conversions/decimal_to_hexadecimal.py index 6bd9533ab390..433f78dfecb7 100644 --- a/conversions/decimal_to_hexadecimal.py +++ b/conversions/decimal_to_hexadecimal.py @@ -23,39 +23,39 @@ def decimal_to_hexadecimal(decimal): """ - take integer decimal value, return hexadecimal representation as str beginning - with 0x - >>> decimal_to_hexadecimal(5) - '0x5' - >>> decimal_to_hexadecimal(15) - '0xf' - >>> decimal_to_hexadecimal(37) - '0x25' - >>> decimal_to_hexadecimal(255) - '0xff' - >>> decimal_to_hexadecimal(4096) - '0x1000' - >>> decimal_to_hexadecimal(999098) - '0xf3eba' - >>> # negatives work too - >>> decimal_to_hexadecimal(-256) - '-0x100' - >>> # floats are acceptable if equivalent to an int - >>> decimal_to_hexadecimal(17.0) - '0x11' - >>> # other floats will error - >>> decimal_to_hexadecimal(16.16) # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - AssertionError - >>> # strings will error as well - >>> decimal_to_hexadecimal('0xfffff') # doctest: +ELLIPSIS - Traceback (most recent call last): - ... - AssertionError - >>> # results are the same when compared to Python's default hex function - >>> decimal_to_hexadecimal(-256) == hex(-256) - True + take integer decimal value, return hexadecimal representation as str beginning + with 0x + >>> decimal_to_hexadecimal(5) + '0x5' + >>> decimal_to_hexadecimal(15) + '0xf' + >>> decimal_to_hexadecimal(37) + '0x25' + >>> decimal_to_hexadecimal(255) + '0xff' + >>> decimal_to_hexadecimal(4096) + '0x1000' + >>> decimal_to_hexadecimal(999098) + '0xf3eba' + >>> # negatives work too + >>> decimal_to_hexadecimal(-256) + '-0x100' + >>> # floats are acceptable if equivalent to an int + >>> decimal_to_hexadecimal(17.0) + '0x11' + >>> # other floats will error + >>> decimal_to_hexadecimal(16.16) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AssertionError + >>> # strings will error as well + >>> decimal_to_hexadecimal('0xfffff') # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + AssertionError + >>> # results are the same when compared to Python's default hex function + >>> decimal_to_hexadecimal(-256) == hex(-256) + True """ assert type(decimal) in (int, float) and decimal == int(decimal) hexadecimal = "" diff --git a/data_structures/binary_tree/avl_tree.py b/data_structures/binary_tree/avl_tree.py index c6a45f1cbeb7..3362610b9303 100644 --- a/data_structures/binary_tree/avl_tree.py +++ b/data_structures/binary_tree/avl_tree.py @@ -109,7 +109,7 @@ def right_rotation(node): def left_rotation(node): """ - a mirror symmetry rotation of the left_rotation + a mirror symmetry rotation of the left_rotation """ print("right rotation node:", node.get_data()) ret = node.get_right() diff --git a/data_structures/binary_tree/red_black_tree.py b/data_structures/binary_tree/red_black_tree.py index 379cee61d888..5d721edfa45b 100644 --- a/data_structures/binary_tree/red_black_tree.py +++ b/data_structures/binary_tree/red_black_tree.py @@ -28,11 +28,11 @@ def __init__( right: Optional["RedBlackTree"] = None, ) -> None: """Initialize a new Red-Black Tree node with the given values: - label: The value associated with this node - color: 0 if black, 1 if red - parent: The parent to this node - left: This node's left child - right: This node's right child + label: The value associated with this node + color: 0 if black, 1 if red + parent: The parent to this node + left: This node's left child + right: This node's right child """ self.label = label self.parent = parent diff --git a/data_structures/binary_tree/treap.py b/data_structures/binary_tree/treap.py index 52b757d584c3..6e7e938d1aac 100644 --- a/data_structures/binary_tree/treap.py +++ b/data_structures/binary_tree/treap.py @@ -130,19 +130,19 @@ def interactTreap(root, args): >>> root = interactTreap(None, "+1") >>> inorder(root) - 1 + 1 >>> root = interactTreap(root, "+3 +5 +17 +19 +2 +16 +4 +0") >>> inorder(root) - 0 1 2 3 4 5 16 17 19 + 0 1 2 3 4 5 16 17 19 >>> root = interactTreap(root, "+4 +4 +4") >>> inorder(root) - 0 1 2 3 4 4 4 4 5 16 17 19 + 0 1 2 3 4 4 4 4 5 16 17 19 >>> root = interactTreap(root, "-0") >>> inorder(root) - 1 2 3 4 4 4 4 5 16 17 19 + 1 2 3 4 4 4 4 5 16 17 19 >>> root = interactTreap(root, "-4") >>> inorder(root) - 1 2 3 5 16 17 19 + 1 2 3 5 16 17 19 >>> root = interactTreap(root, "=0") Unknown command """ diff --git a/data_structures/hashing/double_hash.py b/data_structures/hashing/double_hash.py index 9c6c8fed6a00..1007849c5a53 100644 --- a/data_structures/hashing/double_hash.py +++ b/data_structures/hashing/double_hash.py @@ -5,7 +5,7 @@ class DoubleHash(HashTable): """ - Hash Table example with open addressing and Double Hash + Hash Table example with open addressing and Double Hash """ def __init__(self, *args, **kwargs): diff --git a/data_structures/hashing/hash_table.py b/data_structures/hashing/hash_table.py index 3b39742f9d09..6e03be95b737 100644 --- a/data_structures/hashing/hash_table.py +++ b/data_structures/hashing/hash_table.py @@ -4,7 +4,7 @@ class HashTable: """ - Basic Hash Table example with open addressing and linear probing + Basic Hash Table example with open addressing and linear probing """ def __init__(self, size_table, charge_factor=None, lim_charge=None): diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py index 2a966e0da7f2..db4d40f475b2 100644 --- a/data_structures/hashing/number_theory/prime_numbers.py +++ b/data_structures/hashing/number_theory/prime_numbers.py @@ -6,8 +6,8 @@ def check_prime(number): """ - it's not the best solution - """ + it's not the best solution + """ special_non_primes = [0, 1, 2] if number in special_non_primes[:2]: return 2 diff --git a/data_structures/hashing/quadratic_probing.py b/data_structures/hashing/quadratic_probing.py index 668ddaa85048..06f3ced49d3c 100644 --- a/data_structures/hashing/quadratic_probing.py +++ b/data_structures/hashing/quadratic_probing.py @@ -5,7 +5,7 @@ class QuadraticProbing(HashTable): """ - Basic Hash Table example with open addressing using Quadratic Probing + Basic Hash Table example with open addressing using Quadratic Probing """ def __init__(self, *args, **kwargs): diff --git a/data_structures/linked_list/deque_doubly.py b/data_structures/linked_list/deque_doubly.py index 7025a7ea22f9..c6ee2ed27ba5 100644 --- a/data_structures/linked_list/deque_doubly.py +++ b/data_structures/linked_list/deque_doubly.py @@ -61,7 +61,7 @@ def _delete(self, node): class LinkedDeque(_DoublyLinkedBase): def first(self): - """ return first element + """return first element >>> d = LinkedDeque() >>> d.add_first('A').first() 'A' @@ -73,7 +73,7 @@ def first(self): return self._header._next._data def last(self): - """ return last element + """return last element >>> d = LinkedDeque() >>> d.add_last('A').last() 'A' @@ -87,14 +87,14 @@ def last(self): # DEque Insert Operations (At the front, At the end) def add_first(self, element): - """ insertion in the front + """insertion in the front >>> LinkedDeque().add_first('AV').first() 'AV' """ return self._insert(self._header, element, self._header._next) def add_last(self, element): - """ insertion in the end + """insertion in the end >>> LinkedDeque().add_last('B').last() 'B' """ @@ -103,7 +103,7 @@ def add_last(self, element): # DEqueu Remove Operations (At the front, At the end) def remove_first(self): - """ removal from the front + """removal from the front >>> d = LinkedDeque() >>> d.is_empty() True @@ -123,7 +123,7 @@ def remove_first(self): return self._delete(self._header._next) def remove_last(self): - """ removal in the end + """removal in the end >>> d = LinkedDeque() >>> d.is_empty() True diff --git a/data_structures/stacks/infix_to_postfix_conversion.py b/data_structures/stacks/infix_to_postfix_conversion.py index 61114402377a..4a1180c9d8e4 100644 --- a/data_structures/stacks/infix_to_postfix_conversion.py +++ b/data_structures/stacks/infix_to_postfix_conversion.py @@ -10,7 +10,7 @@ def is_operand(char): def precedence(char): - """ Return integer value representing an operator's precedence, or + """Return integer value representing an operator's precedence, or order of operation. https://en.wikipedia.org/wiki/Order_of_operations @@ -20,7 +20,7 @@ def precedence(char): def infix_to_postfix(expression): - """ Convert infix notation to postfix notation using the Shunting-yard + """Convert infix notation to postfix notation using the Shunting-yard algorithm. https://en.wikipedia.org/wiki/Shunting-yard_algorithm diff --git a/data_structures/stacks/stack.py b/data_structures/stacks/stack.py index baa0857eec0a..a4bcb5beabd4 100644 --- a/data_structures/stacks/stack.py +++ b/data_structures/stacks/stack.py @@ -2,7 +2,7 @@ class Stack: - """ A stack is an abstract data type that serves as a collection of + """A stack is an abstract data type that serves as a collection of elements with two principal operations: push() and pop(). push() adds an element to the top of the stack, and pop() removes an element from the top of a stack. The order in which elements come off of a stack are diff --git a/digital_image_processing/index_calculation.py b/digital_image_processing/index_calculation.py index d55815a6e15e..4350b8603390 100644 --- a/digital_image_processing/index_calculation.py +++ b/digital_image_processing/index_calculation.py @@ -10,98 +10,98 @@ # Class implemented to calculus the index class IndexCalculation: """ - # Class Summary - This algorithm consists in calculating vegetation indices, these - indices can be used for precision agriculture for example (or remote - sensing). There are functions to define the data and to calculate the - implemented indices. - - # Vegetation index - https://en.wikipedia.org/wiki/Vegetation_Index - A Vegetation Index (VI) is a spectral transformation of two or more bands - designed to enhance the contribution of vegetation properties and allow - reliable spatial and temporal inter-comparisons of terrestrial - photosynthetic activity and canopy structural variations - - # Information about channels (Wavelength range for each) - * nir - near-infrared - https://www.malvernpanalytical.com/br/products/technology/near-infrared-spectroscopy - Wavelength Range 700 nm to 2500 nm - * Red Edge - https://en.wikipedia.org/wiki/Red_edge - Wavelength Range 680 nm to 730 nm - * red - https://en.wikipedia.org/wiki/Color - Wavelength Range 635 nm to 700 nm - * blue - https://en.wikipedia.org/wiki/Color - Wavelength Range 450 nm to 490 nm - * green - https://en.wikipedia.org/wiki/Color - Wavelength Range 520 nm to 560 nm - - - # Implemented index list - #"abbreviationOfIndexName" -- list of channels used - - #"ARVI2" -- red, nir - #"CCCI" -- red, redEdge, nir - #"CVI" -- red, green, nir - #"GLI" -- red, green, blue - #"NDVI" -- red, nir - #"BNDVI" -- blue, nir - #"redEdgeNDVI" -- red, redEdge - #"GNDVI" -- green, nir - #"GBNDVI" -- green, blue, nir - #"GRNDVI" -- red, green, nir - #"RBNDVI" -- red, blue, nir - #"PNDVI" -- red, green, blue, nir - #"ATSAVI" -- red, nir - #"BWDRVI" -- blue, nir - #"CIgreen" -- green, nir - #"CIrededge" -- redEdge, nir - #"CI" -- red, blue - #"CTVI" -- red, nir - #"GDVI" -- green, nir - #"EVI" -- red, blue, nir - #"GEMI" -- red, nir - #"GOSAVI" -- green, nir - #"GSAVI" -- green, nir - #"Hue" -- red, green, blue - #"IVI" -- red, nir - #"IPVI" -- red, nir - #"I" -- red, green, blue - #"RVI" -- red, nir - #"MRVI" -- red, nir - #"MSAVI" -- red, nir - #"NormG" -- red, green, nir - #"NormNIR" -- red, green, nir - #"NormR" -- red, green, nir - #"NGRDI" -- red, green - #"RI" -- red, green - #"S" -- red, green, blue - #"IF" -- red, green, blue - #"DVI" -- red, nir - #"TVI" -- red, nir - #"NDRE" -- redEdge, nir - - #list of all index implemented - #allIndex = ["ARVI2", "CCCI", "CVI", "GLI", "NDVI", "BNDVI", "redEdgeNDVI", - "GNDVI", "GBNDVI", "GRNDVI", "RBNDVI", "PNDVI", "ATSAVI", - "BWDRVI", "CIgreen", "CIrededge", "CI", "CTVI", "GDVI", "EVI", - "GEMI", "GOSAVI", "GSAVI", "Hue", "IVI", "IPVI", "I", "RVI", - "MRVI", "MSAVI", "NormG", "NormNIR", "NormR", "NGRDI", "RI", - "S", "IF", "DVI", "TVI", "NDRE"] - - #list of index with not blue channel - #notBlueIndex = ["ARVI2", "CCCI", "CVI", "NDVI", "redEdgeNDVI", "GNDVI", - "GRNDVI", "ATSAVI", "CIgreen", "CIrededge", "CTVI", "GDVI", - "GEMI", "GOSAVI", "GSAVI", "IVI", "IPVI", "RVI", "MRVI", - "MSAVI", "NormG", "NormNIR", "NormR", "NGRDI", "RI", "DVI", - "TVI", "NDRE"] - - #list of index just with RGB channels - #RGBIndex = ["GLI", "CI", "Hue", "I", "NGRDI", "RI", "S", "IF"] + # Class Summary + This algorithm consists in calculating vegetation indices, these + indices can be used for precision agriculture for example (or remote + sensing). There are functions to define the data and to calculate the + implemented indices. + + # Vegetation index + https://en.wikipedia.org/wiki/Vegetation_Index + A Vegetation Index (VI) is a spectral transformation of two or more bands + designed to enhance the contribution of vegetation properties and allow + reliable spatial and temporal inter-comparisons of terrestrial + photosynthetic activity and canopy structural variations + + # Information about channels (Wavelength range for each) + * nir - near-infrared + https://www.malvernpanalytical.com/br/products/technology/near-infrared-spectroscopy + Wavelength Range 700 nm to 2500 nm + * Red Edge + https://en.wikipedia.org/wiki/Red_edge + Wavelength Range 680 nm to 730 nm + * red + https://en.wikipedia.org/wiki/Color + Wavelength Range 635 nm to 700 nm + * blue + https://en.wikipedia.org/wiki/Color + Wavelength Range 450 nm to 490 nm + * green + https://en.wikipedia.org/wiki/Color + Wavelength Range 520 nm to 560 nm + + + # Implemented index list + #"abbreviationOfIndexName" -- list of channels used + + #"ARVI2" -- red, nir + #"CCCI" -- red, redEdge, nir + #"CVI" -- red, green, nir + #"GLI" -- red, green, blue + #"NDVI" -- red, nir + #"BNDVI" -- blue, nir + #"redEdgeNDVI" -- red, redEdge + #"GNDVI" -- green, nir + #"GBNDVI" -- green, blue, nir + #"GRNDVI" -- red, green, nir + #"RBNDVI" -- red, blue, nir + #"PNDVI" -- red, green, blue, nir + #"ATSAVI" -- red, nir + #"BWDRVI" -- blue, nir + #"CIgreen" -- green, nir + #"CIrededge" -- redEdge, nir + #"CI" -- red, blue + #"CTVI" -- red, nir + #"GDVI" -- green, nir + #"EVI" -- red, blue, nir + #"GEMI" -- red, nir + #"GOSAVI" -- green, nir + #"GSAVI" -- green, nir + #"Hue" -- red, green, blue + #"IVI" -- red, nir + #"IPVI" -- red, nir + #"I" -- red, green, blue + #"RVI" -- red, nir + #"MRVI" -- red, nir + #"MSAVI" -- red, nir + #"NormG" -- red, green, nir + #"NormNIR" -- red, green, nir + #"NormR" -- red, green, nir + #"NGRDI" -- red, green + #"RI" -- red, green + #"S" -- red, green, blue + #"IF" -- red, green, blue + #"DVI" -- red, nir + #"TVI" -- red, nir + #"NDRE" -- redEdge, nir + + #list of all index implemented + #allIndex = ["ARVI2", "CCCI", "CVI", "GLI", "NDVI", "BNDVI", "redEdgeNDVI", + "GNDVI", "GBNDVI", "GRNDVI", "RBNDVI", "PNDVI", "ATSAVI", + "BWDRVI", "CIgreen", "CIrededge", "CI", "CTVI", "GDVI", "EVI", + "GEMI", "GOSAVI", "GSAVI", "Hue", "IVI", "IPVI", "I", "RVI", + "MRVI", "MSAVI", "NormG", "NormNIR", "NormR", "NGRDI", "RI", + "S", "IF", "DVI", "TVI", "NDRE"] + + #list of index with not blue channel + #notBlueIndex = ["ARVI2", "CCCI", "CVI", "NDVI", "redEdgeNDVI", "GNDVI", + "GRNDVI", "ATSAVI", "CIgreen", "CIrededge", "CTVI", "GDVI", + "GEMI", "GOSAVI", "GSAVI", "IVI", "IPVI", "RVI", "MRVI", + "MSAVI", "NormG", "NormNIR", "NormR", "NGRDI", "RI", "DVI", + "TVI", "NDRE"] + + #list of index just with RGB channels + #RGBIndex = ["GLI", "CI", "Hue", "I", "NGRDI", "RI", "S", "IF"] """ def __init__(self, red=None, green=None, blue=None, redEdge=None, nir=None): @@ -189,9 +189,9 @@ def ARVI2(self): def CCCI(self): """ - Canopy Chlorophyll Content Index - https://www.indexdatabase.de/db/i-single.php?id=224 - :return: index + Canopy Chlorophyll Content Index + https://www.indexdatabase.de/db/i-single.php?id=224 + :return: index """ return ((self.nir - self.redEdge) / (self.nir + self.redEdge)) / ( (self.nir - self.red) / (self.nir + self.red) @@ -199,17 +199,17 @@ def CCCI(self): def CVI(self): """ - Chlorophyll vegetation index - https://www.indexdatabase.de/db/i-single.php?id=391 - :return: index + Chlorophyll vegetation index + https://www.indexdatabase.de/db/i-single.php?id=391 + :return: index """ return self.nir * (self.red / (self.green ** 2)) def GLI(self): """ - self.green leaf index - https://www.indexdatabase.de/db/i-single.php?id=375 - :return: index + self.green leaf index + https://www.indexdatabase.de/db/i-single.php?id=375 + :return: index """ return (2 * self.green - self.red - self.blue) / ( 2 * self.green + self.red + self.blue @@ -217,43 +217,43 @@ def GLI(self): def NDVI(self): """ - Normalized Difference self.nir/self.red Normalized Difference Vegetation - Index, Calibrated NDVI - CDVI - https://www.indexdatabase.de/db/i-single.php?id=58 - :return: index + Normalized Difference self.nir/self.red Normalized Difference Vegetation + Index, Calibrated NDVI - CDVI + https://www.indexdatabase.de/db/i-single.php?id=58 + :return: index """ return (self.nir - self.red) / (self.nir + self.red) def BNDVI(self): """ - Normalized Difference self.nir/self.blue self.blue-normalized difference - vegetation index - https://www.indexdatabase.de/db/i-single.php?id=135 - :return: index + Normalized Difference self.nir/self.blue self.blue-normalized difference + vegetation index + https://www.indexdatabase.de/db/i-single.php?id=135 + :return: index """ return (self.nir - self.blue) / (self.nir + self.blue) def redEdgeNDVI(self): """ - Normalized Difference self.rededge/self.red - https://www.indexdatabase.de/db/i-single.php?id=235 - :return: index + Normalized Difference self.rededge/self.red + https://www.indexdatabase.de/db/i-single.php?id=235 + :return: index """ return (self.redEdge - self.red) / (self.redEdge + self.red) def GNDVI(self): """ - Normalized Difference self.nir/self.green self.green NDVI - https://www.indexdatabase.de/db/i-single.php?id=401 - :return: index + Normalized Difference self.nir/self.green self.green NDVI + https://www.indexdatabase.de/db/i-single.php?id=401 + :return: index """ return (self.nir - self.green) / (self.nir + self.green) def GBNDVI(self): """ - self.green-self.blue NDVI - https://www.indexdatabase.de/db/i-single.php?id=186 - :return: index + self.green-self.blue NDVI + https://www.indexdatabase.de/db/i-single.php?id=186 + :return: index """ return (self.nir - (self.green + self.blue)) / ( self.nir + (self.green + self.blue) @@ -261,9 +261,9 @@ def GBNDVI(self): def GRNDVI(self): """ - self.green-self.red NDVI - https://www.indexdatabase.de/db/i-single.php?id=185 - :return: index + self.green-self.red NDVI + https://www.indexdatabase.de/db/i-single.php?id=185 + :return: index """ return (self.nir - (self.green + self.red)) / ( self.nir + (self.green + self.red) @@ -271,17 +271,17 @@ def GRNDVI(self): def RBNDVI(self): """ - self.red-self.blue NDVI - https://www.indexdatabase.de/db/i-single.php?id=187 - :return: index + self.red-self.blue NDVI + https://www.indexdatabase.de/db/i-single.php?id=187 + :return: index """ return (self.nir - (self.blue + self.red)) / (self.nir + (self.blue + self.red)) def PNDVI(self): """ - Pan NDVI - https://www.indexdatabase.de/db/i-single.php?id=188 - :return: index + Pan NDVI + https://www.indexdatabase.de/db/i-single.php?id=188 + :return: index """ return (self.nir - (self.green + self.red + self.blue)) / ( self.nir + (self.green + self.red + self.blue) @@ -289,9 +289,9 @@ def PNDVI(self): def ATSAVI(self, X=0.08, a=1.22, b=0.03): """ - Adjusted transformed soil-adjusted VI - https://www.indexdatabase.de/db/i-single.php?id=209 - :return: index + Adjusted transformed soil-adjusted VI + https://www.indexdatabase.de/db/i-single.php?id=209 + :return: index """ return a * ( (self.nir - a * self.red - b) @@ -300,58 +300,58 @@ def ATSAVI(self, X=0.08, a=1.22, b=0.03): def BWDRVI(self): """ - self.blue-wide dynamic range vegetation index - https://www.indexdatabase.de/db/i-single.php?id=136 - :return: index + self.blue-wide dynamic range vegetation index + https://www.indexdatabase.de/db/i-single.php?id=136 + :return: index """ return (0.1 * self.nir - self.blue) / (0.1 * self.nir + self.blue) def CIgreen(self): """ - Chlorophyll Index self.green - https://www.indexdatabase.de/db/i-single.php?id=128 - :return: index + Chlorophyll Index self.green + https://www.indexdatabase.de/db/i-single.php?id=128 + :return: index """ return (self.nir / self.green) - 1 def CIrededge(self): """ - Chlorophyll Index self.redEdge - https://www.indexdatabase.de/db/i-single.php?id=131 - :return: index + Chlorophyll Index self.redEdge + https://www.indexdatabase.de/db/i-single.php?id=131 + :return: index """ return (self.nir / self.redEdge) - 1 def CI(self): """ - Coloration Index - https://www.indexdatabase.de/db/i-single.php?id=11 - :return: index + Coloration Index + https://www.indexdatabase.de/db/i-single.php?id=11 + :return: index """ return (self.red - self.blue) / self.red def CTVI(self): """ - Corrected Transformed Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=244 - :return: index + Corrected Transformed Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=244 + :return: index """ ndvi = self.NDVI() return ((ndvi + 0.5) / (abs(ndvi + 0.5))) * (abs(ndvi + 0.5) ** (1 / 2)) def GDVI(self): """ - Difference self.nir/self.green self.green Difference Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=27 - :return: index + Difference self.nir/self.green self.green Difference Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=27 + :return: index """ return self.nir - self.green def EVI(self): """ - Enhanced Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=16 - :return: index + Enhanced Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=16 + :return: index """ return 2.5 * ( (self.nir - self.red) / (self.nir + 6 * self.red - 7.5 * self.blue + 1) @@ -359,9 +359,9 @@ def EVI(self): def GEMI(self): """ - Global Environment Monitoring Index - https://www.indexdatabase.de/db/i-single.php?id=25 - :return: index + Global Environment Monitoring Index + https://www.indexdatabase.de/db/i-single.php?id=25 + :return: index """ n = (2 * (self.nir ** 2 - self.red ** 2) + 1.5 * self.nir + 0.5 * self.red) / ( self.nir + self.red + 0.5 @@ -370,27 +370,27 @@ def GEMI(self): def GOSAVI(self, Y=0.16): """ - self.green Optimized Soil Adjusted Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=29 - mit Y = 0,16 - :return: index + self.green Optimized Soil Adjusted Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=29 + mit Y = 0,16 + :return: index """ return (self.nir - self.green) / (self.nir + self.green + Y) def GSAVI(self, L=0.5): """ - self.green Soil Adjusted Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=31 - mit L = 0,5 - :return: index + self.green Soil Adjusted Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=31 + mit L = 0,5 + :return: index """ return ((self.nir - self.green) / (self.nir + self.green + L)) * (1 + L) def Hue(self): """ - Hue - https://www.indexdatabase.de/db/i-single.php?id=34 - :return: index + Hue + https://www.indexdatabase.de/db/i-single.php?id=34 + :return: index """ return np.arctan( ((2 * self.red - self.green - self.blue) / 30.5) * (self.green - self.blue) @@ -398,51 +398,51 @@ def Hue(self): def IVI(self, a=None, b=None): """ - Ideal vegetation index - https://www.indexdatabase.de/db/i-single.php?id=276 - b=intercept of vegetation line - a=soil line slope - :return: index + Ideal vegetation index + https://www.indexdatabase.de/db/i-single.php?id=276 + b=intercept of vegetation line + a=soil line slope + :return: index """ return (self.nir - b) / (a * self.red) def IPVI(self): """ - Infraself.red percentage vegetation index - https://www.indexdatabase.de/db/i-single.php?id=35 - :return: index + Infraself.red percentage vegetation index + https://www.indexdatabase.de/db/i-single.php?id=35 + :return: index """ return (self.nir / ((self.nir + self.red) / 2)) * (self.NDVI() + 1) def I(self): # noqa: E741,E743 """ - Intensity - https://www.indexdatabase.de/db/i-single.php?id=36 - :return: index + Intensity + https://www.indexdatabase.de/db/i-single.php?id=36 + :return: index """ return (self.red + self.green + self.blue) / 30.5 def RVI(self): """ - Ratio-Vegetation-Index - http://www.seos-project.eu/modules/remotesensing/remotesensing-c03-s01-p01.html - :return: index + Ratio-Vegetation-Index + http://www.seos-project.eu/modules/remotesensing/remotesensing-c03-s01-p01.html + :return: index """ return self.nir / self.red def MRVI(self): """ - Modified Normalized Difference Vegetation Index RVI - https://www.indexdatabase.de/db/i-single.php?id=275 - :return: index + Modified Normalized Difference Vegetation Index RVI + https://www.indexdatabase.de/db/i-single.php?id=275 + :return: index """ return (self.RVI() - 1) / (self.RVI() + 1) def MSAVI(self): """ - Modified Soil Adjusted Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=44 - :return: index + Modified Soil Adjusted Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=44 + :return: index """ return ( (2 * self.nir + 1) @@ -451,51 +451,51 @@ def MSAVI(self): def NormG(self): """ - Norm G - https://www.indexdatabase.de/db/i-single.php?id=50 - :return: index + Norm G + https://www.indexdatabase.de/db/i-single.php?id=50 + :return: index """ return self.green / (self.nir + self.red + self.green) def NormNIR(self): """ - Norm self.nir - https://www.indexdatabase.de/db/i-single.php?id=51 - :return: index + Norm self.nir + https://www.indexdatabase.de/db/i-single.php?id=51 + :return: index """ return self.nir / (self.nir + self.red + self.green) def NormR(self): """ - Norm R - https://www.indexdatabase.de/db/i-single.php?id=52 - :return: index + Norm R + https://www.indexdatabase.de/db/i-single.php?id=52 + :return: index """ return self.red / (self.nir + self.red + self.green) def NGRDI(self): """ - Normalized Difference self.green/self.red Normalized self.green self.red - difference index, Visible Atmospherically Resistant Indices self.green - (VIself.green) - https://www.indexdatabase.de/db/i-single.php?id=390 - :return: index + Normalized Difference self.green/self.red Normalized self.green self.red + difference index, Visible Atmospherically Resistant Indices self.green + (VIself.green) + https://www.indexdatabase.de/db/i-single.php?id=390 + :return: index """ return (self.green - self.red) / (self.green + self.red) def RI(self): """ - Normalized Difference self.red/self.green self.redness Index - https://www.indexdatabase.de/db/i-single.php?id=74 - :return: index + Normalized Difference self.red/self.green self.redness Index + https://www.indexdatabase.de/db/i-single.php?id=74 + :return: index """ return (self.red - self.green) / (self.red + self.green) def S(self): """ - Saturation - https://www.indexdatabase.de/db/i-single.php?id=77 - :return: index + Saturation + https://www.indexdatabase.de/db/i-single.php?id=77 + :return: index """ max = np.max([np.max(self.red), np.max(self.green), np.max(self.blue)]) min = np.min([np.min(self.red), np.min(self.green), np.min(self.blue)]) @@ -503,26 +503,26 @@ def S(self): def IF(self): """ - Shape Index - https://www.indexdatabase.de/db/i-single.php?id=79 - :return: index + Shape Index + https://www.indexdatabase.de/db/i-single.php?id=79 + :return: index """ return (2 * self.red - self.green - self.blue) / (self.green - self.blue) def DVI(self): """ - Simple Ratio self.nir/self.red Difference Vegetation Index, Vegetation Index - Number (VIN) - https://www.indexdatabase.de/db/i-single.php?id=12 - :return: index + Simple Ratio self.nir/self.red Difference Vegetation Index, Vegetation Index + Number (VIN) + https://www.indexdatabase.de/db/i-single.php?id=12 + :return: index """ return self.nir / self.red def TVI(self): """ - Transformed Vegetation Index - https://www.indexdatabase.de/db/i-single.php?id=98 - :return: index + Transformed Vegetation Index + https://www.indexdatabase.de/db/i-single.php?id=98 + :return: index """ return (self.NDVI() + 0.5) ** (1 / 2) diff --git a/divide_and_conquer/closest_pair_of_points.py b/divide_and_conquer/closest_pair_of_points.py index eecf53a7450e..cb7fa00d1c8f 100644 --- a/divide_and_conquer/closest_pair_of_points.py +++ b/divide_and_conquer/closest_pair_of_points.py @@ -82,7 +82,7 @@ def dis_between_closest_in_strip(points, points_counts, min_dis=float("inf")): def closest_pair_of_points_sqr(points_sorted_on_x, points_sorted_on_y, points_counts): - """ divide and conquer approach + """divide and conquer approach Parameters : points, points_count (list(tuple(int, int)), int) diff --git a/divide_and_conquer/max_subarray_sum.py b/divide_and_conquer/max_subarray_sum.py index 03bf9d25cb8a..43f58086e078 100644 --- a/divide_and_conquer/max_subarray_sum.py +++ b/divide_and_conquer/max_subarray_sum.py @@ -11,7 +11,7 @@ def max_sum_from_start(array): - """ This function finds the maximum contiguous sum of array from 0 index + """This function finds the maximum contiguous sum of array from 0 index Parameters : array (list[int]) : given array @@ -30,7 +30,7 @@ def max_sum_from_start(array): def max_cross_array_sum(array, left, mid, right): - """ This function finds the maximum contiguous sum of left and right arrays + """This function finds the maximum contiguous sum of left and right arrays Parameters : array, left, mid, right (list[int], int, int, int) @@ -46,7 +46,7 @@ def max_cross_array_sum(array, left, mid, right): def max_subarray_sum(array, left, right): - """ Maximum contiguous sub-array sum, using divide and conquer method + """Maximum contiguous sub-array sum, using divide and conquer method Parameters : array, left, right (list[int], int, int) : diff --git a/divide_and_conquer/strassen_matrix_multiplication.py b/divide_and_conquer/strassen_matrix_multiplication.py index ea54b0f52d29..486258e8bae0 100644 --- a/divide_and_conquer/strassen_matrix_multiplication.py +++ b/divide_and_conquer/strassen_matrix_multiplication.py @@ -29,7 +29,9 @@ def matrix_subtraction(matrix_a: List, matrix_b: List): ] -def split_matrix(a: List,) -> Tuple[List, List, List, List]: +def split_matrix( + a: List, +) -> Tuple[List, List, List, List]: """ Given an even length matrix, returns the top_left, top_right, bot_left, bot_right quadrant. diff --git a/dynamic_programming/rod_cutting.py b/dynamic_programming/rod_cutting.py index a4919742e739..442a39cb1616 100644 --- a/dynamic_programming/rod_cutting.py +++ b/dynamic_programming/rod_cutting.py @@ -13,30 +13,30 @@ def naive_cut_rod_recursive(n: int, prices: list): """ - Solves the rod-cutting problem via naively without using the benefit of dynamic - programming. The results is the same sub-problems are solved several times - leading to an exponential runtime - - Runtime: O(2^n) - - Arguments - ------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - - Returns - ------- - The maximum revenue obtainable for a rod of length n given the list of prices - for each piece. - - Examples - -------- - >>> naive_cut_rod_recursive(4, [1, 5, 8, 9]) - 10 - >>> naive_cut_rod_recursive(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) - 30 - """ + Solves the rod-cutting problem via naively without using the benefit of dynamic + programming. The results is the same sub-problems are solved several times + leading to an exponential runtime + + Runtime: O(2^n) + + Arguments + ------- + n: int, the length of the rod + prices: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + + Returns + ------- + The maximum revenue obtainable for a rod of length n given the list of prices + for each piece. + + Examples + -------- + >>> naive_cut_rod_recursive(4, [1, 5, 8, 9]) + 10 + >>> naive_cut_rod_recursive(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) + 30 + """ _enforce_args(n, prices) if n == 0: @@ -52,35 +52,35 @@ def naive_cut_rod_recursive(n: int, prices: list): def top_down_cut_rod(n: int, prices: list): """ - Constructs a top-down dynamic programming solution for the rod-cutting - problem via memoization. This function serves as a wrapper for - _top_down_cut_rod_recursive - - Runtime: O(n^2) - - Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - - Note - ---- - For convenience and because Python's lists using 0-indexing, length(max_rev) = - n + 1, to accommodate for the revenue obtainable from a rod of length 0. - - Returns - ------- - The maximum revenue obtainable for a rod of length n given the list of prices - for each piece. - - Examples - ------- - >>> top_down_cut_rod(4, [1, 5, 8, 9]) - 10 - >>> top_down_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) - 30 - """ + Constructs a top-down dynamic programming solution for the rod-cutting + problem via memoization. This function serves as a wrapper for + _top_down_cut_rod_recursive + + Runtime: O(n^2) + + Arguments + -------- + n: int, the length of the rod + prices: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + + Note + ---- + For convenience and because Python's lists using 0-indexing, length(max_rev) = + n + 1, to accommodate for the revenue obtainable from a rod of length 0. + + Returns + ------- + The maximum revenue obtainable for a rod of length n given the list of prices + for each piece. + + Examples + ------- + >>> top_down_cut_rod(4, [1, 5, 8, 9]) + 10 + >>> top_down_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) + 30 + """ _enforce_args(n, prices) max_rev = [float("-inf") for _ in range(n + 1)] return _top_down_cut_rod_recursive(n, prices, max_rev) @@ -88,24 +88,24 @@ def top_down_cut_rod(n: int, prices: list): def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list): """ - Constructs a top-down dynamic programming solution for the rod-cutting problem - via memoization. - - Runtime: O(n^2) - - Arguments - -------- - n: int, the length of the rod - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - max_rev: list, the computed maximum revenue for a piece of rod. - ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` - - Returns - ------- - The maximum revenue obtainable for a rod of length n given the list of prices - for each piece. - """ + Constructs a top-down dynamic programming solution for the rod-cutting problem + via memoization. + + Runtime: O(n^2) + + Arguments + -------- + n: int, the length of the rod + prices: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + max_rev: list, the computed maximum revenue for a piece of rod. + ``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i`` + + Returns + ------- + The maximum revenue obtainable for a rod of length n given the list of prices + for each piece. + """ if max_rev[n] >= 0: return max_rev[n] elif n == 0: @@ -125,28 +125,28 @@ def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list): def bottom_up_cut_rod(n: int, prices: list): """ - Constructs a bottom-up dynamic programming solution for the rod-cutting problem - - Runtime: O(n^2) - - Arguments - ---------- - n: int, the maximum length of the rod. - prices: list, the prices for each piece of rod. ``p[i-i]`` is the - price for a rod of length ``i`` - - Returns - ------- - The maximum revenue obtainable from cutting a rod of length n given - the prices for each piece of rod p. - - Examples - ------- - >>> bottom_up_cut_rod(4, [1, 5, 8, 9]) - 10 - >>> bottom_up_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) - 30 - """ + Constructs a bottom-up dynamic programming solution for the rod-cutting problem + + Runtime: O(n^2) + + Arguments + ---------- + n: int, the maximum length of the rod. + prices: list, the prices for each piece of rod. ``p[i-i]`` is the + price for a rod of length ``i`` + + Returns + ------- + The maximum revenue obtainable from cutting a rod of length n given + the prices for each piece of rod p. + + Examples + ------- + >>> bottom_up_cut_rod(4, [1, 5, 8, 9]) + 10 + >>> bottom_up_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30]) + 30 + """ _enforce_args(n, prices) # length(max_rev) = n + 1, to accommodate for the revenue obtainable from a rod of @@ -166,16 +166,16 @@ def bottom_up_cut_rod(n: int, prices: list): def _enforce_args(n: int, prices: list): """ - Basic checks on the arguments to the rod-cutting algorithms + Basic checks on the arguments to the rod-cutting algorithms - n: int, the length of the rod - prices: list, the price list for each piece of rod. + n: int, the length of the rod + prices: list, the price list for each piece of rod. - Throws ValueError: + Throws ValueError: - if n is negative or there are fewer items in the price list than the length of - the rod - """ + if n is negative or there are fewer items in the price list than the length of + the rod + """ if n < 0: raise ValueError(f"n must be greater than or equal to 0. Got n = {n}") diff --git a/graphs/bfs_shortest_path.py b/graphs/bfs_shortest_path.py index c3664796e677..1655ca64208d 100644 --- a/graphs/bfs_shortest_path.py +++ b/graphs/bfs_shortest_path.py @@ -20,18 +20,18 @@ 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'] + 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 = [] @@ -70,22 +70,22 @@ 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 - >>> bfs_shortest_path_distance(graph, "A", "A") - 0 - >>> bfs_shortest_path_distance(graph, "A", "H") - -1 + 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 + >>> bfs_shortest_path_distance(graph, "A", "A") + 0 + >>> bfs_shortest_path_distance(graph, "A", "H") + -1 """ if not graph or start not in graph or target not in graph: return -1 diff --git a/graphs/depth_first_search.py b/graphs/depth_first_search.py index 1e2231907b78..9ec7083c7fc1 100644 --- a/graphs/depth_first_search.py +++ b/graphs/depth_first_search.py @@ -17,18 +17,18 @@ def depth_first_search(graph: Dict, start: str) -> Set[int]: """Depth First Search on Graph - :param graph: directed graph in dictionary format - :param vertex: starting vectex as a string - :returns: the trace of the search - >>> G = { "A": ["B", "C", "D"], "B": ["A", "D", "E"], - ... "C": ["A", "F"], "D": ["B", "D"], "E": ["B", "F"], - ... "F": ["C", "E", "G"], "G": ["F"] } - >>> start = "A" - >>> output_G = list({'A', 'B', 'C', 'D', 'E', 'F', 'G'}) - >>> all(x in output_G for x in list(depth_first_search(G, "A"))) - True - >>> all(x in output_G for x in list(depth_first_search(G, "G"))) - True + :param graph: directed graph in dictionary format + :param vertex: starting vectex as a string + :returns: the trace of the search + >>> G = { "A": ["B", "C", "D"], "B": ["A", "D", "E"], + ... "C": ["A", "F"], "D": ["B", "D"], "E": ["B", "F"], + ... "F": ["C", "E", "G"], "G": ["F"] } + >>> start = "A" + >>> output_G = list({'A', 'B', 'C', 'D', 'E', 'F', 'G'}) + >>> all(x in output_G for x in list(depth_first_search(G, "A"))) + True + >>> all(x in output_G for x in list(depth_first_search(G, "G"))) + True """ explored, stack = set(start), [start] while stack: diff --git a/graphs/prim.py b/graphs/prim.py index a1d46a5a12a4..f7376cfb48ca 100644 --- a/graphs/prim.py +++ b/graphs/prim.py @@ -56,14 +56,14 @@ def connect(graph, a, b, edge): def prim(graph: list, root: Vertex) -> list: """Prim's Algorithm. - Runtime: - O(mn) with `m` edges and `n` vertices + Runtime: + O(mn) with `m` edges and `n` vertices - Return: - List with the edges of a Minimum Spanning Tree + Return: + List with the edges of a Minimum Spanning Tree - Usage: - prim(graph, graph[0]) + Usage: + prim(graph, graph[0]) """ a = [] for u in graph: @@ -86,14 +86,14 @@ def prim(graph: list, root: Vertex) -> list: def prim_heap(graph: list, root: Vertex) -> Iterator[tuple]: """Prim's Algorithm with min heap. - Runtime: - O((m + n)log n) with `m` edges and `n` vertices + Runtime: + O((m + n)log n) with `m` edges and `n` vertices - Yield: - Edges of a Minimum Spanning Tree + Yield: + Edges of a Minimum Spanning Tree - Usage: - prim(graph, graph[0]) + Usage: + prim(graph, graph[0]) """ for u in graph: u.key = math.inf diff --git a/hashes/md5.py b/hashes/md5.py index 85565533d175..b7888fb610ac 100644 --- a/hashes/md5.py +++ b/hashes/md5.py @@ -94,9 +94,7 @@ def not32(i): def sum32(a, b): - """ - - """ + """""" return (a + b) % 2 ** 32 diff --git a/linear_algebra/src/lib.py b/linear_algebra/src/lib.py index 10b9da65863f..353c8334093b 100644 --- a/linear_algebra/src/lib.py +++ b/linear_algebra/src/lib.py @@ -26,29 +26,29 @@ class Vector: """ - This class represents a vector of arbitrary size. - You need to give the vector components. - - Overview about the methods: - - constructor(components : list) : init the vector - set(components : list) : changes the vector components. - __str__() : toString method - component(i : int): gets the i-th component (start by 0) - __len__() : gets the size of the vector (number of components) - euclidLength() : returns the euclidean length of the vector. - operator + : vector addition - operator - : vector subtraction - operator * : scalar multiplication and dot product - copy() : copies this vector and returns it. - changeComponent(pos,value) : changes the specified component. - TODO: compare-operator + This class represents a vector of arbitrary size. + You need to give the vector components. + + Overview about the methods: + + constructor(components : list) : init the vector + set(components : list) : changes the vector components. + __str__() : toString method + component(i : int): gets the i-th component (start by 0) + __len__() : gets the size of the vector (number of components) + euclidLength() : returns the euclidean length of the vector. + operator + : vector addition + operator - : vector subtraction + operator * : scalar multiplication and dot product + copy() : copies this vector and returns it. + changeComponent(pos,value) : changes the specified component. + TODO: compare-operator """ def __init__(self, components=None): """ - input: components or nothing - simple constructor for init the vector + input: components or nothing + simple constructor for init the vector """ if components is None: components = [] @@ -56,9 +56,9 @@ def __init__(self, components=None): def set(self, components): """ - input: new components - changes the components of the vector. - replace the components with newer one. + input: new components + changes the components of the vector. + replace the components with newer one. """ if len(components) > 0: self.__components = list(components) @@ -67,14 +67,14 @@ def set(self, components): def __str__(self): """ - returns a string representation of the vector + returns a string representation of the vector """ return "(" + ",".join(map(str, self.__components)) + ")" def component(self, i): """ - input: index (start at 0) - output: the i-th component of the vector. + input: index (start at 0) + output: the i-th component of the vector. """ if type(i) is int and -len(self.__components) <= i < len(self.__components): return self.__components[i] @@ -83,13 +83,13 @@ def component(self, i): def __len__(self): """ - returns the size of the vector + returns the size of the vector """ return len(self.__components) def euclidLength(self): """ - returns the euclidean length of the vector + returns the euclidean length of the vector """ summe = 0 for c in self.__components: @@ -98,9 +98,9 @@ def euclidLength(self): def __add__(self, other): """ - input: other vector - assumes: other vector has the same size - returns a new vector that represents the sum. + input: other vector + assumes: other vector has the same size + returns a new vector that represents the sum. """ size = len(self) if size == len(other): @@ -111,9 +111,9 @@ def __add__(self, other): def __sub__(self, other): """ - input: other vector - assumes: other vector has the same size - returns a new vector that represents the difference. + input: other vector + assumes: other vector has the same size + returns a new vector that represents the difference. """ size = len(self) if size == len(other): @@ -124,8 +124,8 @@ def __sub__(self, other): def __mul__(self, other): """ - mul implements the scalar multiplication - and the dot-product + mul implements the scalar multiplication + and the dot-product """ if isinstance(other, float) or isinstance(other, int): ans = [c * other for c in self.__components] @@ -141,15 +141,15 @@ def __mul__(self, other): def copy(self): """ - copies this vector and returns it. + copies this vector and returns it. """ return Vector(self.__components) def changeComponent(self, pos, value): """ - input: an index (pos) and a value - changes the specified component (pos) with the - 'value' + input: an index (pos) and a value + changes the specified component (pos) with the + 'value' """ # precondition assert -len(self.__components) <= pos < len(self.__components) @@ -158,7 +158,7 @@ def changeComponent(self, pos, value): def zeroVector(dimension): """ - returns a zero-vector of size 'dimension' + returns a zero-vector of size 'dimension' """ # precondition assert isinstance(dimension, int) @@ -167,8 +167,8 @@ def zeroVector(dimension): def unitBasisVector(dimension, pos): """ - returns a unit basis vector with a One - at index 'pos' (indexing at 0) + returns a unit basis vector with a One + at index 'pos' (indexing at 0) """ # precondition assert isinstance(dimension, int) and (isinstance(pos, int)) @@ -179,9 +179,9 @@ def unitBasisVector(dimension, pos): def axpy(scalar, x, y): """ - input: a 'scalar' and two vectors 'x' and 'y' - output: a vector - computes the axpy operation + input: a 'scalar' and two vectors 'x' and 'y' + output: a vector + computes the axpy operation """ # precondition assert ( @@ -194,10 +194,10 @@ def axpy(scalar, x, y): def randomVector(N, a, b): """ - input: size (N) of the vector. - random range (a,b) - output: returns a random vector of size N, with - random integer components between 'a' and 'b'. + input: size (N) of the vector. + random range (a,b) + output: returns a random vector of size N, with + random integer components between 'a' and 'b'. """ random.seed(None) ans = [random.randint(a, b) for i in range(N)] @@ -224,8 +224,8 @@ class Matrix: def __init__(self, matrix, w, h): """ - simple constructor for initializing - the matrix with components. + simple constructor for initializing + the matrix with components. """ self.__matrix = matrix self.__width = w @@ -233,8 +233,8 @@ def __init__(self, matrix, w, h): def __str__(self): """ - returns a string representation of this - matrix. + returns a string representation of this + matrix. """ ans = "" for i in range(self.__height): @@ -248,7 +248,7 @@ def __str__(self): def changeComponent(self, x, y, value): """ - changes the x-y component of this matrix + changes the x-y component of this matrix """ if 0 <= x < self.__height and 0 <= y < self.__width: self.__matrix[x][y] = value @@ -257,7 +257,7 @@ def changeComponent(self, x, y, value): def component(self, x, y): """ - returns the specified (x,y) component + returns the specified (x,y) component """ if 0 <= x < self.__height and 0 <= y < self.__width: return self.__matrix[x][y] @@ -266,19 +266,19 @@ def component(self, x, y): def width(self): """ - getter for the width + getter for the width """ return self.__width def height(self): """ - getter for the height + getter for the height """ return self.__height def determinate(self) -> float: """ - returns the determinate of an nxn matrix using Laplace expansion + returns the determinate of an nxn matrix using Laplace expansion """ if self.__height == self.__width and self.__width >= 2: total = 0 @@ -305,8 +305,8 @@ def determinate(self) -> float: def __mul__(self, other): """ - implements the matrix-vector multiplication. - implements the matrix-scalar multiplication + implements the matrix-vector multiplication. + implements the matrix-scalar multiplication """ if isinstance(other, Vector): # vector-matrix if len(other) == self.__width: @@ -332,7 +332,7 @@ def __mul__(self, other): def __add__(self, other): """ - implements the matrix-addition. + implements the matrix-addition. """ if self.__width == other.width() and self.__height == other.height(): matrix = [] @@ -347,7 +347,7 @@ def __add__(self, other): def __sub__(self, other): """ - implements the matrix-subtraction. + implements the matrix-subtraction. """ if self.__width == other.width() and self.__height == other.height(): matrix = [] @@ -363,7 +363,7 @@ def __sub__(self, other): def squareZeroMatrix(N): """ - returns a square zero-matrix of dimension NxN + returns a square zero-matrix of dimension NxN """ ans = [[0] * N for i in range(N)] return Matrix(ans, N, N) @@ -371,8 +371,8 @@ def squareZeroMatrix(N): def randomMatrix(W, H, a, b): """ - returns a random matrix WxH with integer components - between 'a' and 'b' + returns a random matrix WxH with integer components + between 'a' and 'b' """ random.seed(None) matrix = [[random.randint(a, b) for j in range(W)] for i in range(H)] diff --git a/linear_algebra/src/test_linear_algebra.py b/linear_algebra/src/test_linear_algebra.py index 8db480ceb29d..668ffe858b99 100644 --- a/linear_algebra/src/test_linear_algebra.py +++ b/linear_algebra/src/test_linear_algebra.py @@ -14,7 +14,7 @@ class Test(unittest.TestCase): def test_component(self): """ - test for method component + test for method component """ x = Vector([1, 2, 3]) self.assertEqual(x.component(0), 1) @@ -23,28 +23,28 @@ def test_component(self): def test_str(self): """ - test for toString() method + test for toString() method """ x = Vector([0, 0, 0, 0, 0, 1]) self.assertEqual(str(x), "(0,0,0,0,0,1)") def test_size(self): """ - test for size()-method + test for size()-method """ x = Vector([1, 2, 3, 4]) self.assertEqual(len(x), 4) def test_euclidLength(self): """ - test for the eulidean length + test for the eulidean length """ x = Vector([1, 2]) self.assertAlmostEqual(x.euclidLength(), 2.236, 3) def test_add(self): """ - test for + operator + test for + operator """ x = Vector([1, 2, 3]) y = Vector([1, 1, 1]) @@ -54,7 +54,7 @@ def test_add(self): def test_sub(self): """ - test for - operator + test for - operator """ x = Vector([1, 2, 3]) y = Vector([1, 1, 1]) @@ -64,7 +64,7 @@ def test_sub(self): def test_mul(self): """ - test for * operator + test for * operator """ x = Vector([1, 2, 3]) a = Vector([2, -1, 4]) # for test of dot-product @@ -74,19 +74,19 @@ def test_mul(self): def test_zeroVector(self): """ - test for the global function zeroVector(...) + test for the global function zeroVector(...) """ self.assertTrue(str(zeroVector(10)).count("0") == 10) def test_unitBasisVector(self): """ - test for the global function unitBasisVector(...) + test for the global function unitBasisVector(...) """ self.assertEqual(str(unitBasisVector(3, 1)), "(0,1,0)") def test_axpy(self): """ - test for the global function axpy(...) (operation) + test for the global function axpy(...) (operation) """ x = Vector([1, 2, 3]) y = Vector([1, 0, 1]) @@ -94,7 +94,7 @@ def test_axpy(self): def test_copy(self): """ - test for the copy()-method + test for the copy()-method """ x = Vector([1, 0, 0, 0, 0, 0]) y = x.copy() @@ -102,7 +102,7 @@ def test_copy(self): def test_changeComponent(self): """ - test for the changeComponent(...)-method + test for the changeComponent(...)-method """ x = Vector([1, 0, 0]) x.changeComponent(0, 0) @@ -115,7 +115,7 @@ def test_str_matrix(self): def test_determinate(self): """ - test for determinate() + test for determinate() """ A = Matrix([[1, 1, 4, 5], [3, 3, 3, 2], [5, 1, 9, 0], [9, 7, 7, 9]], 4, 4) self.assertEqual(-376, A.determinate()) diff --git a/machine_learning/decision_tree.py b/machine_learning/decision_tree.py index fe1d54736563..ace6fb0fa883 100644 --- a/machine_learning/decision_tree.py +++ b/machine_learning/decision_tree.py @@ -135,8 +135,7 @@ def predict(self, x): class Test_Decision_Tree: - """Decision Tres test class - """ + """Decision Tres test class""" @staticmethod def helper_mean_squared_error_test(labels, prediction): diff --git a/machine_learning/k_means_clust.py b/machine_learning/k_means_clust.py index 4da904ae3568..130e7f1ad669 100644 --- a/machine_learning/k_means_clust.py +++ b/machine_learning/k_means_clust.py @@ -132,12 +132,12 @@ def kmeans( data, k, initial_centroids, maxiter=500, record_heterogeneity=None, verbose=False ): """This function runs k-means on given data and initial set of centroids. - maxiter: maximum number of iterations to run.(default=500) - record_heterogeneity: (optional) a list, to store the history of heterogeneity - as function of iterations - if None, do not store the history. - verbose: if True, print how many data points changed their cluster labels in - each iteration""" + maxiter: maximum number of iterations to run.(default=500) + record_heterogeneity: (optional) a list, to store the history of heterogeneity + as function of iterations + if None, do not store the history. + verbose: if True, print how many data points changed their cluster labels in + each iteration""" centroids = initial_centroids[:] prev_cluster_assignment = None diff --git a/machine_learning/linear_discriminant_analysis.py b/machine_learning/linear_discriminant_analysis.py index b26da2628982..22ee63a5a62b 100644 --- a/machine_learning/linear_discriminant_analysis.py +++ b/machine_learning/linear_discriminant_analysis.py @@ -153,7 +153,7 @@ def calculate_variance(items: list, means: list, total_count: int) -> float: def predict_y_values( x_items: list, means: list, variance: float, probabilities: list ) -> list: - """ This function predicts new indexes(groups for our data) + """This function predicts new indexes(groups for our data) :param x_items: a list containing all items(gaussian distribution of all classes) :param means: a list containing real mean values of each class :param variance: calculated value of variance by calculate_variance function diff --git a/machine_learning/linear_regression.py b/machine_learning/linear_regression.py index 8c0dfebbca9d..a726629efe00 100644 --- a/machine_learning/linear_regression.py +++ b/machine_learning/linear_regression.py @@ -12,7 +12,7 @@ def collect_dataset(): - """ Collect dataset of CSGO + """Collect dataset of CSGO The dataset contains ADR vs Rating of a Player :return : dataset obtained from the link, as matrix """ @@ -32,7 +32,7 @@ def collect_dataset(): def run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta): - """ Run steep gradient descent and updates the Feature vector accordingly_ + """Run steep gradient descent and updates the Feature vector accordingly_ :param data_x : contains the dataset :param data_y : contains the output associated with each data-entry :param len_data : length of the data_ @@ -51,7 +51,7 @@ def run_steep_gradient_descent(data_x, data_y, len_data, alpha, theta): def sum_of_square_error(data_x, data_y, len_data, theta): - """ Return sum of square error for error calculation + """Return sum of square error for error calculation :param data_x : contains our dataset :param data_y : contains the output (result vector) :param len_data : len of the dataset @@ -66,7 +66,7 @@ def sum_of_square_error(data_x, data_y, len_data, theta): def run_linear_regression(data_x, data_y): - """ Implement Linear regression over the dataset + """Implement Linear regression over the dataset :param data_x : contains our dataset :param data_y : contains the output (result vector) :return : feature for line of best fit (Feature vector) diff --git a/maths/kth_lexicographic_permutation.py b/maths/kth_lexicographic_permutation.py index 491c1c84fa85..23eab626fbf8 100644 --- a/maths/kth_lexicographic_permutation.py +++ b/maths/kth_lexicographic_permutation.py @@ -1,18 +1,18 @@ def kthPermutation(k, n): """ - Finds k'th lexicographic permutation (in increasing order) of - 0,1,2,...n-1 in O(n^2) time. - - Examples: - First permutation is always 0,1,2,...n - >>> kthPermutation(0,5) - [0, 1, 2, 3, 4] - - The order of permutation of 0,1,2,3 is [0,1,2,3], [0,1,3,2], [0,2,1,3], - [0,2,3,1], [0,3,1,2], [0,3,2,1], [1,0,2,3], [1,0,3,2], [1,2,0,3], - [1,2,3,0], [1,3,0,2] - >>> kthPermutation(10,4) - [1, 3, 0, 2] + Finds k'th lexicographic permutation (in increasing order) of + 0,1,2,...n-1 in O(n^2) time. + + Examples: + First permutation is always 0,1,2,...n + >>> kthPermutation(0,5) + [0, 1, 2, 3, 4] + + The order of permutation of 0,1,2,3 is [0,1,2,3], [0,1,3,2], [0,2,1,3], + [0,2,3,1], [0,3,1,2], [0,3,2,1], [1,0,2,3], [1,0,3,2], [1,2,0,3], + [1,2,3,0], [1,3,0,2] + >>> kthPermutation(10,4) + [1, 3, 0, 2] """ # Factorails from 1! to (n-1)! factorials = [1] diff --git a/maths/newton_raphson.py b/maths/newton_raphson.py index d8a98dfa6703..f2b7cb9766d2 100644 --- a/maths/newton_raphson.py +++ b/maths/newton_raphson.py @@ -12,8 +12,8 @@ def calc_derivative(f, a, h=0.001): """ - Calculates derivative at point a for function f using finite difference - method + Calculates derivative at point a for function f using finite difference + method """ return (f(a + h) - f(a - h)) / (2 * h) diff --git a/maths/prime_sieve_eratosthenes.py b/maths/prime_sieve_eratosthenes.py index 0ebdfdb94e15..35eb1654950b 100644 --- a/maths/prime_sieve_eratosthenes.py +++ b/maths/prime_sieve_eratosthenes.py @@ -19,9 +19,9 @@ def prime_sieve_eratosthenes(num): print the prime numbers up to n >>> prime_sieve_eratosthenes(10) - 2 3 5 7 + 2 3 5 7 >>> prime_sieve_eratosthenes(20) - 2 3 5 7 11 13 17 19 + 2 3 5 7 11 13 17 19 """ primes = [True for i in range(num + 1)] diff --git a/maths/relu.py b/maths/relu.py index 89667ab3e73f..826ada65fa16 100644 --- a/maths/relu.py +++ b/maths/relu.py @@ -16,20 +16,20 @@ def relu(vector: List[float]): """ - Implements the relu function + Implements the relu function - Parameters: - vector (np.array,list,tuple): A numpy array of shape (1,n) - consisting of real values or a similar list,tuple + Parameters: + vector (np.array,list,tuple): A numpy array of shape (1,n) + consisting of real values or a similar list,tuple - Returns: - relu_vec (np.array): The input numpy array, after applying - relu. + Returns: + relu_vec (np.array): The input numpy array, after applying + relu. - >>> vec = np.array([-1, 0, 5]) - >>> relu(vec) - array([0, 0, 5]) + >>> vec = np.array([-1, 0, 5]) + >>> relu(vec) + array([0, 0, 5]) """ # compare two arrays and then return element-wise maxima. diff --git a/maths/softmax.py b/maths/softmax.py index 92ff4ca27b88..e021a7f8a6fe 100644 --- a/maths/softmax.py +++ b/maths/softmax.py @@ -15,28 +15,28 @@ def softmax(vector): """ - Implements the softmax function + Implements the softmax function - Parameters: - vector (np.array,list,tuple): A numpy array of shape (1,n) - consisting of real values or a similar list,tuple + Parameters: + vector (np.array,list,tuple): A numpy array of shape (1,n) + consisting of real values or a similar list,tuple - Returns: - softmax_vec (np.array): The input numpy array after applying - softmax. + Returns: + softmax_vec (np.array): The input numpy array after applying + softmax. - The softmax vector adds up to one. We need to ceil to mitigate for - precision - >>> np.ceil(np.sum(softmax([1,2,3,4]))) - 1.0 + The softmax vector adds up to one. We need to ceil to mitigate for + precision + >>> np.ceil(np.sum(softmax([1,2,3,4]))) + 1.0 - >>> vec = np.array([5,5]) - >>> softmax(vec) - array([0.5, 0.5]) + >>> vec = np.array([5,5]) + >>> softmax(vec) + array([0.5, 0.5]) - >>> softmax([0]) - array([1.]) + >>> softmax([0]) + array([1.]) """ # Calculate e^x for each x in your vector where e is Euler's diff --git a/maths/sum_of_geometric_progression.py b/maths/sum_of_geometric_progression.py index 614d6646ec43..f29dd8005cff 100644 --- a/maths/sum_of_geometric_progression.py +++ b/maths/sum_of_geometric_progression.py @@ -1,7 +1,7 @@ def sum_of_geometric_progression( first_term: int, common_ratio: int, num_of_terms: int ) -> float: - """" + """ " Return the sum of n terms in a geometric progression. >>> sum_of_geometric_progression(1, 2, 10) 1023.0 diff --git a/maths/zellers_congruence.py b/maths/zellers_congruence.py index 8608b32f3ee3..2d4a22a0a5ba 100644 --- a/maths/zellers_congruence.py +++ b/maths/zellers_congruence.py @@ -63,8 +63,7 @@ def zeller(date_input: str) -> str: >>> zeller('01-31-19082939') Traceback (most recent call last): ... - ValueError: Must be 10 characters long -""" + ValueError: Must be 10 characters long""" # Days of the week for response days = { diff --git a/matrix/matrix_operation.py b/matrix/matrix_operation.py index 42a94da12375..3838dab6be09 100644 --- a/matrix/matrix_operation.py +++ b/matrix/matrix_operation.py @@ -168,7 +168,9 @@ def main(): matrix_c = [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]] matrix_d = [[3, 0, 2], [2, 0, -2], [0, 1, 1]] print(f"Add Operation, {add(matrix_a, matrix_b) = } \n") - print(f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n",) + print( + f"Multiply Operation, {multiply(matrix_a, matrix_b) = } \n", + ) print(f"Identity: {identity(5)}\n") print(f"Minor of {matrix_c} = {minor(matrix_c, 1, 2)} \n") print(f"Determinant of {matrix_b} = {determinant(matrix_b)} \n") diff --git a/other/activity_selection.py b/other/activity_selection.py index 8876eb2930fc..210c7cd633dd 100644 --- a/other/activity_selection.py +++ b/other/activity_selection.py @@ -16,7 +16,7 @@ def printMaxActivities(start, finish): >>> finish = [2, 4, 6, 7, 9, 9] >>> printMaxActivities(start, finish) The following activities are selected: - 0 1 3 4 + 0 1 3 4 """ n = len(finish) print("The following activities are selected:") diff --git a/other/game_of_life.py b/other/game_of_life.py index 651467969fad..09863993dc3a 100644 --- a/other/game_of_life.py +++ b/other/game_of_life.py @@ -52,7 +52,7 @@ def seed(canvas): def run(canvas): - """ This function runs the rules of game through all points, and changes their + """This function runs the rules of game through all points, and changes their status accordingly.(in the same canvas) @Args: -- diff --git a/other/least_recently_used.py b/other/least_recently_used.py index 1b901d544b8d..213339636469 100644 --- a/other/least_recently_used.py +++ b/other/least_recently_used.py @@ -12,7 +12,7 @@ class LRUCache: @abstractmethod def __init__(self, n: int): - """ Creates an empty store and map for the keys. + """Creates an empty store and map for the keys. The LRUCache is set the size n. """ self.dq_store = deque() @@ -26,9 +26,9 @@ def __init__(self, n: int): def refer(self, x): """ - Looks for a page in the cache store and adds reference to the set. - Remove the least recently used key if the store is full. - Update store to reflect recent access. + Looks for a page in the cache store and adds reference to the set. + Remove the least recently used key if the store is full. + Update store to reflect recent access. """ if x not in self.key_reference_map: if len(self.dq_store) == LRUCache._MAX_CAPACITY: @@ -47,7 +47,7 @@ def refer(self, x): def display(self): """ - Prints all the elements in the store. + Prints all the elements in the store. """ for k in self.dq_store: print(k) diff --git a/other/magicdiamondpattern.py b/other/magicdiamondpattern.py index 37b5e4809f47..71bc50b51fc2 100644 --- a/other/magicdiamondpattern.py +++ b/other/magicdiamondpattern.py @@ -6,7 +6,7 @@ def floyd(n): """ Parameters: n : size of pattern - """ + """ for i in range(0, n): for j in range(0, n - i - 1): # printing spaces print(" ", end="") @@ -20,7 +20,7 @@ def reverse_floyd(n): """ Parameters: n : size of pattern - """ + """ for i in range(n, 0, -1): for j in range(i, 0, -1): # printing stars print("* ", end="") @@ -34,7 +34,7 @@ def pretty_print(n): """ Parameters: n : size of pattern - """ + """ if n <= 0: print(" ... .... nothing printing :(") return diff --git a/other/primelib.py b/other/primelib.py index a6d1d7dfb324..37883d9cf591 100644 --- a/other/primelib.py +++ b/other/primelib.py @@ -43,8 +43,8 @@ def isPrime(number): """ - input: positive integer 'number' - returns true if 'number' is prime otherwise false. + input: positive integer 'number' + returns true if 'number' is prime otherwise false. """ # precondition @@ -77,11 +77,11 @@ def isPrime(number): def sieveEr(N): """ - input: positive integer 'N' > 2 - returns a list of prime numbers from 2 up to N. + input: positive integer 'N' > 2 + returns a list of prime numbers from 2 up to N. - This function implements the algorithm called - sieve of erathostenes. + This function implements the algorithm called + sieve of erathostenes. """ @@ -115,9 +115,9 @@ def sieveEr(N): def getPrimeNumbers(N): """ - input: positive integer 'N' > 2 - returns a list of prime numbers from 2 up to N (inclusive) - This function is more efficient as function 'sieveEr(...)' + input: positive integer 'N' > 2 + returns a list of prime numbers from 2 up to N (inclusive) + This function is more efficient as function 'sieveEr(...)' """ # precondition @@ -144,8 +144,8 @@ def getPrimeNumbers(N): def primeFactorization(number): """ - input: positive integer 'number' - returns a list of the prime number factors of 'number' + input: positive integer 'number' + returns a list of the prime number factors of 'number' """ # precondition @@ -188,8 +188,8 @@ def primeFactorization(number): def greatestPrimeFactor(number): """ - input: positive integer 'number' >= 0 - returns the greatest prime number factor of 'number' + input: positive integer 'number' >= 0 + returns the greatest prime number factor of 'number' """ # precondition @@ -215,8 +215,8 @@ def greatestPrimeFactor(number): def smallestPrimeFactor(number): """ - input: integer 'number' >= 0 - returns the smallest prime number factor of 'number' + input: integer 'number' >= 0 + returns the smallest prime number factor of 'number' """ # precondition @@ -242,8 +242,8 @@ def smallestPrimeFactor(number): def isEven(number): """ - input: integer 'number' - returns true if 'number' is even, otherwise false. + input: integer 'number' + returns true if 'number' is even, otherwise false. """ # precondition @@ -258,8 +258,8 @@ def isEven(number): def isOdd(number): """ - input: integer 'number' - returns true if 'number' is odd, otherwise false. + input: integer 'number' + returns true if 'number' is odd, otherwise false. """ # precondition @@ -274,9 +274,9 @@ def isOdd(number): def goldbach(number): """ - Goldbach's assumption - input: a even positive integer 'number' > 2 - returns a list of two prime numbers whose sum is equal to 'number' + Goldbach's assumption + input: a even positive integer 'number' > 2 + returns a list of two prime numbers whose sum is equal to 'number' """ # precondition @@ -329,9 +329,9 @@ def goldbach(number): def gcd(number1, number2): """ - Greatest common divisor - input: two positive integer 'number1' and 'number2' - returns the greatest common divisor of 'number1' and 'number2' + Greatest common divisor + input: two positive integer 'number1' and 'number2' + returns the greatest common divisor of 'number1' and 'number2' """ # precondition @@ -363,9 +363,9 @@ def gcd(number1, number2): def kgV(number1, number2): """ - Least common multiple - input: two positive integer 'number1' and 'number2' - returns the least common multiple of 'number1' and 'number2' + Least common multiple + input: two positive integer 'number1' and 'number2' + returns the least common multiple of 'number1' and 'number2' """ # precondition @@ -443,9 +443,9 @@ def kgV(number1, number2): def getPrime(n): """ - Gets the n-th prime number. - input: positive integer 'n' >= 0 - returns the n-th prime number, beginning at index 0 + Gets the n-th prime number. + input: positive integer 'n' >= 0 + returns the n-th prime number, beginning at index 0 """ # precondition @@ -478,10 +478,10 @@ def getPrime(n): def getPrimesBetween(pNumber1, pNumber2): """ - input: prime numbers 'pNumber1' and 'pNumber2' - pNumber1 < pNumber2 - returns a list of all prime numbers between 'pNumber1' (exclusive) - and 'pNumber2' (exclusive) + input: prime numbers 'pNumber1' and 'pNumber2' + pNumber1 < pNumber2 + returns a list of all prime numbers between 'pNumber1' (exclusive) + and 'pNumber2' (exclusive) """ # precondition @@ -522,8 +522,8 @@ def getPrimesBetween(pNumber1, pNumber2): def getDivisors(n): """ - input: positive integer 'n' >= 1 - returns all divisors of n (inclusive 1 and 'n') + input: positive integer 'n' >= 1 + returns all divisors of n (inclusive 1 and 'n') """ # precondition @@ -547,8 +547,8 @@ def getDivisors(n): def isPerfectNumber(number): """ - input: positive integer 'number' > 1 - returns true if 'number' is a perfect number otherwise false. + input: positive integer 'number' > 1 + returns true if 'number' is a perfect number otherwise false. """ # precondition @@ -574,9 +574,9 @@ def isPerfectNumber(number): def simplifyFraction(numerator, denominator): """ - input: two integer 'numerator' and 'denominator' - assumes: 'denominator' != 0 - returns: a tuple with simplify numerator and denominator. + input: two integer 'numerator' and 'denominator' + assumes: 'denominator' != 0 + returns: a tuple with simplify numerator and denominator. """ # precondition @@ -604,8 +604,8 @@ def simplifyFraction(numerator, denominator): def factorial(n): """ - input: positive integer 'n' - returns the factorial of 'n' (n!) + input: positive integer 'n' + returns the factorial of 'n' (n!) """ # precondition @@ -624,8 +624,8 @@ def factorial(n): def fib(n): """ - input: positive integer 'n' - returns the n-th fibonacci term , indexing by 0 + input: positive integer 'n' + returns the n-th fibonacci term , indexing by 0 """ # precondition diff --git a/project_euler/problem_09/sol2.py b/project_euler/problem_09/sol2.py index de7b12d40c09..d16835ca09a2 100644 --- a/project_euler/problem_09/sol2.py +++ b/project_euler/problem_09/sol2.py @@ -23,8 +23,7 @@ def solution(n): product = -1 d = 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): diff --git a/project_euler/problem_10/sol3.py b/project_euler/problem_10/sol3.py index e5bc0731d8ab..739aaa9f16bb 100644 --- a/project_euler/problem_10/sol3.py +++ b/project_euler/problem_10/sol3.py @@ -12,7 +12,7 @@ def prime_sum(n: int) -> int: - """ Returns the sum of all the primes below n. + """Returns the sum of all the primes below n. >>> prime_sum(2_000_000) 142913828922 diff --git a/project_euler/problem_15/sol1.py b/project_euler/problem_15/sol1.py index 1be7d10ed674..feeb3ddab57a 100644 --- a/project_euler/problem_15/sol1.py +++ b/project_euler/problem_15/sol1.py @@ -8,31 +8,31 @@ def lattice_paths(n): """ - Returns the number of paths possible in a n x n grid starting at top left - corner going to bottom right corner and being able to move right and down - only. - -bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 50 -1.008913445455642e+29 -bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 25 -126410606437752.0 -bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 23 -8233430727600.0 -bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 15 -155117520.0 -bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 1 -2.0 - - >>> lattice_paths(25) - 126410606437752 - >>> lattice_paths(23) - 8233430727600 - >>> lattice_paths(20) - 137846528820 - >>> lattice_paths(15) - 155117520 - >>> lattice_paths(1) - 2 + Returns the number of paths possible in a n x n grid starting at top left + corner going to bottom right corner and being able to move right and down + only. + + bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 50 + 1.008913445455642e+29 + bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 25 + 126410606437752.0 + bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 23 + 8233430727600.0 + bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 15 + 155117520.0 + bruno@bruno-laptop:~/git/Python/project_euler/problem_15$ python3 sol1.py 1 + 2.0 + + >>> lattice_paths(25) + 126410606437752 + >>> lattice_paths(23) + 8233430727600 + >>> lattice_paths(20) + 137846528820 + >>> lattice_paths(15) + 155117520 + >>> lattice_paths(1) + 2 """ n = 2 * n # middle entry of odd rows starting at row 3 is the solution for n = 1, diff --git a/project_euler/problem_27/problem_27_sol1.py b/project_euler/problem_27/problem_27_sol1.py index 84b007a0bc88..e4833574c509 100644 --- a/project_euler/problem_27/problem_27_sol1.py +++ b/project_euler/problem_27/problem_27_sol1.py @@ -39,17 +39,17 @@ def is_prime(k: int) -> bool: def solution(a_limit: int, b_limit: int) -> int: """ - >>> solution(1000, 1000) - -59231 - >>> solution(200, 1000) - -59231 - >>> solution(200, 200) - -4925 - >>> solution(-1000, 1000) - 0 - >>> solution(-1000, -1000) - 0 - """ + >>> solution(1000, 1000) + -59231 + >>> solution(200, 1000) + -59231 + >>> solution(200, 200) + -4925 + >>> solution(-1000, 1000) + 0 + >>> solution(-1000, -1000) + 0 + """ longest = [0, 0, 0] # length, a, b for a in range((a_limit * -1) + 1, a_limit): for b in range(2, b_limit): diff --git a/project_euler/problem_56/sol1.py b/project_euler/problem_56/sol1.py index 2a00fa6a195d..98094ea8eb28 100644 --- a/project_euler/problem_56/sol1.py +++ b/project_euler/problem_56/sol1.py @@ -1,18 +1,18 @@ def maximum_digital_sum(a: int, b: int) -> int: """ - Considering natural numbers of the form, a**b, where a, b < 100, - what is the maximum digital sum? - :param a: - :param b: - :return: - >>> maximum_digital_sum(10,10) - 45 + Considering natural numbers of the form, a**b, where a, b < 100, + what is the maximum digital sum? + :param a: + :param b: + :return: + >>> maximum_digital_sum(10,10) + 45 - >>> maximum_digital_sum(100,100) - 972 + >>> maximum_digital_sum(100,100) + 972 - >>> maximum_digital_sum(100,200) - 1872 + >>> maximum_digital_sum(100,200) + 1872 """ # RETURN the MAXIMUM from the list of SUMs of the list of INT converted from STR of diff --git a/sorts/merge_sort.py b/sorts/merge_sort.py index 572f38a57029..4da29f32a36d 100644 --- a/sorts/merge_sort.py +++ b/sorts/merge_sort.py @@ -29,11 +29,13 @@ def merge(left: list, right: list) -> list: :param right: right collection :return: merge result """ + def _merge(): while left and right: yield (left if left[0] <= right[0] else right).pop(0) yield from left yield from right + return list(_merge()) if len(collection) <= 1: @@ -44,6 +46,7 @@ def _merge(): if __name__ == "__main__": import doctest + doctest.testmod() user_input = input("Enter numbers separated by a comma:\n").strip() unsorted = [int(item) for item in user_input.split(",")] diff --git a/strings/boyer_moore_search.py b/strings/boyer_moore_search.py index f340855d7a05..9d32a6943906 100644 --- a/strings/boyer_moore_search.py +++ b/strings/boyer_moore_search.py @@ -25,7 +25,7 @@ def __init__(self, text, pattern): self.textLen, self.patLen = len(text), len(pattern) def match_in_pattern(self, char): - """ finds the index of char in pattern in reverse order + """finds the index of char in pattern in reverse order Parameters : char (chr): character to be searched diff --git a/traversals/binary_tree_traversals.py b/traversals/binary_tree_traversals.py index 7f100e66f200..5c0ba0f25f3a 100644 --- a/traversals/binary_tree_traversals.py +++ b/traversals/binary_tree_traversals.py @@ -53,7 +53,7 @@ def pre_order(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> pre_order(root) - 1 2 4 5 3 6 7 + 1 2 4 5 3 6 7 """ if not isinstance(node, TreeNode) or not node: return @@ -75,7 +75,7 @@ def in_order(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> in_order(root) - 4 2 5 1 6 3 7 + 4 2 5 1 6 3 7 """ if not isinstance(node, TreeNode) or not node: return @@ -97,7 +97,7 @@ def post_order(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> post_order(root) - 4 5 2 6 7 3 1 + 4 5 2 6 7 3 1 """ if not isinstance(node, TreeNode) or not node: return @@ -119,7 +119,7 @@ def level_order(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> level_order(root) - 1 2 3 4 5 6 7 + 1 2 3 4 5 6 7 """ if not isinstance(node, TreeNode) or not node: return @@ -146,10 +146,10 @@ def level_order_actual(node: TreeNode) -> None: >>> root.left, root.right = tree_node2, tree_node3 >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 - >>> level_order_actual(root) - 1 - 2 3 - 4 5 6 7 + >>> level_order_actual(root) + 1 + 2 3 + 4 5 6 7 """ if not isinstance(node, TreeNode) or not node: return @@ -183,7 +183,7 @@ def pre_order_iter(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> pre_order_iter(root) - 1 2 4 5 3 6 7 + 1 2 4 5 3 6 7 """ if not isinstance(node, TreeNode) or not node: return @@ -213,7 +213,7 @@ def in_order_iter(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> in_order_iter(root) - 4 2 5 1 6 3 7 + 4 2 5 1 6 3 7 """ if not isinstance(node, TreeNode) or not node: return @@ -241,7 +241,7 @@ def post_order_iter(node: TreeNode) -> None: >>> tree_node2.left, tree_node2.right = tree_node4 , tree_node5 >>> tree_node3.left, tree_node3.right = tree_node6 , tree_node7 >>> post_order_iter(root) - 4 5 2 6 7 3 1 + 4 5 2 6 7 3 1 """ if not isinstance(node, TreeNode) or not node: return From f0cd9d5428201fad4fd75d40019c92f33845bbf8 Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Thu, 26 Nov 2020 10:59:18 +0330 Subject: [PATCH 6/6] Merge branch 'master' into 'test/string-conversion' --- .gitattributes | 1 + .github/CODEOWNERS | 88 ++ .github/workflows/build.yml | 27 + .github/workflows/pre-commit.yml | 21 + .github/workflows/project_euler.yml | 31 + .pre-commit-config.yaml | 60 + arithmetic_analysis/__init__.py | 0 arithmetic_analysis/image_data/__init__.py | 0 backtracking/__init__.py | 0 bit_manipulation/__init__.py | 0 bit_manipulation/binary_and_operator.py | 52 + bit_manipulation/binary_count_setbits.py | 41 + .../binary_count_trailing_zeros.py | 44 + bit_manipulation/binary_xor_operator.py | 52 + .../single_bit_manipulation_operations.py | 80 ++ blockchain/__init__.py | 0 boolean_algebra/__init__.py | 0 cellular_automata/__init__.py | 0 ciphers/__init__.py | 0 ciphers/base64_encoding.py | 142 +++ ciphers/beaufort_cipher.py | 82 ++ ciphers/mono_alphabetic_ciphers.py | 59 + ciphers/rail_fence_cipher.py | 102 ++ compression/__init__.py | 0 compression/image_data/__init__.py | 0 computer_vision/__init__.py | 0 computer_vision/meanthreshold.py | 30 + conversions/__init__.py | 0 conversions/binary_to_decimal.py | 43 + conversions/binary_to_octal.py | 45 + conversions/decimal_to_binary_recursion.py | 53 + conversions/hexadecimal_to_decimal.py | 49 + conversions/molecular_chemistry.py | 92 ++ conversions/octal_to_decimal.py | 43 + data_structures/__init__.py | 0 data_structures/binary_tree/__init__.py | 0 .../binary_tree/binary_tree_mirror.py | 44 + .../binary_tree/binary_tree_traversals.py | 161 +++ data_structures/disjoint_set/__init__.py | 0 data_structures/hashing/__init__.py | 0 data_structures/heap/__init__.py | 0 data_structures/heap/randomized_heap.py | 188 ++++ data_structures/heap/skew_heap.py | 192 ++++ .../linked_list/doubly_linked_list_two.py | 253 +++++ data_structures/linked_list/has_loop.py | 60 + data_structures/queue/__init__.py | 0 .../queue/priority_queue_using_list.py | 232 ++++ .../stacks/evaluate_postfix_notations.py | 49 + data_structures/trie/__init__.py | 0 .../histogram_equalization/__init__.py | 0 .../image_data/__init__.py | 0 .../output_data/__init__.py | 0 .../image_data/__init__.py | 0 divide_and_conquer/__init__.py | 0 divide_and_conquer/heaps_algorithm.py | 56 + .../heaps_algorithm_iterative.py | 60 + divide_and_conquer/kth_order_statistic.py | 64 ++ divide_and_conquer/max_difference_pair.py | 47 + divide_and_conquer/peak.py | 53 + dynamic_programming/__init__.py | 0 electronics/ohms_law.py | 39 + file_transfer/__init__.py | 0 file_transfer/tests/__init__.py | 0 file_transfer/tests/test_send_file.py | 31 + fuzzy_logic/__init__.py | 0 genetic_algorithm/__init__.py | 0 geodesy/__init__.py | 0 graphics/__init__.py | 0 graphics/vector3_for_2d_rendering.py | 102 ++ graphs/__init__.py | 0 graphs/bfs_zero_one_shortest_path.py | 138 +++ graphs/minimum_spanning_tree_prims2.py | 271 +++++ .../tests/test_min_spanning_tree_kruskal.py | 36 + graphs/tests/test_min_spanning_tree_prim.py | 46 + greedy_method/__init__.py | 0 hashes/__init__.py | 0 images/__init__.py | 0 knapsack/README.md | 32 + knapsack/__init__.py | 0 knapsack/knapsack.py | 47 + knapsack/test_knapsack.py | 52 + linear_algebra/__init__.py | 0 linear_algebra/src/__init__.py | 0 linear_algebra/src/polynom_for_points.py | 132 +++ machine_learning/__init__.py | 0 machine_learning/forecasting/__init__.py | 0 machine_learning/forecasting/ex_data.csv | 114 ++ machine_learning/forecasting/run.py | 157 +++ .../gradient_boosting_regressor.py | 66 ++ machine_learning/lstm/__init__.py | 0 machine_learning/lstm/lstm_prediction.py_tf | 54 + machine_learning/similarity_search.py | 137 +++ maths/decimal_isolate.py | 45 + maths/images/__init__.py | 0 maths/series/__init__.py | 0 maths/sigmoid.py | 39 + matrix/__init__.py | 0 matrix/tests/__init__.py | 0 networking_flow/__init__.py | 0 neural_network/__init__.py | 0 other/__init__.py | 0 ...42\200\223logemann\342\200\223loveland.py" | 357 ++++++ other/doomsday.py | 59 + other/max_sum_sliding_window.py | 45 + other/median_of_two_arrays.py | 33 + other/two_pointer.py | 61 + project_euler/__init__.py | 0 project_euler/problem_001/__init__.py | 0 project_euler/problem_001/sol1.py | 33 + project_euler/problem_001/sol2.py | 38 + project_euler/problem_001/sol3.py | 64 ++ project_euler/problem_001/sol4.py | 52 + project_euler/problem_001/sol5.py | 32 + project_euler/problem_001/sol6.py | 39 + project_euler/problem_001/sol7.py | 37 + project_euler/problem_002/__init__.py | 0 project_euler/problem_002/sol1.py | 48 + project_euler/problem_002/sol2.py | 46 + project_euler/problem_002/sol3.py | 48 + project_euler/problem_002/sol4.py | 72 ++ project_euler/problem_002/sol5.py | 52 + project_euler/problem_003/__init__.py | 0 project_euler/problem_003/sol1.py | 104 ++ project_euler/problem_003/sol2.py | 64 ++ project_euler/problem_003/sol3.py | 66 ++ project_euler/problem_004/__init__.py | 0 project_euler/problem_004/sol1.py | 53 + project_euler/problem_004/sol2.py | 39 + project_euler/problem_005/__init__.py | 0 project_euler/problem_005/sol1.py | 69 ++ project_euler/problem_005/sol2.py | 75 ++ project_euler/problem_006/__init__.py | 0 project_euler/problem_006/sol1.py | 44 + project_euler/problem_006/sol2.py | 41 + project_euler/problem_006/sol3.py | 42 + project_euler/problem_006/sol4.py | 41 + project_euler/problem_007/__init__.py | 0 project_euler/problem_007/sol1.py | 76 ++ project_euler/problem_007/sol2.py | 88 ++ project_euler/problem_007/sol3.py | 68 ++ project_euler/problem_008/__init__.py | 0 project_euler/problem_008/sol1.py | 82 ++ project_euler/problem_008/sol2.py | 81 ++ project_euler/problem_008/sol3.py | 94 ++ project_euler/problem_009/__init__.py | 0 project_euler/problem_009/sol1.py | 79 ++ project_euler/problem_009/sol2.py | 48 + project_euler/problem_009/sol3.py | 41 + project_euler/problem_010/__init__.py | 0 project_euler/problem_010/sol1.py | 65 ++ project_euler/problem_010/sol2.py | 67 ++ project_euler/problem_010/sol3.py | 61 + project_euler/problem_011/__init__.py | 0 project_euler/problem_011/grid.txt | 20 + project_euler/problem_011/sol1.py | 88 ++ project_euler/problem_011/sol2.py | 75 ++ project_euler/problem_012/__init__.py | 0 project_euler/problem_012/sol1.py | 60 + project_euler/problem_012/sol2.py | 47 + project_euler/problem_013/__init__.py | 0 project_euler/problem_013/num.txt | 100 ++ project_euler/problem_013/sol1.py | 25 + project_euler/problem_014/__init__.py | 0 project_euler/problem_014/sol1.py | 60 + project_euler/problem_014/sol2.py | 62 + project_euler/problem_015/__init__.py | 0 project_euler/problem_015/sol1.py | 44 + project_euler/problem_016/__init__.py | 0 project_euler/problem_016/sol1.py | 36 + project_euler/problem_016/sol2.py | 30 + project_euler/problem_017/__init__.py | 0 project_euler/problem_017/sol1.py | 63 ++ project_euler/problem_018/__init__.py | 0 project_euler/problem_018/solution.py | 64 ++ project_euler/problem_018/triangle.txt | 15 + project_euler/problem_019/__init__.py | 0 project_euler/problem_019/sol1.py | 64 ++ project_euler/problem_020/__init__.py | 0 project_euler/problem_020/sol1.py | 54 + project_euler/problem_020/sol2.py | 35 + project_euler/problem_020/sol3.py | 41 + project_euler/problem_020/sol4.py | 42 + project_euler/problem_021/__init__.py | 0 project_euler/problem_021/sol1.py | 54 + project_euler/problem_022/__init__.py | 0 project_euler/problem_022/p022_names.txt | 1 + project_euler/problem_022/sol1.py | 45 + project_euler/problem_022/sol2.py | 42 + project_euler/problem_023/__init__.py | 0 project_euler/problem_023/sol1.py | 52 + project_euler/problem_024/__init__.py | 0 project_euler/problem_024/sol1.py | 27 + project_euler/problem_025/__init__.py | 0 project_euler/problem_025/sol1.py | 101 ++ project_euler/problem_025/sol2.py | 71 ++ project_euler/problem_025/sol3.py | 56 + project_euler/problem_026/__init__.py | 0 project_euler/problem_026/sol1.py | 60 + project_euler/problem_027/__init__.py | 0 project_euler/problem_027/sol1.py | 74 ++ project_euler/problem_028/__init__.py | 0 project_euler/problem_028/sol1.py | 58 + project_euler/problem_029/__init__.py | 0 project_euler/problem_029/sol1.py | 50 + project_euler/problem_030/__init__.py | 0 project_euler/problem_030/sol1.py | 39 + project_euler/problem_031/__init__.py | 0 project_euler/problem_031/sol1.py | 65 ++ project_euler/problem_031/sol2.py | 59 + project_euler/problem_032/__init__.py | 0 project_euler/problem_032/sol32.py | 58 + project_euler/problem_033/__init__.py | 0 project_euler/problem_033/sol1.py | 68 ++ project_euler/problem_034/__init__.py | 1 + project_euler/problem_034/sol1.py | 36 + project_euler/problem_035/__init__.py | 1 + project_euler/problem_035/sol1.py | 82 ++ project_euler/problem_036/__init__.py | 0 project_euler/problem_036/sol1.py | 69 ++ project_euler/problem_037/__init__.py | 1 + project_euler/problem_037/sol1.py | 98 ++ project_euler/problem_038/__init__.py | 0 project_euler/problem_038/sol1.py | 77 ++ project_euler/problem_039/__init__.py | 1 + project_euler/problem_039/sol1.py | 55 + project_euler/problem_040/__init__.py | 0 project_euler/problem_040/sol1.py | 45 + project_euler/problem_041/__init__.py | 1 + project_euler/problem_041/sol1.py | 56 + project_euler/problem_042/__init__.py | 0 project_euler/problem_042/solution42.py | 47 + project_euler/problem_042/words.txt | 1 + project_euler/problem_043/__init__.py | 1 + project_euler/problem_043/sol1.py | 60 + project_euler/problem_044/__init__.py | 1 + project_euler/problem_044/sol1.py | 47 + project_euler/problem_045/__init__.py | 1 + project_euler/problem_045/sol1.py | 59 + project_euler/problem_046/__init__.py | 1 + project_euler/problem_046/sol1.py | 95 ++ project_euler/problem_047/__init__.py | 0 project_euler/problem_047/sol1.py | 112 ++ project_euler/problem_048/__init__.py | 0 project_euler/problem_048/sol1.py | 25 + project_euler/problem_049/__init__.py | 0 project_euler/problem_049/sol1.py | 139 +++ project_euler/problem_050/__init__.py | 0 project_euler/problem_050/sol1.py | 85 ++ project_euler/problem_051/__init__.py | 0 project_euler/problem_051/sol1.py | 111 ++ project_euler/problem_052/__init__.py | 0 project_euler/problem_052/sol1.py | 37 + project_euler/problem_053/__init__.py | 0 project_euler/problem_053/sol1.py | 43 + project_euler/problem_054/__init__.py | 0 project_euler/problem_054/poker_hands.txt | 1000 +++++++++++++++++ project_euler/problem_054/sol1.py | 381 +++++++ project_euler/problem_054/test_poker_hand.py | 228 ++++ project_euler/problem_055/__init__.py | 1 + project_euler/problem_055/sol1.py | 81 ++ project_euler/problem_056/__init__.py | 0 project_euler/problem_056/sol1.py | 45 + project_euler/problem_057/__init__.py | 0 project_euler/problem_057/sol1.py | 48 + project_euler/problem_058/__init__.py | 1 + project_euler/problem_058/sol1.py | 86 ++ project_euler/problem_062/__init__.py | 0 project_euler/problem_062/sol1.py | 62 + project_euler/problem_063/__init__.py | 1 + project_euler/problem_063/sol1.py | 34 + project_euler/problem_064/__init__.py | 0 project_euler/problem_064/sol1.py | 77 ++ project_euler/problem_065/__init__.py | 0 project_euler/problem_065/sol1.py | 99 ++ project_euler/problem_067/__init__.py | 0 project_euler/problem_067/sol1.py | 49 + project_euler/problem_067/triangle.txt | 100 ++ project_euler/problem_069/__init__.py | 0 project_euler/problem_069/sol1.py | 66 ++ project_euler/problem_070/__init__.py | 0 project_euler/problem_070/sol1.py | 119 ++ project_euler/problem_071/__init__.py | 0 project_euler/problem_071/sol1.py | 48 + project_euler/problem_072/__init__.py | 0 project_euler/problem_072/sol1.py | 46 + project_euler/problem_072/sol2.py | 45 + project_euler/problem_074/__init__.py | 0 project_euler/problem_074/sol1.py | 111 ++ project_euler/problem_074/sol2.py | 126 +++ project_euler/problem_075/__init__.py | 0 project_euler/problem_075/sol1.py | 60 + project_euler/problem_076/__init__.py | 0 project_euler/problem_076/sol1.py | 55 + project_euler/problem_080/__init__.py | 0 project_euler/problem_080/sol1.py | 37 + project_euler/problem_081/__init__.py | 0 project_euler/problem_081/matrix.txt | 80 ++ project_euler/problem_081/sol1.py | 47 + project_euler/problem_087/__init__.py | 0 project_euler/problem_087/sol1.py | 52 + 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 +++ project_euler/problem_091/__init__.py | 0 project_euler/problem_091/sol1.py | 59 + project_euler/problem_097/__init__.py | 1 + project_euler/problem_097/sol1.py | 46 + project_euler/problem_099/__init__.py | 0 project_euler/problem_099/base_exp.txt | 1000 +++++++++++++++++ project_euler/problem_099/sol1.py | 34 + project_euler/problem_112/__init__.py | 0 project_euler/problem_112/sol1.py | 89 ++ project_euler/problem_113/__init__.py | 0 project_euler/problem_113/sol1.py | 75 ++ project_euler/problem_119/__init__.py | 0 project_euler/problem_119/sol1.py | 51 + project_euler/problem_120/__init__.py | 0 project_euler/problem_120/sol1.py | 32 + project_euler/problem_123/__init__.py | 0 project_euler/problem_123/sol1.py | 99 ++ project_euler/problem_125/__init__.py | 0 project_euler/problem_125/sol1.py | 56 + project_euler/problem_129/__init__.py | 0 project_euler/problem_129/sol1.py | 57 + project_euler/problem_173/__init__.py | 0 project_euler/problem_173/sol1.py | 41 + project_euler/problem_174/__init__.py | 0 project_euler/problem_174/sol1.py | 52 + project_euler/problem_188/__init__.py | 0 project_euler/problem_188/sol1.py | 68 ++ project_euler/problem_191/__init__.py | 0 project_euler/problem_191/sol1.py | 105 ++ project_euler/problem_203/__init__.py | 0 project_euler/problem_203/sol1.py | 188 ++++ project_euler/problem_206/__init__.py | 0 project_euler/problem_206/sol1.py | 74 ++ project_euler/problem_207/__init__.py | 0 project_euler/problem_207/sol1.py | 98 ++ project_euler/problem_301/__init__.py | 0 project_euler/problem_301/sol1.py | 58 + quantum/__init__.py | 0 quantum/deutsch_jozsa.py | 122 ++ quantum/half_adder.py | 59 + quantum/not_gate.py | 41 + quantum/quantum_entanglement.py | 58 + quantum/ripple_adder_classic.py | 108 ++ quantum/single_qubit_measure.py | 34 + scheduling/__init__.py | 0 scripts/__init__.py | 0 scripts/project_euler_answers.json | 727 ++++++++++++ scripts/validate_solutions.py | 52 + searches/__init__.py | 0 searches/double_linear_search_recursion.py | 35 + searches/simple_binary_search.py | 59 + sorts/__init__.py | 0 sorts/intro_sort.py | 173 +++ sorts/natural_sort.py | 36 + sorts/patience_sort.py | 64 ++ sorts/recursive_quick_sort.py | 22 + strings/__init__.py | 0 strings/aho_corasick.py | 93 ++ .../can_string_be_rearranged_as_palindrome.py | 115 ++ strings/capitalize.py | 27 + strings/reverse_letters.py | 19 + strings/swap_case.py | 37 + traversals/__init__.py | 0 web_programming/__init__.py | 0 web_programming/co2_emission.py | 25 + web_programming/covid_stats_via_xpath.py | 23 + .../crawl_google_scholar_citation.py | 32 + web_programming/currency_converter.py | 192 ++++ web_programming/instagram_crawler.py | 140 +++ web_programming/instagram_pic.py | 16 + web_programming/recaptcha_verification.py | 66 ++ web_programming/test_fetch_github_info.py | 27 + 376 files changed, 19248 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .github/workflows/project_euler.yml create mode 100644 .pre-commit-config.yaml create mode 100644 arithmetic_analysis/__init__.py create mode 100644 arithmetic_analysis/image_data/__init__.py create mode 100644 backtracking/__init__.py create mode 100644 bit_manipulation/__init__.py create mode 100644 bit_manipulation/binary_and_operator.py create mode 100644 bit_manipulation/binary_count_setbits.py create mode 100644 bit_manipulation/binary_count_trailing_zeros.py create mode 100644 bit_manipulation/binary_xor_operator.py create mode 100644 bit_manipulation/single_bit_manipulation_operations.py create mode 100644 blockchain/__init__.py create mode 100644 boolean_algebra/__init__.py create mode 100644 cellular_automata/__init__.py create mode 100644 ciphers/__init__.py create mode 100644 ciphers/base64_encoding.py create mode 100644 ciphers/beaufort_cipher.py create mode 100644 ciphers/mono_alphabetic_ciphers.py create mode 100644 ciphers/rail_fence_cipher.py create mode 100644 compression/__init__.py create mode 100644 compression/image_data/__init__.py create mode 100644 computer_vision/__init__.py create mode 100644 computer_vision/meanthreshold.py create mode 100644 conversions/__init__.py create mode 100644 conversions/binary_to_decimal.py create mode 100644 conversions/binary_to_octal.py create mode 100644 conversions/decimal_to_binary_recursion.py create mode 100644 conversions/hexadecimal_to_decimal.py create mode 100644 conversions/molecular_chemistry.py create mode 100644 conversions/octal_to_decimal.py create mode 100644 data_structures/__init__.py create mode 100644 data_structures/binary_tree/__init__.py create mode 100644 data_structures/binary_tree/binary_tree_mirror.py create mode 100644 data_structures/binary_tree/binary_tree_traversals.py create mode 100644 data_structures/disjoint_set/__init__.py create mode 100644 data_structures/hashing/__init__.py create mode 100644 data_structures/heap/__init__.py create mode 100644 data_structures/heap/randomized_heap.py create mode 100644 data_structures/heap/skew_heap.py create mode 100644 data_structures/linked_list/doubly_linked_list_two.py create mode 100644 data_structures/linked_list/has_loop.py create mode 100644 data_structures/queue/__init__.py create mode 100644 data_structures/queue/priority_queue_using_list.py create mode 100644 data_structures/stacks/evaluate_postfix_notations.py create mode 100644 data_structures/trie/__init__.py create mode 100644 digital_image_processing/histogram_equalization/__init__.py create mode 100644 digital_image_processing/histogram_equalization/image_data/__init__.py create mode 100644 digital_image_processing/histogram_equalization/output_data/__init__.py create mode 100644 digital_image_processing/image_data/__init__.py create mode 100644 divide_and_conquer/__init__.py create mode 100644 divide_and_conquer/heaps_algorithm.py create mode 100644 divide_and_conquer/heaps_algorithm_iterative.py create mode 100644 divide_and_conquer/kth_order_statistic.py create mode 100644 divide_and_conquer/max_difference_pair.py create mode 100644 divide_and_conquer/peak.py create mode 100644 dynamic_programming/__init__.py create mode 100644 electronics/ohms_law.py create mode 100644 file_transfer/__init__.py create mode 100644 file_transfer/tests/__init__.py create mode 100644 file_transfer/tests/test_send_file.py create mode 100644 fuzzy_logic/__init__.py create mode 100644 genetic_algorithm/__init__.py create mode 100644 geodesy/__init__.py create mode 100644 graphics/__init__.py create mode 100644 graphics/vector3_for_2d_rendering.py create mode 100644 graphs/__init__.py create mode 100644 graphs/bfs_zero_one_shortest_path.py create mode 100644 graphs/minimum_spanning_tree_prims2.py create mode 100644 graphs/tests/test_min_spanning_tree_kruskal.py create mode 100644 graphs/tests/test_min_spanning_tree_prim.py create mode 100644 greedy_method/__init__.py create mode 100644 hashes/__init__.py create mode 100644 images/__init__.py 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 create mode 100644 linear_algebra/__init__.py create mode 100644 linear_algebra/src/__init__.py create mode 100644 linear_algebra/src/polynom_for_points.py create mode 100644 machine_learning/__init__.py 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 create mode 100644 machine_learning/gradient_boosting_regressor.py create mode 100644 machine_learning/lstm/__init__.py create mode 100644 machine_learning/lstm/lstm_prediction.py_tf create mode 100644 machine_learning/similarity_search.py create mode 100644 maths/decimal_isolate.py create mode 100644 maths/images/__init__.py create mode 100644 maths/series/__init__.py create mode 100644 maths/sigmoid.py create mode 100644 matrix/__init__.py create mode 100644 matrix/tests/__init__.py create mode 100644 networking_flow/__init__.py create mode 100644 neural_network/__init__.py create mode 100644 other/__init__.py create mode 100644 "other/davis\342\200\223putnam\342\200\223logemann\342\200\223loveland.py" create mode 100644 other/doomsday.py create mode 100644 other/max_sum_sliding_window.py create mode 100644 other/median_of_two_arrays.py create mode 100644 other/two_pointer.py create mode 100644 project_euler/__init__.py create mode 100644 project_euler/problem_001/__init__.py create mode 100644 project_euler/problem_001/sol1.py create mode 100644 project_euler/problem_001/sol2.py create mode 100644 project_euler/problem_001/sol3.py create mode 100644 project_euler/problem_001/sol4.py create mode 100644 project_euler/problem_001/sol5.py create mode 100644 project_euler/problem_001/sol6.py create mode 100644 project_euler/problem_001/sol7.py create mode 100644 project_euler/problem_002/__init__.py create mode 100644 project_euler/problem_002/sol1.py create mode 100644 project_euler/problem_002/sol2.py create mode 100644 project_euler/problem_002/sol3.py create mode 100644 project_euler/problem_002/sol4.py create mode 100644 project_euler/problem_002/sol5.py create mode 100644 project_euler/problem_003/__init__.py create mode 100644 project_euler/problem_003/sol1.py create mode 100644 project_euler/problem_003/sol2.py create mode 100644 project_euler/problem_003/sol3.py create mode 100644 project_euler/problem_004/__init__.py create mode 100644 project_euler/problem_004/sol1.py create mode 100644 project_euler/problem_004/sol2.py create mode 100644 project_euler/problem_005/__init__.py create mode 100644 project_euler/problem_005/sol1.py create mode 100644 project_euler/problem_005/sol2.py create mode 100644 project_euler/problem_006/__init__.py create mode 100644 project_euler/problem_006/sol1.py create mode 100644 project_euler/problem_006/sol2.py create mode 100644 project_euler/problem_006/sol3.py create mode 100644 project_euler/problem_006/sol4.py create mode 100644 project_euler/problem_007/__init__.py create mode 100644 project_euler/problem_007/sol1.py create mode 100644 project_euler/problem_007/sol2.py create mode 100644 project_euler/problem_007/sol3.py create mode 100644 project_euler/problem_008/__init__.py create mode 100644 project_euler/problem_008/sol1.py create mode 100644 project_euler/problem_008/sol2.py create mode 100644 project_euler/problem_008/sol3.py create mode 100644 project_euler/problem_009/__init__.py create mode 100644 project_euler/problem_009/sol1.py create mode 100644 project_euler/problem_009/sol2.py create mode 100644 project_euler/problem_009/sol3.py create mode 100644 project_euler/problem_010/__init__.py create mode 100644 project_euler/problem_010/sol1.py create mode 100644 project_euler/problem_010/sol2.py create mode 100644 project_euler/problem_010/sol3.py create mode 100644 project_euler/problem_011/__init__.py create mode 100644 project_euler/problem_011/grid.txt create mode 100644 project_euler/problem_011/sol1.py create mode 100644 project_euler/problem_011/sol2.py create mode 100644 project_euler/problem_012/__init__.py create mode 100644 project_euler/problem_012/sol1.py create mode 100644 project_euler/problem_012/sol2.py create mode 100644 project_euler/problem_013/__init__.py create mode 100644 project_euler/problem_013/num.txt create mode 100644 project_euler/problem_013/sol1.py create mode 100644 project_euler/problem_014/__init__.py create mode 100644 project_euler/problem_014/sol1.py create mode 100644 project_euler/problem_014/sol2.py create mode 100644 project_euler/problem_015/__init__.py create mode 100644 project_euler/problem_015/sol1.py create mode 100644 project_euler/problem_016/__init__.py create mode 100644 project_euler/problem_016/sol1.py create mode 100644 project_euler/problem_016/sol2.py create mode 100644 project_euler/problem_017/__init__.py create mode 100644 project_euler/problem_017/sol1.py create mode 100644 project_euler/problem_018/__init__.py create mode 100644 project_euler/problem_018/solution.py create mode 100644 project_euler/problem_018/triangle.txt create mode 100644 project_euler/problem_019/__init__.py create mode 100644 project_euler/problem_019/sol1.py create mode 100644 project_euler/problem_020/__init__.py create mode 100644 project_euler/problem_020/sol1.py create mode 100644 project_euler/problem_020/sol2.py create mode 100644 project_euler/problem_020/sol3.py create mode 100644 project_euler/problem_020/sol4.py create mode 100644 project_euler/problem_021/__init__.py create mode 100644 project_euler/problem_021/sol1.py create mode 100644 project_euler/problem_022/__init__.py create mode 100644 project_euler/problem_022/p022_names.txt create mode 100644 project_euler/problem_022/sol1.py create mode 100644 project_euler/problem_022/sol2.py create mode 100644 project_euler/problem_023/__init__.py create mode 100644 project_euler/problem_023/sol1.py create mode 100644 project_euler/problem_024/__init__.py create mode 100644 project_euler/problem_024/sol1.py create mode 100644 project_euler/problem_025/__init__.py create mode 100644 project_euler/problem_025/sol1.py create mode 100644 project_euler/problem_025/sol2.py create mode 100644 project_euler/problem_025/sol3.py create mode 100644 project_euler/problem_026/__init__.py create mode 100644 project_euler/problem_026/sol1.py create mode 100644 project_euler/problem_027/__init__.py create mode 100644 project_euler/problem_027/sol1.py create mode 100644 project_euler/problem_028/__init__.py create mode 100644 project_euler/problem_028/sol1.py create mode 100644 project_euler/problem_029/__init__.py create mode 100644 project_euler/problem_029/sol1.py create mode 100644 project_euler/problem_030/__init__.py create mode 100644 project_euler/problem_030/sol1.py create mode 100644 project_euler/problem_031/__init__.py create mode 100644 project_euler/problem_031/sol1.py create mode 100644 project_euler/problem_031/sol2.py create mode 100644 project_euler/problem_032/__init__.py create mode 100644 project_euler/problem_032/sol32.py create mode 100644 project_euler/problem_033/__init__.py create mode 100644 project_euler/problem_033/sol1.py create mode 100644 project_euler/problem_034/__init__.py create mode 100644 project_euler/problem_034/sol1.py create mode 100644 project_euler/problem_035/__init__.py create mode 100644 project_euler/problem_035/sol1.py create mode 100644 project_euler/problem_036/__init__.py create mode 100644 project_euler/problem_036/sol1.py create mode 100644 project_euler/problem_037/__init__.py create mode 100644 project_euler/problem_037/sol1.py create mode 100644 project_euler/problem_038/__init__.py create mode 100644 project_euler/problem_038/sol1.py create mode 100644 project_euler/problem_039/__init__.py create mode 100644 project_euler/problem_039/sol1.py create mode 100644 project_euler/problem_040/__init__.py create mode 100644 project_euler/problem_040/sol1.py create mode 100644 project_euler/problem_041/__init__.py create mode 100644 project_euler/problem_041/sol1.py create mode 100644 project_euler/problem_042/__init__.py create mode 100644 project_euler/problem_042/solution42.py create mode 100644 project_euler/problem_042/words.txt create mode 100644 project_euler/problem_043/__init__.py create mode 100644 project_euler/problem_043/sol1.py create mode 100644 project_euler/problem_044/__init__.py create mode 100644 project_euler/problem_044/sol1.py create mode 100644 project_euler/problem_045/__init__.py create mode 100644 project_euler/problem_045/sol1.py create mode 100644 project_euler/problem_046/__init__.py create mode 100644 project_euler/problem_046/sol1.py create mode 100644 project_euler/problem_047/__init__.py create mode 100644 project_euler/problem_047/sol1.py create mode 100644 project_euler/problem_048/__init__.py create mode 100644 project_euler/problem_048/sol1.py create mode 100644 project_euler/problem_049/__init__.py create mode 100644 project_euler/problem_049/sol1.py create mode 100644 project_euler/problem_050/__init__.py create mode 100644 project_euler/problem_050/sol1.py create mode 100644 project_euler/problem_051/__init__.py create mode 100644 project_euler/problem_051/sol1.py create mode 100644 project_euler/problem_052/__init__.py create mode 100644 project_euler/problem_052/sol1.py create mode 100644 project_euler/problem_053/__init__.py create mode 100644 project_euler/problem_053/sol1.py create mode 100644 project_euler/problem_054/__init__.py create mode 100644 project_euler/problem_054/poker_hands.txt create mode 100644 project_euler/problem_054/sol1.py create mode 100644 project_euler/problem_054/test_poker_hand.py create mode 100644 project_euler/problem_055/__init__.py create mode 100644 project_euler/problem_055/sol1.py create mode 100644 project_euler/problem_056/__init__.py create mode 100644 project_euler/problem_056/sol1.py create mode 100644 project_euler/problem_057/__init__.py create mode 100644 project_euler/problem_057/sol1.py create mode 100644 project_euler/problem_058/__init__.py create mode 100644 project_euler/problem_058/sol1.py create mode 100644 project_euler/problem_062/__init__.py create mode 100644 project_euler/problem_062/sol1.py create mode 100644 project_euler/problem_063/__init__.py create mode 100644 project_euler/problem_063/sol1.py create mode 100644 project_euler/problem_064/__init__.py create mode 100644 project_euler/problem_064/sol1.py create mode 100644 project_euler/problem_065/__init__.py create mode 100644 project_euler/problem_065/sol1.py create mode 100644 project_euler/problem_067/__init__.py create mode 100644 project_euler/problem_067/sol1.py create mode 100644 project_euler/problem_067/triangle.txt create mode 100644 project_euler/problem_069/__init__.py create mode 100644 project_euler/problem_069/sol1.py create mode 100644 project_euler/problem_070/__init__.py create mode 100644 project_euler/problem_070/sol1.py create mode 100644 project_euler/problem_071/__init__.py create mode 100644 project_euler/problem_071/sol1.py create mode 100644 project_euler/problem_072/__init__.py create mode 100644 project_euler/problem_072/sol1.py create mode 100644 project_euler/problem_072/sol2.py create mode 100644 project_euler/problem_074/__init__.py create mode 100644 project_euler/problem_074/sol1.py create mode 100644 project_euler/problem_074/sol2.py create mode 100644 project_euler/problem_075/__init__.py create mode 100644 project_euler/problem_075/sol1.py create mode 100644 project_euler/problem_076/__init__.py create mode 100644 project_euler/problem_076/sol1.py create mode 100644 project_euler/problem_080/__init__.py create mode 100644 project_euler/problem_080/sol1.py 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 create mode 100644 project_euler/problem_087/__init__.py create mode 100644 project_euler/problem_087/sol1.py 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 create mode 100644 project_euler/problem_091/__init__.py create mode 100644 project_euler/problem_091/sol1.py create mode 100644 project_euler/problem_097/__init__.py create mode 100644 project_euler/problem_097/sol1.py create mode 100644 project_euler/problem_099/__init__.py create mode 100644 project_euler/problem_099/base_exp.txt create mode 100644 project_euler/problem_099/sol1.py create mode 100644 project_euler/problem_112/__init__.py create mode 100644 project_euler/problem_112/sol1.py create mode 100644 project_euler/problem_113/__init__.py create mode 100644 project_euler/problem_113/sol1.py create mode 100644 project_euler/problem_119/__init__.py create mode 100644 project_euler/problem_119/sol1.py create mode 100644 project_euler/problem_120/__init__.py create mode 100644 project_euler/problem_120/sol1.py create mode 100644 project_euler/problem_123/__init__.py create mode 100644 project_euler/problem_123/sol1.py create mode 100644 project_euler/problem_125/__init__.py create mode 100644 project_euler/problem_125/sol1.py create mode 100644 project_euler/problem_129/__init__.py create mode 100644 project_euler/problem_129/sol1.py create mode 100644 project_euler/problem_173/__init__.py create mode 100644 project_euler/problem_173/sol1.py create mode 100644 project_euler/problem_174/__init__.py create mode 100644 project_euler/problem_174/sol1.py create mode 100644 project_euler/problem_188/__init__.py create mode 100644 project_euler/problem_188/sol1.py create mode 100644 project_euler/problem_191/__init__.py create mode 100644 project_euler/problem_191/sol1.py create mode 100644 project_euler/problem_203/__init__.py create mode 100644 project_euler/problem_203/sol1.py create mode 100644 project_euler/problem_206/__init__.py create mode 100644 project_euler/problem_206/sol1.py create mode 100644 project_euler/problem_207/__init__.py create mode 100644 project_euler/problem_207/sol1.py create mode 100644 project_euler/problem_301/__init__.py create mode 100644 project_euler/problem_301/sol1.py create mode 100644 quantum/__init__.py create mode 100755 quantum/deutsch_jozsa.py create mode 100755 quantum/half_adder.py create mode 100644 quantum/not_gate.py create mode 100644 quantum/quantum_entanglement.py create mode 100644 quantum/ripple_adder_classic.py create mode 100755 quantum/single_qubit_measure.py create mode 100644 scheduling/__init__.py create mode 100644 scripts/__init__.py create mode 100644 scripts/project_euler_answers.json create mode 100755 scripts/validate_solutions.py create mode 100644 searches/__init__.py create mode 100644 searches/double_linear_search_recursion.py create mode 100644 searches/simple_binary_search.py create mode 100644 sorts/__init__.py create mode 100644 sorts/intro_sort.py create mode 100644 sorts/natural_sort.py create mode 100644 sorts/patience_sort.py create mode 100644 sorts/recursive_quick_sort.py create mode 100644 strings/__init__.py create mode 100644 strings/aho_corasick.py create mode 100644 strings/can_string_be_rearranged_as_palindrome.py create mode 100644 strings/capitalize.py create mode 100644 strings/reverse_letters.py create mode 100644 strings/swap_case.py create mode 100644 traversals/__init__.py create mode 100644 web_programming/__init__.py create mode 100644 web_programming/co2_emission.py create mode 100644 web_programming/covid_stats_via_xpath.py create mode 100644 web_programming/crawl_google_scholar_citation.py create mode 100644 web_programming/currency_converter.py create mode 100644 web_programming/instagram_crawler.py create mode 100644 web_programming/instagram_pic.py create mode 100644 web_programming/recaptcha_verification.py create mode 100644 web_programming/test_fetch_github_info.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..176a458f94e0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..260b9704eda7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,88 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# More details are here: https://help.github.com/articles/about-codeowners/ + +# The '*' pattern is global owners. + +# Order is important. The last matching pattern has the most precedence. + +/.* @cclauss @dhruvmanila + +# /arithmetic_analysis/ + +# /backtracking/ + +# /bit_manipulation/ + +# /blockchain/ + +# /boolean_algebra/ + +# /cellular_automata/ + +# /ciphers/ @cclauss # TODO: Uncomment this line after Hacktoberfest + +# /compression/ + +# /computer_vision/ + +# /conversions/ @cclauss # TODO: Uncomment this line after Hacktoberfest + +# /data_structures/ @cclauss # TODO: Uncomment this line after Hacktoberfest + +/digital_image_processing/ @mateuszz0000 + +# /divide_and_conquer/ + +/dynamic_programming/ @Kush1101 + +# /file_transfer/ + +# /fuzzy_logic/ + +# /genetic_algorithm/ + +# /geodesy/ + +# /graphics/ + +# /graphs/ + +# /greedy_method/ + +# /hashes/ + +# /images/ + +# /linear_algebra/ + +# /machine_learning/ + +/maths/ @Kush1101 + +# /matrix/ + +# /networking_flow/ + +# /neural_network/ + +# /other/ @cclauss # TODO: Uncomment this line after Hacktoberfest + +/project_euler/ @dhruvmanila @Kush1101 + +# /quantum/ + +# /scheduling/ + +# /scripts/ + +# /searches/ + +/sorts/ @mateuszz0000 + +# /strings/ @cclauss # TODO: Uncomment this line after Hacktoberfest + +# /traversals/ + +/web_programming/ @cclauss diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000000..ae9b4e36b1ce --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: "build" + +on: + pull_request: + schedule: + - cron: "0 0 * * *" # Run everyday + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + 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 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=. . + - if: ${{ success() }} + run: scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000000..96175cfecea5 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,21 @@ +name: pre-commit + +on: [push, pull_request] + +jobs: + pre-commit: + 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: | + python -m pip install --upgrade pip + python -m pip install --upgrade pre-commit + - run: pre-commit run --verbose --all-files --show-diff-on-failure diff --git a/.github/workflows/project_euler.yml b/.github/workflows/project_euler.yml new file mode 100644 index 000000000000..e8b011af20a6 --- /dev/null +++ b/.github/workflows/project_euler.yml @@ -0,0 +1,31 @@ +on: + pull_request: + # 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' + +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 --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 scripts/validate_solutions.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..a3288e1c5eef --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,60 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: check-executables-have-shebangs + - id: check-yaml + - id: end-of-file-fixer + types: [python] + - id: trailing-whitespace + exclude: | + (?x)^( + data_structures/heap/binomial_heap.py + )$ + - id: requirements-txt-fixer + - repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + - repo: https://github.com/PyCQA/isort + rev: 5.5.3 + hooks: + - id: isort + args: + - --profile=black + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + args: + - --ignore=E203,W503 + - --max-complexity=25 + - --max-line-length=88 +# FIXME: fix mypy errors and then uncomment this +# - repo: https://github.com/pre-commit/mirrors-mypy +# rev: v0.782 +# hooks: +# - id: mypy +# args: +# - --ignore-missing-imports + - repo: https://github.com/codespell-project/codespell + rev: v1.17.1 + hooks: + - id: codespell + args: + - --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: | + (?x)^( + other/dictionary.txt | + other/words | + project_euler/problem_022/p022_names.txt + )$ + - repo: local + hooks: + - id: validate-filenames + name: Validate filenames + entry: ./scripts/validate_filenames.py + language: script + pass_filenames: false diff --git a/arithmetic_analysis/__init__.py b/arithmetic_analysis/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/arithmetic_analysis/image_data/__init__.py b/arithmetic_analysis/image_data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/backtracking/__init__.py b/backtracking/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/bit_manipulation/__init__.py b/bit_manipulation/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/bit_manipulation/binary_and_operator.py b/bit_manipulation/binary_and_operator.py new file mode 100644 index 000000000000..f1b910f8cc9b --- /dev/null +++ b/bit_manipulation/binary_and_operator.py @@ -0,0 +1,52 @@ +# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm + + +def binary_and(a: int, b: int): + """ + Take in 2 integers, convert them to binary, + return a binary number that is the + result of a binary and operation on the integers provided. + + >>> binary_and(25, 32) + '0b000000' + >>> binary_and(37, 50) + '0b100000' + >>> binary_and(21, 30) + '0b10100' + >>> binary_and(58, 73) + '0b0001000' + >>> binary_and(0, 255) + '0b00000000' + >>> binary_and(256, 256) + '0b100000000' + >>> binary_and(0, -1) + Traceback (most recent call last): + ... + ValueError: the value of both input must be positive + >>> binary_and(0, 1.1) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> binary_and("0", "1") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0 or b < 0: + raise ValueError("the value of both input must be positive") + + a_binary = str(bin(a))[2:] # remove the leading "0b" + b_binary = str(bin(b))[2:] # remove the leading "0b" + + max_len = max(len(a_binary), len(b_binary)) + + return "0b" + "".join( + str(int(char_a == "1" and char_b == "1")) + for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len)) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() 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() diff --git a/bit_manipulation/binary_xor_operator.py b/bit_manipulation/binary_xor_operator.py new file mode 100644 index 000000000000..0edf2ba6606d --- /dev/null +++ b/bit_manipulation/binary_xor_operator.py @@ -0,0 +1,52 @@ +# https://www.tutorialspoint.com/python3/bitwise_operators_example.htm + + +def binary_xor(a: int, b: int): + """ + Take in 2 integers, convert them to binary, + return a binary number that is the + result of a binary xor operation on the integers provided. + + >>> binary_xor(25, 32) + '0b111001' + >>> binary_xor(37, 50) + '0b010111' + >>> binary_xor(21, 30) + '0b01011' + >>> binary_xor(58, 73) + '0b1110011' + >>> binary_xor(0, 255) + '0b11111111' + >>> binary_xor(256, 256) + '0b000000000' + >>> binary_xor(0, -1) + Traceback (most recent call last): + ... + ValueError: the value of both input must be positive + >>> binary_xor(0, 1.1) + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> binary_xor("0", "1") + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + """ + if a < 0 or b < 0: + raise ValueError("the value of both input must be positive") + + a_binary = str(bin(a))[2:] # remove the leading "0b" + b_binary = str(bin(b))[2:] # remove the leading "0b" + + max_len = max(len(a_binary), len(b_binary)) + + return "0b" + "".join( + str(int(char_a != char_b)) + for char_a, char_b in zip(a_binary.zfill(max_len), b_binary.zfill(max_len)) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() 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() diff --git a/blockchain/__init__.py b/blockchain/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/boolean_algebra/__init__.py b/boolean_algebra/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cellular_automata/__init__.py b/cellular_automata/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/ciphers/__init__.py b/ciphers/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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() 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() 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() diff --git a/ciphers/rail_fence_cipher.py b/ciphers/rail_fence_cipher.py new file mode 100644 index 000000000000..484ba6fbd7c1 --- /dev/null +++ b/ciphers/rail_fence_cipher.py @@ -0,0 +1,102 @@ +""" https://en.wikipedia.org/wiki/Rail_fence_cipher """ + + +def encrypt(input_string: str, key: int) -> str: + """ + Shuffles the character of a string by placing each of them + in a grid (the height is dependent on the key) in a zigzag + formation and reading it left to right. + + >>> encrypt("Hello World", 4) + 'HWe olordll' + + >>> encrypt("This is a message", 0) + Traceback (most recent call last): + ... + ValueError: Height of grid can't be 0 or negative + + >>> encrypt(b"This is a byte string", 5) + Traceback (most recent call last): + ... + TypeError: sequence item 0: expected str instance, int found + """ + grid = [[] for _ in range(key)] + lowest = key - 1 + + if key <= 0: + raise ValueError("Height of grid can't be 0 or negative") + if key == 1 or len(input_string) <= key: + return input_string + + for position, character in enumerate(input_string): + num = position % (lowest * 2) # puts it in bounds + num = min(num, lowest * 2 - num) # creates zigzag pattern + grid[num].append(character) + grid = ["".join(row) for row in grid] + output_string = "".join(grid) + + return output_string + + +def decrypt(input_string: str, key: int) -> str: + """ + Generates a template based on the key and fills it in with + the characters of the input string and then reading it in + a zigzag formation. + + >>> decrypt("HWe olordll", 4) + 'Hello World' + + >>> decrypt("This is a message", -10) + Traceback (most recent call last): + ... + ValueError: Height of grid can't be 0 or negative + + >>> decrypt("My key is very big", 100) + 'My key is very big' + """ + grid = [] + lowest = key - 1 + + if key <= 0: + raise ValueError("Height of grid can't be 0 or negative") + if key == 1: + return input_string + + temp_grid = [[] for _ in range(key)] # generates template + for position in range(len(input_string)): + num = position % (lowest * 2) # puts it in bounds + num = min(num, lowest * 2 - num) # creates zigzag pattern + temp_grid[num].append("*") + + counter = 0 + for row in temp_grid: # fills in the characters + splice = input_string[counter : counter + len(row)] + grid.append([character for character in splice]) + counter += len(row) + + output_string = "" # reads as zigzag + for position in range(len(input_string)): + num = position % (lowest * 2) # puts it in bounds + num = min(num, lowest * 2 - num) # creates zigzag pattern + output_string += grid[num][0] + grid[num].pop(0) + return output_string + + +def bruteforce(input_string: str) -> dict: + """Uses decrypt function by guessing every key + + >>> bruteforce("HWe olordll")[4] + 'Hello World' + """ + results = {} + for key_guess in range(1, len(input_string)): # tries every key + results[key_guess] = decrypt(input_string, key_guess) + return results + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/compression/__init__.py b/compression/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/compression/image_data/__init__.py b/compression/image_data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/computer_vision/__init__.py b/computer_vision/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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") diff --git a/conversions/__init__.py b/conversions/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/conversions/binary_to_decimal.py b/conversions/binary_to_decimal.py new file mode 100644 index 000000000000..a7625e475bdc --- /dev/null +++ b/conversions/binary_to_decimal.py @@ -0,0 +1,43 @@ +def bin_to_decimal(bin_string: str) -> int: + """ + Convert a binary value to its decimal equivalent + + >>> bin_to_decimal("101") + 5 + >>> bin_to_decimal(" 1010 ") + 10 + >>> bin_to_decimal("-11101") + -29 + >>> bin_to_decimal("0") + 0 + >>> bin_to_decimal("a") + Traceback (most recent call last): + ... + ValueError: Non-binary value was passed to the function + >>> bin_to_decimal("") + Traceback (most recent call last): + ... + ValueError: Empty string was passed to the function + >>> bin_to_decimal("39") + Traceback (most recent call last): + ... + ValueError: Non-binary value was passed to the function + """ + bin_string = str(bin_string).strip() + if not bin_string: + raise ValueError("Empty string was passed to the function") + is_negative = bin_string[0] == "-" + if is_negative: + bin_string = bin_string[1:] + if not all(char in "01" for char in bin_string): + raise ValueError("Non-binary value was passed to the function") + decimal_number = 0 + for char in bin_string: + decimal_number = 2 * decimal_number + int(char) + return -decimal_number if is_negative else decimal_number + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/conversions/binary_to_octal.py b/conversions/binary_to_octal.py new file mode 100644 index 000000000000..8b594887867e --- /dev/null +++ b/conversions/binary_to_octal.py @@ -0,0 +1,45 @@ +""" +The function below will convert any binary string to the octal equivalent. + +>>> bin_to_octal("1111") +'17' + +>>> bin_to_octal("101010101010011") +'52523' + +>>> bin_to_octal("") +Traceback (most recent call last): +... +ValueError: Empty string was passed to the function +>>> bin_to_octal("a-1") +Traceback (most recent call last): +... +ValueError: Non-binary value was passed to the function +""" + + +def bin_to_octal(bin_string: str) -> str: + if not all(char in "01" for char in bin_string): + raise ValueError("Non-binary value was passed to the function") + if not bin_string: + raise ValueError("Empty string was passed to the function") + oct_string = "" + while len(bin_string) % 3 != 0: + bin_string = "0" + bin_string + bin_string_in_3_list = [ + bin_string[index : index + 3] + for index, value in enumerate(bin_string) + if index % 3 == 0 + ] + for bin_group in bin_string_in_3_list: + oct_val = 0 + for index, val in enumerate(bin_group): + oct_val += int(2 ** (2 - index) * int(val)) + oct_string += str(oct_val) + return oct_string + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/conversions/decimal_to_binary_recursion.py b/conversions/decimal_to_binary_recursion.py new file mode 100644 index 000000000000..c149ea86592f --- /dev/null +++ b/conversions/decimal_to_binary_recursion.py @@ -0,0 +1,53 @@ +def binary_recursive(decimal: int) -> str: + """ + Take a positive integer value and return its binary equivalent. + >>> binary_recursive(1000) + '1111101000' + >>> binary_recursive("72") + '1001000' + >>> binary_recursive("number") + Traceback (most recent call last): + ... + ValueError: invalid literal for int() with base 10: 'number' + """ + decimal = int(decimal) + if decimal in (0, 1): # Exit cases for the recursion + return str(decimal) + div, mod = divmod(decimal, 2) + return binary_recursive(div) + str(mod) + + +def main(number: str) -> str: + """ + Take an integer value and raise ValueError for wrong inputs, + call the function above and return the output with prefix "0b" & "-0b" + for positive and negative integers respectively. + >>> main(0) + '0b0' + >>> main(40) + '0b101000' + >>> main(-40) + '-0b101000' + >>> main(40.8) + Traceback (most recent call last): + ... + ValueError: Input value is not an integer + >>> main("forty") + Traceback (most recent call last): + ... + ValueError: Input value is not an integer + """ + number = str(number).strip() + if not number: + raise ValueError("No input value was provided") + negative = "-" if number.startswith("-") else "" + number = number.lstrip("-") + if not number.isnumeric(): + raise ValueError("Input value is not an integer") + return f"{negative}0b{binary_recursive(int(number))}" + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/conversions/hexadecimal_to_decimal.py b/conversions/hexadecimal_to_decimal.py new file mode 100644 index 000000000000..beb1c2c3ded6 --- /dev/null +++ b/conversions/hexadecimal_to_decimal.py @@ -0,0 +1,49 @@ +hex_table = {hex(i)[2:]: i for i in range(16)} # Use [:2] to strip off the leading '0x' + + +def hex_to_decimal(hex_string: str) -> int: + """ + Convert a hexadecimal value to its decimal equivalent + #https://www.programiz.com/python-programming/methods/built-in/hex + + >>> hex_to_decimal("a") + 10 + >>> hex_to_decimal("12f") + 303 + >>> hex_to_decimal(" 12f ") + 303 + >>> hex_to_decimal("FfFf") + 65535 + >>> hex_to_decimal("-Ff") + -255 + >>> hex_to_decimal("F-f") + Traceback (most recent call last): + ... + ValueError: Non-hexadecimal value was passed to the function + >>> hex_to_decimal("") + Traceback (most recent call last): + ... + ValueError: Empty string was passed to the function + >>> hex_to_decimal("12m") + Traceback (most recent call last): + ... + ValueError: Non-hexadecimal value was passed to the function + """ + hex_string = hex_string.strip().lower() + if not hex_string: + raise ValueError("Empty string was passed to the function") + is_negative = hex_string[0] == "-" + if is_negative: + hex_string = hex_string[1:] + if not all(char in hex_table for char in hex_string): + raise ValueError("Non-hexadecimal value was passed to the function") + decimal_number = 0 + for char in hex_string: + decimal_number = 16 * decimal_number + hex_table[char] + return -decimal_number if is_negative else decimal_number + + +if __name__ == "__main__": + from doctest import testmod + + testmod() 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() diff --git a/conversions/octal_to_decimal.py b/conversions/octal_to_decimal.py new file mode 100644 index 000000000000..5a7373fef7e3 --- /dev/null +++ b/conversions/octal_to_decimal.py @@ -0,0 +1,43 @@ +def oct_to_decimal(oct_string: str) -> int: + """ + Convert a octal value to its decimal equivalent + + >>> oct_to_decimal("12") + 10 + >>> oct_to_decimal(" 12 ") + 10 + >>> 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("") + 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() + if not oct_string: + raise ValueError("Empty string was passed to the function") + is_negative = oct_string[0] == "-" + if is_negative: + oct_string = oct_string[1:] + 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: + decimal_number = 8 * decimal_number + int(char) + if is_negative: + decimal_number = -decimal_number + return decimal_number + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/data_structures/__init__.py b/data_structures/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/binary_tree/__init__.py b/data_structures/binary_tree/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/binary_tree/binary_tree_mirror.py b/data_structures/binary_tree/binary_tree_mirror.py new file mode 100644 index 000000000000..dc7f657b37c7 --- /dev/null +++ b/data_structures/binary_tree/binary_tree_mirror.py @@ -0,0 +1,44 @@ +""" +Problem Description: +Given a binary tree, return it's mirror. +""" + + +def binary_tree_mirror_dict(binary_tree_mirror_dictionary: dict, root: int): + if not root or root not in binary_tree_mirror_dictionary: + return + left_child, right_child = binary_tree_mirror_dictionary[root][:2] + binary_tree_mirror_dictionary[root] = [right_child, left_child] + binary_tree_mirror_dict(binary_tree_mirror_dictionary, left_child) + binary_tree_mirror_dict(binary_tree_mirror_dictionary, right_child) + + +def binary_tree_mirror(binary_tree: dict, root: int = 1) -> dict: + """ + >>> binary_tree_mirror({ 1: [2,3], 2: [4,5], 3: [6,7], 7: [8,9]}, 1) + {1: [3, 2], 2: [5, 4], 3: [7, 6], 7: [9, 8]} + >>> binary_tree_mirror({ 1: [2,3], 2: [4,5], 3: [6,7], 4: [10,11]}, 1) + {1: [3, 2], 2: [5, 4], 3: [7, 6], 4: [11, 10]} + >>> binary_tree_mirror({ 1: [2,3], 2: [4,5], 3: [6,7], 4: [10,11]}, 5) + Traceback (most recent call last): + ... + ValueError: root 5 is not present in the binary_tree + >>> binary_tree_mirror({}, 5) + Traceback (most recent call last): + ... + ValueError: binary tree cannot be empty + """ + if not binary_tree: + raise ValueError("binary tree cannot be empty") + if root not in binary_tree: + raise ValueError(f"root {root} is not present in the binary_tree") + binary_tree_mirror_dictionary = dict(binary_tree) + binary_tree_mirror_dict(binary_tree_mirror_dictionary, root) + return binary_tree_mirror_dictionary + + +if __name__ == "__main__": + binary_tree = {1: [2, 3], 2: [4, 5], 3: [6, 7], 7: [8, 9]} + print(f"Binary tree: {binary_tree}") + binary_tree_mirror_dictionary = binary_tree_mirror(binary_tree, 5) + print(f"Binary tree mirror: {binary_tree_mirror_dictionary}") diff --git a/data_structures/binary_tree/binary_tree_traversals.py b/data_structures/binary_tree/binary_tree_traversals.py new file mode 100644 index 000000000000..11d3cc90c56a --- /dev/null +++ b/data_structures/binary_tree/binary_tree_traversals.py @@ -0,0 +1,161 @@ +# https://en.wikipedia.org/wiki/Tree_traversal + + +class Node: + """ + A Node has data variable and pointers to its left and right nodes. + """ + + def __init__(self, data): + self.left = None + self.right = None + self.data = data + + +def make_tree() -> Node: + root = Node(1) + root.left = Node(2) + root.right = Node(3) + root.left.left = Node(4) + root.left.right = Node(5) + return root + + +def preorder(root: Node): + """ + Pre-order traversal visits root node, left subtree, right subtree. + >>> preorder(make_tree()) + [1, 2, 4, 5, 3] + """ + return [root.data] + preorder(root.left) + preorder(root.right) if root else [] + + +def postorder(root: Node): + """ + Post-order traversal visits left subtree, right subtree, root node. + >>> postorder(make_tree()) + [4, 5, 2, 3, 1] + """ + return postorder(root.left) + postorder(root.right) + [root.data] if root else [] + + +def inorder(root: Node): + """ + In-order traversal visits left subtree, root node, right subtree. + >>> inorder(make_tree()) + [4, 2, 5, 1, 3] + """ + return inorder(root.left) + [root.data] + inorder(root.right) if root else [] + + +def height(root: Node): + """ + Recursive function for calculating the height of the binary tree. + >>> height(None) + 0 + >>> height(make_tree()) + 3 + """ + return (max(height(root.left), height(root.right)) + 1) if root else 0 + + +def level_order_1(root: Node): + """ + Print whole binary tree in Level Order Traverse. + Level Order traverse: Visit nodes of the tree level-by-level. + """ + if not root: + return + temp = root + que = [temp] + while len(que) > 0: + print(que[0].data, end=" ") + temp = que.pop(0) + if temp.left: + que.append(temp.left) + if temp.right: + que.append(temp.right) + return que + + +def level_order_2(root: Node, level: int): + """ + Level-wise traversal: Print all nodes present at the given level of the binary tree + """ + if not root: + return root + if level == 1: + print(root.data, end=" ") + elif level > 1: + level_order_2(root.left, level - 1) + level_order_2(root.right, level - 1) + + +def print_left_to_right(root: Node, level: int): + """ + Print elements on particular level from left to right direction of the binary tree. + """ + if not root: + return + if level == 1: + print(root.data, end=" ") + elif level > 1: + print_left_to_right(root.left, level - 1) + print_left_to_right(root.right, level - 1) + + +def print_right_to_left(root: Node, level: int): + """ + Print elements on particular level from right to left direction of the binary tree. + """ + if not root: + return + if level == 1: + print(root.data, end=" ") + elif level > 1: + print_right_to_left(root.right, level - 1) + print_right_to_left(root.left, level - 1) + + +def zigzag(root: Node): + """ + ZigZag traverse: Print node left to right and right to left, alternatively. + """ + flag = 0 + height_tree = height(root) + for h in range(1, height_tree + 1): + if flag == 0: + print_left_to_right(root, h) + flag = 1 + else: + print_right_to_left(root, h) + flag = 0 + + +def main(): # Main function for testing. + """ + Create binary tree. + """ + root = make_tree() + """ + All Traversals of the binary are as follows: + """ + print(f" In-order Traversal is {inorder(root)}") + print(f" Pre-order Traversal is {preorder(root)}") + print(f"Post-order Traversal is {postorder(root)}") + print(f"Height of Tree is {height(root)}") + print("Complete Level Order Traversal is : ") + level_order_1(root) + print("\nLevel-wise order Traversal is : ") + for h in range(1, height(root) + 1): + level_order_2(root, h) + print("\nZigZag order Traversal is : ") + zigzag(root) + print() + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() diff --git a/data_structures/disjoint_set/__init__.py b/data_structures/disjoint_set/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/hashing/__init__.py b/data_structures/hashing/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/heap/__init__.py b/data_structures/heap/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/heap/randomized_heap.py b/data_structures/heap/randomized_heap.py new file mode 100644 index 000000000000..0ddc2272efe8 --- /dev/null +++ b/data_structures/heap/randomized_heap.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import random +from typing import Generic, Iterable, List, Optional, TypeVar + +T = TypeVar("T") + + +class RandomizedHeapNode(Generic[T]): + """ + One node of the randomized heap. Contains the value and references to + two children. + """ + + def __init__(self, value: T) -> None: + self._value: T = value + self.left: Optional[RandomizedHeapNode[T]] = None + self.right: Optional[RandomizedHeapNode[T]] = None + + @property + def value(self) -> T: + """Return the value of the node.""" + return self._value + + @staticmethod + def merge( + root1: Optional[RandomizedHeapNode[T]], root2: Optional[RandomizedHeapNode[T]] + ) -> Optional[RandomizedHeapNode[T]]: + """Merge 2 nodes together.""" + if not root1: + return root2 + + if not root2: + return root1 + + if root1.value > root2.value: + root1, root2 = root2, root1 + + if random.choice([True, False]): + root1.left, root1.right = root1.right, root1.left + + root1.left = RandomizedHeapNode.merge(root1.left, root2) + + return root1 + + +class RandomizedHeap(Generic[T]): + """ + A data structure that allows inserting a new value and to pop the smallest + values. Both operations take O(logN) time where N is the size of the + structure. + Wiki: https://en.wikipedia.org/wiki/Randomized_meldable_heap + + >>> RandomizedHeap([2, 3, 1, 5, 1, 7]).to_sorted_list() + [1, 1, 2, 3, 5, 7] + + >>> rh = RandomizedHeap() + >>> rh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + + >>> rh.insert(1) + >>> rh.insert(-1) + >>> rh.insert(0) + >>> rh.to_sorted_list() + [-1, 0, 1] + """ + + def __init__(self, data: Optional[Iterable[T]] = ()) -> None: + """ + >>> rh = RandomizedHeap([3, 1, 3, 7]) + >>> rh.to_sorted_list() + [1, 3, 3, 7] + """ + self._root: Optional[RandomizedHeapNode[T]] = None + for item in data: + self.insert(item) + + def insert(self, value: T) -> None: + """ + Insert the value into the heap. + + >>> rh = RandomizedHeap() + >>> rh.insert(3) + >>> rh.insert(1) + >>> rh.insert(3) + >>> rh.insert(7) + >>> rh.to_sorted_list() + [1, 3, 3, 7] + """ + self._root = RandomizedHeapNode.merge(self._root, RandomizedHeapNode(value)) + + def pop(self) -> T: + """ + Pop the smallest value from the heap and return it. + + >>> rh = RandomizedHeap([3, 1, 3, 7]) + >>> rh.pop() + 1 + >>> rh.pop() + 3 + >>> rh.pop() + 3 + >>> rh.pop() + 7 + >>> rh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + """ + result = self.top() + self._root = RandomizedHeapNode.merge(self._root.left, self._root.right) + + return result + + def top(self) -> T: + """ + Return the smallest value from the heap. + + >>> rh = RandomizedHeap() + >>> rh.insert(3) + >>> rh.top() + 3 + >>> rh.insert(1) + >>> rh.top() + 1 + >>> rh.insert(3) + >>> rh.top() + 1 + >>> rh.insert(7) + >>> rh.top() + 1 + """ + if not self._root: + raise IndexError("Can't get top element for the empty heap.") + return self._root.value + + def clear(self): + """ + Clear the heap. + + >>> rh = RandomizedHeap([3, 1, 3, 7]) + >>> rh.clear() + >>> rh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + """ + self._root = None + + def to_sorted_list(self) -> List[T]: + """ + Returns sorted list containing all the values in the heap. + + >>> rh = RandomizedHeap([3, 1, 3, 7]) + >>> rh.to_sorted_list() + [1, 3, 3, 7] + """ + result = [] + while self: + result.append(self.pop()) + + return result + + def __bool__(self) -> bool: + """ + Check if the heap is not empty. + + >>> rh = RandomizedHeap() + >>> bool(rh) + False + >>> rh.insert(1) + >>> bool(rh) + True + >>> rh.clear() + >>> bool(rh) + False + """ + return self._root is not None + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/data_structures/heap/skew_heap.py b/data_structures/heap/skew_heap.py new file mode 100644 index 000000000000..417a383f733e --- /dev/null +++ b/data_structures/heap/skew_heap.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +from typing import Generic, Iterable, Iterator, Optional, TypeVar + +T = TypeVar("T") + + +class SkewNode(Generic[T]): + """ + One node of the skew heap. Contains the value and references to + two children. + """ + + def __init__(self, value: T) -> None: + self._value: T = value + self.left: Optional[SkewNode[T]] = None + self.right: Optional[SkewNode[T]] = None + + @property + def value(self) -> T: + """Return the value of the node.""" + return self._value + + @staticmethod + def merge( + root1: Optional[SkewNode[T]], root2: Optional[SkewNode[T]] + ) -> Optional[SkewNode[T]]: + """Merge 2 nodes together.""" + if not root1: + return root2 + + if not root2: + return root1 + + if root1.value > root2.value: + root1, root2 = root2, root1 + + result = root1 + temp = root1.right + result.right = root1.left + result.left = SkewNode.merge(temp, root2) + + return result + + +class SkewHeap(Generic[T]): + """ + A data structure that allows inserting a new value and to pop the smallest + values. Both operations take O(logN) time where N is the size of the + structure. + Wiki: https://en.wikipedia.org/wiki/Skew_heap + Visualisation: https://www.cs.usfca.edu/~galles/visualization/SkewHeap.html + + >>> list(SkewHeap([2, 3, 1, 5, 1, 7])) + [1, 1, 2, 3, 5, 7] + + >>> sh = SkewHeap() + >>> sh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + + >>> sh.insert(1) + >>> sh.insert(-1) + >>> sh.insert(0) + >>> list(sh) + [-1, 0, 1] + """ + + def __init__(self, data: Optional[Iterable[T]] = ()) -> None: + """ + >>> sh = SkewHeap([3, 1, 3, 7]) + >>> list(sh) + [1, 3, 3, 7] + """ + self._root: Optional[SkewNode[T]] = None + for item in data: + self.insert(item) + + def __bool__(self) -> bool: + """ + Check if the heap is not empty. + + >>> sh = SkewHeap() + >>> bool(sh) + False + >>> sh.insert(1) + >>> bool(sh) + True + >>> sh.clear() + >>> bool(sh) + False + """ + return self._root is not None + + def __iter__(self) -> Iterator[T]: + """ + Returns sorted list containing all the values in the heap. + + >>> sh = SkewHeap([3, 1, 3, 7]) + >>> list(sh) + [1, 3, 3, 7] + """ + result = [] + while self: + result.append(self.pop()) + + # Pushing items back to the heap not to clear it. + for item in result: + self.insert(item) + + return iter(result) + + def insert(self, value: T) -> None: + """ + Insert the value into the heap. + + >>> sh = SkewHeap() + >>> sh.insert(3) + >>> sh.insert(1) + >>> sh.insert(3) + >>> sh.insert(7) + >>> list(sh) + [1, 3, 3, 7] + """ + self._root = SkewNode.merge(self._root, SkewNode(value)) + + def pop(self) -> T: + """ + Pop the smallest value from the heap and return it. + + >>> sh = SkewHeap([3, 1, 3, 7]) + >>> sh.pop() + 1 + >>> sh.pop() + 3 + >>> sh.pop() + 3 + >>> sh.pop() + 7 + >>> sh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + """ + result = self.top() + self._root = SkewNode.merge(self._root.left, self._root.right) + + return result + + def top(self) -> T: + """ + Return the smallest value from the heap. + + >>> sh = SkewHeap() + >>> sh.insert(3) + >>> sh.top() + 3 + >>> sh.insert(1) + >>> sh.top() + 1 + >>> sh.insert(3) + >>> sh.top() + 1 + >>> sh.insert(7) + >>> sh.top() + 1 + """ + if not self._root: + raise IndexError("Can't get top element for the empty heap.") + return self._root.value + + def clear(self): + """ + Clear the heap. + + >>> sh = SkewHeap([3, 1, 3, 7]) + >>> sh.clear() + >>> sh.pop() + Traceback (most recent call last): + ... + IndexError: Can't get top element for the empty heap. + """ + self._root = None + + +if __name__ == "__main__": + import doctest + + doctest.testmod() 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() diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_loop.py new file mode 100644 index 000000000000..405ece7e27c8 --- /dev/null +++ b/data_structures/linked_list/has_loop.py @@ -0,0 +1,60 @@ +from typing import Any + + +class ContainsLoopError(Exception): + pass + + +class Node: + def __init__(self, data: Any) -> None: + self.data = data + self.next_node = None + + def __iter__(self): + node = self + visited = [] + while node: + if node in visited: + raise ContainsLoopError + visited.append(node) + yield node.data + node = node.next_node + + @property + def has_loop(self) -> bool: + """ + A loop is when the exact same Node appears more than once in a linked list. + >>> root_node = Node(1) + >>> root_node.next_node = Node(2) + >>> root_node.next_node.next_node = Node(3) + >>> root_node.next_node.next_node.next_node = Node(4) + >>> root_node.has_loop + False + >>> root_node.next_node.next_node.next_node = root_node.next_node + >>> root_node.has_loop + True + """ + try: + list(self) + return False + except ContainsLoopError: + return True + + +if __name__ == "__main__": + root_node = Node(1) + root_node.next_node = Node(2) + root_node.next_node.next_node = Node(3) + root_node.next_node.next_node.next_node = Node(4) + print(root_node.has_loop) # False + root_node.next_node.next_node.next_node = root_node.next_node + print(root_node.has_loop) # True + + root_node = Node(5) + root_node.next_node = Node(6) + root_node.next_node.next_node = Node(5) + root_node.next_node.next_node.next_node = Node(6) + print(root_node.has_loop) # False + + root_node = Node(1) + print(root_node.has_loop) # False diff --git a/data_structures/queue/__init__.py b/data_structures/queue/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/data_structures/queue/priority_queue_using_list.py b/data_structures/queue/priority_queue_using_list.py new file mode 100644 index 000000000000..c5cf26433fff --- /dev/null +++ b/data_structures/queue/priority_queue_using_list.py @@ -0,0 +1,232 @@ +""" +Pure Python implementations of a Fixed Priority Queue and an Element Priority Queue +using Python lists. +""" + + +class OverFlowError(Exception): + pass + + +class UnderFlowError(Exception): + pass + + +class FixedPriorityQueue: + """ + Tasks can be added to a Priority Queue at any time and in any order but when Tasks + are removed then the Task with the highest priority is removed in FIFO order. In + code we will use three levels of priority with priority zero Tasks being the most + urgent (high priority) and priority 2 tasks being the least urgent. + + Examples + >>> fpq = FixedPriorityQueue() + >>> fpq.enqueue(0, 10) + >>> fpq.enqueue(1, 70) + >>> fpq.enqueue(0, 100) + >>> fpq.enqueue(2, 1) + >>> fpq.enqueue(2, 5) + >>> fpq.enqueue(1, 7) + >>> fpq.enqueue(2, 4) + >>> fpq.enqueue(1, 64) + >>> fpq.enqueue(0, 128) + >>> print(fpq) + Priority 0: [10, 100, 128] + Priority 1: [70, 7, 64] + Priority 2: [1, 5, 4] + >>> fpq.dequeue() + 10 + >>> fpq.dequeue() + 100 + >>> fpq.dequeue() + 128 + >>> fpq.dequeue() + 70 + >>> fpq.dequeue() + 7 + >>> print(fpq) + Priority 0: [] + Priority 1: [64] + Priority 2: [1, 5, 4] + >>> fpq.dequeue() + 64 + >>> fpq.dequeue() + 1 + >>> fpq.dequeue() + 5 + >>> fpq.dequeue() + 4 + >>> fpq.dequeue() + Traceback (most recent call last): + ... + data_structures.queue.priority_queue_using_list.UnderFlowError: All queues are empty + >>> print(fpq) + Priority 0: [] + Priority 1: [] + Priority 2: [] + """ + + def __init__(self): + self.queues = [ + [], + [], + [], + ] + + def enqueue(self, priority: int, data: int) -> None: + """ + Add an element to a queue based on its priority. + If the priority is invalid ValueError is raised. + If the queue is full an OverFlowError is raised. + """ + try: + if len(self.queues[priority]) >= 100: + raise OverflowError("Maximum queue size is 100") + self.queues[priority].append(data) + except IndexError: + raise ValueError("Valid priorities are 0, 1, and 2") + + def dequeue(self) -> int: + """ + Return the highest priority element in FIFO order. + If the queue is empty then an under flow exception is raised. + """ + for queue in self.queues: + if queue: + return queue.pop(0) + raise UnderFlowError("All queues are empty") + + def __str__(self) -> str: + return "\n".join(f"Priority {i}: {q}" for i, q in enumerate(self.queues)) + + +class ElementPriorityQueue: + """ + Element Priority Queue is the same as Fixed Priority Queue except that the value of + the element itself is the priority. The rules for priorities are the same the as + Fixed Priority Queue. + + >>> epq = ElementPriorityQueue() + >>> epq.enqueue(10) + >>> epq.enqueue(70) + >>> epq.enqueue(4) + >>> epq.enqueue(1) + >>> epq.enqueue(5) + >>> epq.enqueue(7) + >>> epq.enqueue(4) + >>> epq.enqueue(64) + >>> epq.enqueue(128) + >>> print(epq) + [10, 70, 4, 1, 5, 7, 4, 64, 128] + >>> epq.dequeue() + 1 + >>> epq.dequeue() + 4 + >>> epq.dequeue() + 4 + >>> epq.dequeue() + 5 + >>> epq.dequeue() + 7 + >>> epq.dequeue() + 10 + >>> print(epq) + [70, 64, 128] + >>> epq.dequeue() + 64 + >>> epq.dequeue() + 70 + >>> epq.dequeue() + 128 + >>> epq.dequeue() + Traceback (most recent call last): + ... + data_structures.queue.priority_queue_using_list.UnderFlowError: The queue is empty + >>> print(epq) + [] + """ + + def __init__(self): + self.queue = [] + + def enqueue(self, data: int) -> None: + """ + This function enters the element into the queue + If the queue is full an Exception is raised saying Over Flow! + """ + if len(self.queue) == 100: + raise OverFlowError("Maximum queue size is 100") + self.queue.append(data) + + def dequeue(self) -> int: + """ + Return the highest priority element in FIFO order. + If the queue is empty then an under flow exception is raised. + """ + if not self.queue: + raise UnderFlowError("The queue is empty") + else: + data = min(self.queue) + self.queue.remove(data) + return data + + def __str__(self) -> str: + """ + Prints all the elements within the Element Priority Queue + """ + return str(self.queue) + + +def fixed_priority_queue(): + fpq = FixedPriorityQueue() + fpq.enqueue(0, 10) + fpq.enqueue(1, 70) + fpq.enqueue(0, 100) + fpq.enqueue(2, 1) + fpq.enqueue(2, 5) + fpq.enqueue(1, 7) + fpq.enqueue(2, 4) + fpq.enqueue(1, 64) + fpq.enqueue(0, 128) + print(fpq) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + print(fpq.dequeue()) + + +def element_priority_queue(): + epq = ElementPriorityQueue() + epq.enqueue(10) + epq.enqueue(70) + epq.enqueue(100) + epq.enqueue(1) + epq.enqueue(5) + epq.enqueue(7) + epq.enqueue(4) + epq.enqueue(64) + epq.enqueue(128) + print(epq) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + print(epq.dequeue()) + + +if __name__ == "__main__": + fixed_priority_queue() + element_priority_queue() 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() diff --git a/data_structures/trie/__init__.py b/data_structures/trie/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/digital_image_processing/histogram_equalization/__init__.py b/digital_image_processing/histogram_equalization/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/digital_image_processing/histogram_equalization/image_data/__init__.py b/digital_image_processing/histogram_equalization/image_data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/digital_image_processing/histogram_equalization/output_data/__init__.py b/digital_image_processing/histogram_equalization/output_data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/digital_image_processing/image_data/__init__.py b/digital_image_processing/image_data/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/divide_and_conquer/__init__.py b/divide_and_conquer/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/divide_and_conquer/heaps_algorithm.py b/divide_and_conquer/heaps_algorithm.py new file mode 100644 index 000000000000..af30ad664101 --- /dev/null +++ b/divide_and_conquer/heaps_algorithm.py @@ -0,0 +1,56 @@ +""" +Heap's algorithm returns the list of all permutations possible from a list. +It minimizes movement by generating each permutation from the previous one +by swapping only two elements. +More information: +https://en.wikipedia.org/wiki/Heap%27s_algorithm. +""" + + +def heaps(arr: list) -> list: + """ + Pure python implementation of the Heap's algorithm (recursive version), + returning all permutations of a list. + >>> heaps([]) + [()] + >>> heaps([0]) + [(0,)] + >>> heaps([-1, 1]) + [(-1, 1), (1, -1)] + >>> heaps([1, 2, 3]) + [(1, 2, 3), (2, 1, 3), (3, 1, 2), (1, 3, 2), (2, 3, 1), (3, 2, 1)] + >>> from itertools import permutations + >>> sorted(heaps([1,2,3])) == sorted(permutations([1,2,3])) + True + >>> all(sorted(heaps(x)) == sorted(permutations(x)) + ... for x in ([], [0], [-1, 1], [1, 2, 3])) + True + """ + + if len(arr) <= 1: + return [tuple(arr)] + + res = [] + + def generate(k: int, arr: list): + if k == 1: + res.append(tuple(arr[:])) + return + + generate(k - 1, arr) + + for i in range(k - 1): + if k % 2 == 0: # k is even + arr[i], arr[k - 1] = arr[k - 1], arr[i] + else: # k is odd + arr[0], arr[k - 1] = arr[k - 1], arr[0] + generate(k - 1, arr) + + generate(len(arr), arr) + return res + + +if __name__ == "__main__": + user_input = input("Enter numbers separated by a comma:\n").strip() + arr = [int(item) for item in user_input.split(",")] + print(heaps(arr)) diff --git a/divide_and_conquer/heaps_algorithm_iterative.py b/divide_and_conquer/heaps_algorithm_iterative.py new file mode 100644 index 000000000000..4dab41f539c0 --- /dev/null +++ b/divide_and_conquer/heaps_algorithm_iterative.py @@ -0,0 +1,60 @@ +""" +Heap's (iterative) algorithm returns the list of all permutations possible from a list. +It minimizes movement by generating each permutation from the previous one +by swapping only two elements. +More information: +https://en.wikipedia.org/wiki/Heap%27s_algorithm. +""" + + +def heaps(arr: list) -> list: + """ + Pure python implementation of the iterative Heap's algorithm, + returning all permutations of a list. + >>> heaps([]) + [()] + >>> heaps([0]) + [(0,)] + >>> heaps([-1, 1]) + [(-1, 1), (1, -1)] + >>> heaps([1, 2, 3]) + [(1, 2, 3), (2, 1, 3), (3, 1, 2), (1, 3, 2), (2, 3, 1), (3, 2, 1)] + >>> from itertools import permutations + >>> sorted(heaps([1,2,3])) == sorted(permutations([1,2,3])) + True + >>> all(sorted(heaps(x)) == sorted(permutations(x)) + ... for x in ([], [0], [-1, 1], [1, 2, 3])) + True + """ + + if len(arr) <= 1: + return [tuple(arr)] + + res = [] + + def generate(n: int, arr: list): + c = [0] * n + res.append(tuple(arr)) + + i = 0 + while i < n: + if c[i] < i: + if i % 2 == 0: + arr[0], arr[i] = arr[i], arr[0] + else: + arr[c[i]], arr[i] = arr[i], arr[c[i]] + res.append(tuple(arr)) + c[i] += 1 + i = 0 + else: + c[i] = 0 + i += 1 + + generate(len(arr), arr) + return res + + +if __name__ == "__main__": + user_input = input("Enter numbers separated by a comma:\n").strip() + arr = [int(item) for item in user_input.split(",")] + print(heaps(arr)) 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() 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() 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() diff --git a/dynamic_programming/__init__.py b/dynamic_programming/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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() diff --git a/file_transfer/__init__.py b/file_transfer/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/file_transfer/tests/__init__.py b/file_transfer/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/file_transfer/tests/test_send_file.py b/file_transfer/tests/test_send_file.py new file mode 100644 index 000000000000..2a6008448362 --- /dev/null +++ b/file_transfer/tests/test_send_file.py @@ -0,0 +1,31 @@ +from unittest.mock import Mock, patch + +from file_transfer.send_file import send_file + + +@patch("socket.socket") +@patch("builtins.open") +def test_send_file_running_as_expected(file, sock): + # ===== initialization ===== + conn = Mock() + sock.return_value.accept.return_value = conn, Mock() + f = iter([1, None]) + file.return_value.__enter__.return_value.read.side_effect = lambda _: next(f) + + # ===== invoke ===== + send_file(filename="mytext.txt", testing=True) + + # ===== ensurance ===== + sock.assert_called_once() + sock.return_value.bind.assert_called_once() + sock.return_value.listen.assert_called_once() + sock.return_value.accept.assert_called_once() + conn.recv.assert_called_once() + + file.return_value.__enter__.assert_called_once() + file.return_value.__enter__.return_value.read.assert_called() + + conn.send.assert_called_once() + conn.close.assert_called_once() + sock.return_value.shutdown.assert_called_once() + sock.return_value.close.assert_called_once() diff --git a/fuzzy_logic/__init__.py b/fuzzy_logic/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/genetic_algorithm/__init__.py b/genetic_algorithm/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/geodesy/__init__.py b/geodesy/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/graphics/__init__.py b/graphics/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/graphics/vector3_for_2d_rendering.py b/graphics/vector3_for_2d_rendering.py new file mode 100644 index 000000000000..dfa22262a8d8 --- /dev/null +++ b/graphics/vector3_for_2d_rendering.py @@ -0,0 +1,102 @@ +""" +render 3d points for 2d surfaces. +""" + +from __future__ import annotations + +import math + +__version__ = "2020.9.26" +__author__ = "xcodz-dot, cclaus, dhruvmanila" + + +def convert_to_2d( + x: float, y: float, z: float, scale: float, distance: float +) -> tuple[float, float]: + """ + Converts 3d point to a 2d drawable point + + >>> convert_to_2d(1.0, 2.0, 3.0, 10.0, 10.0) + (7.6923076923076925, 15.384615384615385) + + >>> convert_to_2d(1, 2, 3, 10, 10) + (7.6923076923076925, 15.384615384615385) + + >>> convert_to_2d("1", 2, 3, 10, 10) # '1' is str + Traceback (most recent call last): + ... + TypeError: Input values must either be float or int: ['1', 2, 3, 10, 10] + """ + if not all(isinstance(val, (float, int)) for val in locals().values()): + raise TypeError( + "Input values must either be float or int: " f"{list(locals().values())}" + ) + projected_x = ((x * distance) / (z + distance)) * scale + projected_y = ((y * distance) / (z + distance)) * scale + return projected_x, projected_y + + +def rotate( + x: float, y: float, z: float, axis: str, angle: float +) -> tuple[float, float, float]: + """ + rotate a point around a certain axis with a certain angle + angle can be any integer between 1, 360 and axis can be any one of + 'x', 'y', 'z' + + >>> rotate(1.0, 2.0, 3.0, 'y', 90.0) + (3.130524675073759, 2.0, 0.4470070007889556) + + >>> rotate(1, 2, 3, "z", 180) + (0.999736015495891, -2.0001319704760485, 3) + + >>> rotate('1', 2, 3, "z", 90.0) # '1' is str + Traceback (most recent call last): + ... + TypeError: Input values except axis must either be float or int: ['1', 2, 3, 90.0] + + >>> rotate(1, 2, 3, "n", 90) # 'n' is not a valid axis + Traceback (most recent call last): + ... + ValueError: not a valid axis, choose one of 'x', 'y', 'z' + + >>> rotate(1, 2, 3, "x", -90) + (1, -2.5049096187183877, -2.5933429780983657) + + >>> rotate(1, 2, 3, "x", 450) # 450 wrap around to 90 + (1, 3.5776792428178217, -0.44744970165427644) + """ + if not isinstance(axis, str): + raise TypeError("Axis must be a str") + input_variables = locals() + del input_variables["axis"] + if not all(isinstance(val, (float, int)) for val in input_variables.values()): + raise TypeError( + "Input values except axis must either be float or int: " + f"{list(input_variables.values())}" + ) + angle = (angle % 360) / 450 * 180 / math.pi + if axis == "z": + new_x = x * math.cos(angle) - y * math.sin(angle) + new_y = y * math.cos(angle) + x * math.sin(angle) + new_z = z + elif axis == "x": + new_y = y * math.cos(angle) - z * math.sin(angle) + new_z = z * math.cos(angle) + y * math.sin(angle) + new_x = x + elif axis == "y": + new_x = x * math.cos(angle) - z * math.sin(angle) + new_z = z * math.cos(angle) + x * math.sin(angle) + new_y = y + else: + raise ValueError("not a valid axis, choose one of 'x', 'y', 'z'") + + return new_x, new_y, new_z + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{convert_to_2d(1.0, 2.0, 3.0, 10.0, 10.0) = }") + print(f"{rotate(1.0, 2.0, 3.0, 'y', 90.0) = }") diff --git a/graphs/__init__.py b/graphs/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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() diff --git a/graphs/minimum_spanning_tree_prims2.py b/graphs/minimum_spanning_tree_prims2.py new file mode 100644 index 000000000000..10ed736c9d17 --- /dev/null +++ b/graphs/minimum_spanning_tree_prims2.py @@ -0,0 +1,271 @@ +""" +Prim's (also known as Jarník's) algorithm is a greedy algorithm that finds a minimum +spanning tree for a weighted undirected graph. This means it finds a subset of the +edges that forms a tree that includes every vertex, where the total weight of all the +edges in the tree is minimized. The algorithm operates by building this tree one vertex +at a time, from an arbitrary starting vertex, at each step adding the cheapest possible +connection from the tree to another vertex. +""" + +from sys import maxsize +from typing import Dict, Optional, Tuple, Union + + +def get_parent_position(position: int) -> int: + """ + heap helper function get the position of the parent of the current node + + >>> get_parent_position(1) + 0 + >>> get_parent_position(2) + 0 + """ + return (position - 1) // 2 + + +def get_child_left_position(position: int) -> int: + """ + heap helper function get the position of the left child of the current node + + >>> get_child_left_position(0) + 1 + """ + return (2 * position) + 1 + + +def get_child_right_position(position: int) -> int: + """ + heap helper function get the position of the right child of the current node + + >>> get_child_right_position(0) + 2 + """ + return (2 * position) + 2 + + +class MinPriorityQueue: + """ + Minimum Priority Queue Class + + Functions: + is_empty: function to check if the priority queue is empty + push: function to add an element with given priority to the queue + extract_min: function to remove and return the element with lowest weight (highest + priority) + update_key: function to update the weight of the given key + _bubble_up: helper function to place a node at the proper position (upward + movement) + _bubble_down: helper function to place a node at the proper position (downward + movement) + _swap_nodes: helper function to swap the nodes at the given positions + + >>> queue = MinPriorityQueue() + + >>> queue.push(1, 1000) + >>> queue.push(2, 100) + >>> queue.push(3, 4000) + >>> queue.push(4, 3000) + + >>> print(queue.extract_min()) + 2 + + >>> queue.update_key(4, 50) + + >>> print(queue.extract_min()) + 4 + >>> print(queue.extract_min()) + 1 + >>> print(queue.extract_min()) + 3 + """ + + def __init__(self) -> None: + self.heap = [] + self.position_map = {} + self.elements = 0 + + def __len__(self) -> int: + return self.elements + + def __repr__(self) -> str: + return str(self.heap) + + def is_empty(self) -> bool: + # Check if the priority queue is empty + return self.elements == 0 + + def push(self, elem: Union[int, str], weight: int) -> None: + # Add an element with given priority to the queue + self.heap.append((elem, weight)) + self.position_map[elem] = self.elements + self.elements += 1 + self._bubble_up(elem) + + def extract_min(self) -> Union[int, str]: + # Remove and return the element with lowest weight (highest priority) + if self.elements > 1: + self._swap_nodes(0, self.elements - 1) + elem, _ = self.heap.pop() + del self.position_map[elem] + self.elements -= 1 + if self.elements > 0: + bubble_down_elem, _ = self.heap[0] + self._bubble_down(bubble_down_elem) + return elem + + def update_key(self, elem: Union[int, str], weight: int) -> None: + # Update the weight of the given key + position = self.position_map[elem] + self.heap[position] = (elem, weight) + if position > 0: + parent_position = get_parent_position(position) + _, parent_weight = self.heap[parent_position] + if parent_weight > weight: + self._bubble_up(elem) + else: + self._bubble_down(elem) + else: + self._bubble_down(elem) + + def _bubble_up(self, elem: Union[int, str]) -> None: + # Place a node at the proper position (upward movement) [to be used internally + # only] + curr_pos = self.position_map[elem] + if curr_pos == 0: + return + parent_position = get_parent_position(curr_pos) + _, weight = self.heap[curr_pos] + _, parent_weight = self.heap[parent_position] + if parent_weight > weight: + self._swap_nodes(parent_position, curr_pos) + return self._bubble_up(elem) + return + + def _bubble_down(self, elem: Union[int, str]) -> None: + # Place a node at the proper position (downward movement) [to be used + # internally only] + curr_pos = self.position_map[elem] + _, weight = self.heap[curr_pos] + child_left_position = get_child_left_position(curr_pos) + child_right_position = get_child_right_position(curr_pos) + if child_left_position < self.elements and child_right_position < self.elements: + _, child_left_weight = self.heap[child_left_position] + _, child_right_weight = self.heap[child_right_position] + if child_right_weight < child_left_weight: + if child_right_weight < weight: + self._swap_nodes(child_right_position, curr_pos) + return self._bubble_down(elem) + if child_left_position < self.elements: + _, child_left_weight = self.heap[child_left_position] + if child_left_weight < weight: + self._swap_nodes(child_left_position, curr_pos) + return self._bubble_down(elem) + else: + return + if child_right_position < self.elements: + _, child_right_weight = self.heap[child_right_position] + if child_right_weight < weight: + self._swap_nodes(child_right_position, curr_pos) + return self._bubble_down(elem) + else: + return + + def _swap_nodes(self, node1_pos: int, node2_pos: int) -> None: + # Swap the nodes at the given positions + node1_elem = self.heap[node1_pos][0] + node2_elem = self.heap[node2_pos][0] + self.heap[node1_pos], self.heap[node2_pos] = ( + self.heap[node2_pos], + self.heap[node1_pos], + ) + self.position_map[node1_elem] = node2_pos + self.position_map[node2_elem] = node1_pos + + +class GraphUndirectedWeighted: + """ + Graph Undirected Weighted Class + + Functions: + add_node: function to add a node in the graph + add_edge: function to add an edge between 2 nodes in the graph + """ + + def __init__(self) -> None: + self.connections = {} + self.nodes = 0 + + def __repr__(self) -> str: + return str(self.connections) + + def __len__(self) -> int: + return self.nodes + + def add_node(self, node: Union[int, str]) -> None: + # Add a node in the graph if it is not in the graph + if node not in self.connections: + self.connections[node] = {} + self.nodes += 1 + + def add_edge( + self, node1: Union[int, str], node2: Union[int, str], weight: int + ) -> None: + # Add an edge between 2 nodes in the graph + self.add_node(node1) + self.add_node(node2) + self.connections[node1][node2] = weight + self.connections[node2][node1] = weight + + +def prims_algo( + graph: GraphUndirectedWeighted, +) -> Tuple[Dict[str, int], Dict[str, Optional[str]]]: + """ + >>> graph = GraphUndirectedWeighted() + + >>> graph.add_edge("a", "b", 3) + >>> graph.add_edge("b", "c", 10) + >>> graph.add_edge("c", "d", 5) + >>> graph.add_edge("a", "c", 15) + >>> graph.add_edge("b", "d", 100) + + >>> dist, parent = prims_algo(graph) + + >>> abs(dist["a"] - dist["b"]) + 3 + >>> abs(dist["d"] - dist["b"]) + 15 + >>> abs(dist["a"] - dist["c"]) + 13 + """ + # prim's algorithm for minimum spanning tree + dist = {node: maxsize for node in graph.connections} + parent = {node: None for node in graph.connections} + priority_queue = MinPriorityQueue() + [priority_queue.push(node, weight) for node, weight in dist.items()] + if priority_queue.is_empty(): + return dist, parent + + # initialization + node = priority_queue.extract_min() + dist[node] = 0 + for neighbour in graph.connections[node]: + if dist[neighbour] > dist[node] + graph.connections[node][neighbour]: + dist[neighbour] = dist[node] + graph.connections[node][neighbour] + priority_queue.update_key(neighbour, dist[neighbour]) + parent[neighbour] = node + # running prim's algorithm + while not priority_queue.is_empty(): + node = priority_queue.extract_min() + for neighbour in graph.connections[node]: + if dist[neighbour] > dist[node] + graph.connections[node][neighbour]: + dist[neighbour] = dist[node] + graph.connections[node][neighbour] + priority_queue.update_key(neighbour, dist[neighbour]) + parent[neighbour] = node + return dist, parent + + +if __name__ == "__main__": + from doctest import testmod + + testmod() diff --git a/graphs/tests/test_min_spanning_tree_kruskal.py b/graphs/tests/test_min_spanning_tree_kruskal.py new file mode 100644 index 000000000000..3a527aef384f --- /dev/null +++ b/graphs/tests/test_min_spanning_tree_kruskal.py @@ -0,0 +1,36 @@ +from graphs.minimum_spanning_tree_kruskal import kruskal + + +def test_kruskal_successful_result(): + num_nodes, num_edges = 9, 14 + edges = [ + [0, 1, 4], + [0, 7, 8], + [1, 2, 8], + [7, 8, 7], + [7, 6, 1], + [2, 8, 2], + [8, 6, 6], + [2, 3, 7], + [2, 5, 4], + [6, 5, 2], + [3, 5, 14], + [3, 4, 9], + [5, 4, 10], + [1, 7, 11], + ] + + result = kruskal(num_nodes, num_edges, edges) + + expected = [ + [7, 6, 1], + [2, 8, 2], + [6, 5, 2], + [0, 1, 4], + [2, 5, 4], + [2, 3, 7], + [0, 7, 8], + [3, 4, 9], + ] + + assert sorted(expected) == sorted(result) diff --git a/graphs/tests/test_min_spanning_tree_prim.py b/graphs/tests/test_min_spanning_tree_prim.py new file mode 100644 index 000000000000..048fbf595fa6 --- /dev/null +++ b/graphs/tests/test_min_spanning_tree_prim.py @@ -0,0 +1,46 @@ +from collections import defaultdict + +from graphs.minimum_spanning_tree_prims import PrimsAlgorithm as mst + + +def test_prim_successful_result(): + num_nodes, num_edges = 9, 14 # noqa: F841 + edges = [ + [0, 1, 4], + [0, 7, 8], + [1, 2, 8], + [7, 8, 7], + [7, 6, 1], + [2, 8, 2], + [8, 6, 6], + [2, 3, 7], + [2, 5, 4], + [6, 5, 2], + [3, 5, 14], + [3, 4, 9], + [5, 4, 10], + [1, 7, 11], + ] + + adjancency = defaultdict(list) + for node1, node2, cost in edges: + adjancency[node1].append([node2, cost]) + adjancency[node2].append([node1, cost]) + + result = mst(adjancency) + + expected = [ + [7, 6, 1], + [2, 8, 2], + [6, 5, 2], + [0, 1, 4], + [2, 5, 4], + [2, 3, 7], + [0, 7, 8], + [3, 4, 9], + ] + + for answer in expected: + edge = tuple(answer[:2]) + reverse = tuple(edge[::-1]) + assert edge in result or reverse in result diff --git a/greedy_method/__init__.py b/greedy_method/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/hashes/__init__.py b/hashes/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/images/__init__.py b/images/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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() diff --git a/linear_algebra/__init__.py b/linear_algebra/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/linear_algebra/src/__init__.py b/linear_algebra/src/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/linear_algebra/src/polynom_for_points.py b/linear_algebra/src/polynom_for_points.py new file mode 100644 index 000000000000..7a363723d9d2 --- /dev/null +++ b/linear_algebra/src/polynom_for_points.py @@ -0,0 +1,132 @@ +from __future__ import annotations + + +def points_to_polynomial(coordinates: list[list[int]]) -> str: + """ + coordinates is a two dimensional matrix: [[x, y], [x, y], ...] + number of points you want to use + + >>> print(points_to_polynomial([])) + The program cannot work out a fitting polynomial. + >>> print(points_to_polynomial([[]])) + The program cannot work out a fitting polynomial. + >>> print(points_to_polynomial([[1, 0], [2, 0], [3, 0]])) + f(x)=x^2*0.0+x^1*-0.0+x^0*0.0 + >>> print(points_to_polynomial([[1, 1], [2, 1], [3, 1]])) + f(x)=x^2*0.0+x^1*-0.0+x^0*1.0 + >>> print(points_to_polynomial([[1, 3], [2, 3], [3, 3]])) + f(x)=x^2*0.0+x^1*-0.0+x^0*3.0 + >>> print(points_to_polynomial([[1, 1], [2, 2], [3, 3]])) + f(x)=x^2*0.0+x^1*1.0+x^0*0.0 + >>> print(points_to_polynomial([[1, 1], [2, 4], [3, 9]])) + f(x)=x^2*1.0+x^1*-0.0+x^0*0.0 + >>> print(points_to_polynomial([[1, 3], [2, 6], [3, 11]])) + f(x)=x^2*1.0+x^1*-0.0+x^0*2.0 + >>> print(points_to_polynomial([[1, -3], [2, -6], [3, -11]])) + f(x)=x^2*-1.0+x^1*-0.0+x^0*-2.0 + >>> print(points_to_polynomial([[1, 5], [2, 2], [3, 9]])) + f(x)=x^2*5.0+x^1*-18.0+x^0*18.0 + """ + try: + check = 1 + more_check = 0 + d = coordinates[0][0] + for j in range(len(coordinates)): + if j == 0: + continue + if d == coordinates[j][0]: + more_check += 1 + solved = "x=" + str(coordinates[j][0]) + if more_check == len(coordinates) - 1: + check = 2 + break + elif more_check > 0 and more_check != len(coordinates) - 1: + check = 3 + else: + check = 1 + + if len(coordinates) == 1 and coordinates[0][0] == 0: + check = 2 + solved = "x=0" + except Exception: + check = 3 + + x = len(coordinates) + + if check == 1: + count_of_line = 0 + matrix = [] + # put the x and x to the power values in a matrix + while count_of_line < x: + count_in_line = 0 + a = coordinates[count_of_line][0] + count_line: list[int] = [] + while count_in_line < x: + count_line.append(a ** (x - (count_in_line + 1))) + count_in_line += 1 + matrix.append(count_line) + count_of_line += 1 + + count_of_line = 0 + # put the y values into a vector + vector: list[int] = [] + while count_of_line < x: + vector.append(coordinates[count_of_line][1]) + count_of_line += 1 + + count = 0 + + while count < x: + zahlen = 0 + while zahlen < x: + if count == zahlen: + zahlen += 1 + if zahlen == x: + break + bruch = matrix[zahlen][count] / matrix[count][count] + for counting_columns, item in enumerate(matrix[count]): + # manipulating all the values in the matrix + matrix[zahlen][counting_columns] -= item * bruch + # manipulating the values in the vector + vector[zahlen] -= vector[count] * bruch + zahlen += 1 + count += 1 + + count = 0 + # make solutions + solution: list[str] = [] + while count < x: + solution.append(vector[count] / matrix[count][count]) + count += 1 + + count = 0 + solved = "f(x)=" + + while count < x: + remove_e: list[str] = str(solution[count]).split("E") + if len(remove_e) > 1: + solution[count] = remove_e[0] + "*10^" + remove_e[1] + solved += "x^" + str(x - (count + 1)) + "*" + str(solution[count]) + if count + 1 != x: + solved += "+" + count += 1 + + return solved + + elif check == 2: + return solved + else: + return "The program cannot work out a fitting polynomial." + + +if __name__ == "__main__": + print(points_to_polynomial([])) + print(points_to_polynomial([[]])) + print(points_to_polynomial([[1, 0], [2, 0], [3, 0]])) + print(points_to_polynomial([[1, 1], [2, 1], [3, 1]])) + print(points_to_polynomial([[1, 3], [2, 3], [3, 3]])) + print(points_to_polynomial([[1, 1], [2, 2], [3, 3]])) + print(points_to_polynomial([[1, 1], [2, 4], [3, 9]])) + print(points_to_polynomial([[1, 3], [2, 6], [3, 11]])) + print(points_to_polynomial([[1, -3], [2, -6], [3, -11]])) + print(points_to_polynomial([[1, 5], [2, 2], [3, 9]])) diff --git a/machine_learning/__init__.py b/machine_learning/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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..0e11f958825f --- /dev/null +++ b/machine_learning/forecasting/run.py @@ -0,0 +1,157 @@ +""" +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 + >>> 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) + 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/machine_learning/gradient_boosting_regressor.py b/machine_learning/gradient_boosting_regressor.py new file mode 100644 index 000000000000..0aa0e7a10ac5 --- /dev/null +++ b/machine_learning/gradient_boosting_regressor.py @@ -0,0 +1,66 @@ +"""Implementation of GradientBoostingRegressor in sklearn using the + boston dataset which is very popular for regression problem to + predict house price. +""" + +import matplotlib.pyplot as plt +import pandas as pd +from sklearn.datasets import load_boston +from sklearn.ensemble import GradientBoostingRegressor +from sklearn.metrics import mean_squared_error, r2_score +from sklearn.model_selection import train_test_split + + +def main(): + + # loading the dataset from the sklearn + df = load_boston() + print(df.keys()) + # now let construct a data frame + df_boston = pd.DataFrame(df.data, columns=df.feature_names) + # let add the target to the dataframe + df_boston["Price"] = df.target + # print the first five rows using the head function + print(df_boston.head()) + # Summary statistics + print(df_boston.describe().T) + # Feature selection + + X = df_boston.iloc[:, :-1] + y = df_boston.iloc[:, -1] # target variable + # split the data with 75% train and 25% test sets. + X_train, X_test, y_train, y_test = train_test_split( + X, y, random_state=0, test_size=0.25 + ) + + model = GradientBoostingRegressor( + n_estimators=500, max_depth=5, min_samples_split=4, learning_rate=0.01 + ) + # training the model + model.fit(X_train, y_train) + # to see how good the model fit the data + training_score = model.score(X_train, y_train).round(3) + test_score = model.score(X_test, y_test).round(3) + print("Training score of GradientBoosting is :", training_score) + print("The test score of GradientBoosting is :", test_score) + # Let us evaluation the model by finding the errors + y_pred = model.predict(X_test) + + # The mean squared error + print("Mean squared error: %.2f" % mean_squared_error(y_test, y_pred)) + # Explained variance score: 1 is perfect prediction + print("Test Variance score: %.2f" % r2_score(y_test, y_pred)) + + # So let's run the model against the test data + fig, ax = plt.subplots() + ax.scatter(y_test, y_pred, edgecolors=(0, 0, 0)) + ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "k--", lw=4) + ax.set_xlabel("Actual") + ax.set_ylabel("Predicted") + ax.set_title("Truth vs Predicted") + # this show function will display the plotting + plt.show() + + +if __name__ == "__main__": + main() diff --git a/machine_learning/lstm/__init__.py b/machine_learning/lstm/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/machine_learning/lstm/lstm_prediction.py_tf b/machine_learning/lstm/lstm_prediction.py_tf new file mode 100644 index 000000000000..5452f0443f62 --- /dev/null +++ b/machine_learning/lstm/lstm_prediction.py_tf @@ -0,0 +1,54 @@ +""" + Create a Long Short Term Memory (LSTM) network model + An LSTM is a type of Recurrent Neural Network (RNN) as discussed at: + * http://colah.github.io/posts/2015-08-Understanding-LSTMs + * https://en.wikipedia.org/wiki/Long_short-term_memory +""" +import numpy as np +import pandas as pd +from keras.layers import LSTM, Dense +from keras.models import Sequential +from sklearn.preprocessing import MinMaxScaler + +if __name__ == "__main__": + """ + First part of building a model is to get the data and prepare + it for our model. You can use any dataset for stock prediction + make sure you set the price column on line number 21. Here we + use a dataset which have the price on 3rd column. + """ + df = pd.read_csv("sample_data.csv", header=None) + len_data = df.shape[:1][0] + # If you're using some other dataset input the target column + actual_data = df.iloc[:, 1:2] + actual_data = actual_data.values.reshape(len_data, 1) + actual_data = MinMaxScaler().fit_transform(actual_data) + look_back = 10 + forward_days = 5 + periods = 20 + division = len_data - periods * look_back + train_data = actual_data[:division] + test_data = actual_data[division - look_back :] + train_x, train_y = [], [] + test_x, test_y = [], [] + + for i in range(0, len(train_data) - forward_days - look_back + 1): + train_x.append(train_data[i : i + look_back]) + train_y.append(train_data[i + look_back : i + look_back + forward_days]) + for i in range(0, len(test_data) - forward_days - look_back + 1): + test_x.append(test_data[i : i + look_back]) + test_y.append(test_data[i + look_back : i + look_back + forward_days]) + x_train = np.array(train_x) + x_test = np.array(test_x) + y_train = np.array([list(i.ravel()) for i in train_y]) + y_test = np.array([list(i.ravel()) for i in test_y]) + + model = Sequential() + model.add(LSTM(128, input_shape=(look_back, 1), return_sequences=True)) + model.add(LSTM(64, input_shape=(128, 1))) + model.add(Dense(forward_days)) + model.compile(loss="mean_squared_error", optimizer="adam") + history = model.fit( + x_train, y_train, epochs=150, verbose=1, shuffle=True, batch_size=4 + ) + pred = model.predict(x_test) 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() 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)) diff --git a/maths/images/__init__.py b/maths/images/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/maths/series/__init__.py b/maths/series/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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() diff --git a/matrix/__init__.py b/matrix/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/matrix/tests/__init__.py b/matrix/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/networking_flow/__init__.py b/networking_flow/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/neural_network/__init__.py b/neural_network/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/other/__init__.py b/other/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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.") diff --git a/other/doomsday.py b/other/doomsday.py new file mode 100644 index 000000000000..d8fe261156a1 --- /dev/null +++ b/other/doomsday.py @@ -0,0 +1,59 @@ +#!/bin/python3 +# Doomsday algorithm info: https://en.wikipedia.org/wiki/Doomsday_rule + +DOOMSDAY_LEAP = [4, 1, 7, 4, 2, 6, 4, 1, 5, 3, 7, 5] +DOOMSDAY_NOT_LEAP = [3, 7, 7, 4, 2, 6, 4, 1, 5, 3, 7, 5] +WEEK_DAY_NAMES = { + 0: "Sunday", + 1: "Monday", + 2: "Tuesday", + 3: "Wednesday", + 4: "Thursday", + 5: "Friday", + 6: "Saturday", +} + + +def get_week_day(year: int, month: int, day: int) -> str: + """Returns the week-day name out of a given date. + + >>> get_week_day(2020, 10, 24) + 'Saturday' + >>> get_week_day(2017, 10, 24) + 'Tuesday' + >>> get_week_day(2019, 5, 3) + 'Friday' + >>> get_week_day(1970, 9, 16) + 'Wednesday' + >>> get_week_day(1870, 8, 13) + 'Saturday' + >>> get_week_day(2040, 3, 14) + 'Wednesday' + + """ + # minimal input check: + assert len(str(year)) > 2, "year should be in YYYY format" + assert 1 <= month <= 12, "month should be between 1 to 12" + assert 1 <= day <= 31, "day should be between 1 to 31" + + # Doomsday algorithm: + century = year // 100 + century_anchor = (5 * (century % 4) + 2) % 7 + centurian = year % 100 + centurian_m = centurian % 12 + dooms_day = ( + (centurian // 12) + centurian_m + (centurian_m // 4) + century_anchor + ) % 7 + day_anchor = ( + DOOMSDAY_NOT_LEAP[month - 1] + if (year % 4 != 0) or (centurian == 0 and (year % 400) == 0) + else DOOMSDAY_LEAP[month - 1] + ) + week_day = (dooms_day + day - day_anchor) % 7 + return WEEK_DAY_NAMES[week_day] + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/other/max_sum_sliding_window.py b/other/max_sum_sliding_window.py new file mode 100644 index 000000000000..593cb5c8bd67 --- /dev/null +++ b/other/max_sum_sliding_window.py @@ -0,0 +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)}") 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)}") 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) = }") diff --git a/project_euler/__init__.py b/project_euler/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_001/__init__.py b/project_euler/problem_001/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_001/sol1.py b/project_euler/problem_001/sol1.py new file mode 100644 index 000000000000..85ad32294c9b --- /dev/null +++ b/project_euler/problem_001/sol1.py @@ -0,0 +1,33 @@ +""" +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 + >>> solution(-7) + 0 + """ + + return sum([e for e in range(3, n) if e % 3 == 0 or e % 5 == 0]) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol2.py b/project_euler/problem_001/sol2.py new file mode 100644 index 000000000000..7093d3513378 --- /dev/null +++ b/project_euler/problem_001/sol2.py @@ -0,0 +1,38 @@ +""" +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 + """ + + total = 0 + terms = (n - 1) // 3 + total += ((terms) * (6 + (terms - 1) * 3)) // 2 # total of an A.P. + terms = (n - 1) // 5 + total += ((terms) * (10 + (terms - 1) * 5)) // 2 + terms = (n - 1) // 15 + total -= ((terms) * (30 + (terms - 1) * 15)) // 2 + return total + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol3.py b/project_euler/problem_001/sol3.py new file mode 100644 index 000000000000..8267fec84155 --- /dev/null +++ b/project_euler/problem_001/sol3.py @@ -0,0 +1,64 @@ +""" +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: + """ + This solution is based on the pattern that the successive numbers in the + series follow: 0+3,+2,+1,+3,+1,+2,+3. + 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 + """ + + total = 0 + num = 0 + while 1: + num += 3 + if num >= n: + break + total += num + num += 2 + if num >= n: + break + total += num + num += 1 + if num >= n: + break + total += num + num += 3 + if num >= n: + break + total += num + num += 1 + if num >= n: + break + total += num + num += 2 + if num >= n: + break + total += num + num += 3 + if num >= n: + break + total += num + return total + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol4.py b/project_euler/problem_001/sol4.py new file mode 100644 index 000000000000..a0643c05b34f --- /dev/null +++ b/project_euler/problem_001/sol4.py @@ -0,0 +1,52 @@ +""" +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 new file mode 100644 index 000000000000..7f0b0bd1bc7c --- /dev/null +++ b/project_euler/problem_001/sol5.py @@ -0,0 +1,32 @@ +""" +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. + A straightforward pythonic solution using list comprehension. + + >>> solution(3) + 0 + >>> solution(4) + 3 + >>> solution(10) + 23 + >>> solution(600) + 83700 + """ + + return sum([i for i in range(n) if i % 3 == 0 or i % 5 == 0]) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol6.py b/project_euler/problem_001/sol6.py new file mode 100644 index 000000000000..8ddce18ced04 --- /dev/null +++ b/project_euler/problem_001/sol6.py @@ -0,0 +1,39 @@ +""" +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 + """ + + a = 3 + result = 0 + while a < n: + if a % 3 == 0 or a % 5 == 0: + result += a + elif a % 15 == 0: + result -= a + a += 1 + return result + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_001/sol7.py b/project_euler/problem_001/sol7.py new file mode 100644 index 000000000000..8f5d1977fdde --- /dev/null +++ b/project_euler/problem_001/sol7.py @@ -0,0 +1,37 @@ +""" +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 + """ + + result = 0 + for i in range(n): + if i % 3 == 0: + result += i + elif i % 5 == 0: + result += i + return result + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_002/__init__.py b/project_euler/problem_002/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_002/sol1.py b/project_euler/problem_002/sol1.py new file mode 100644 index 000000000000..539f68fb6bc1 --- /dev/null +++ b/project_euler/problem_002/sol1.py @@ -0,0 +1,48 @@ +""" +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 + """ + + i = 1 + j = 2 + total = 0 + while j <= n: + if j % 2 == 0: + total += j + i, j = j, i + j + + return total + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol2.py b/project_euler/problem_002/sol2.py new file mode 100644 index 000000000000..9033d0a69bcf --- /dev/null +++ b/project_euler/problem_002/sol2.py @@ -0,0 +1,46 @@ +""" +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 new file mode 100644 index 000000000000..3ae175a99815 --- /dev/null +++ b/project_euler/problem_002/sol3.py @@ -0,0 +1,48 @@ +""" +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 + """ + + if n <= 1: + return 0 + a = 0 + b = 2 + count = 0 + while 4 * b + a <= n: + a, b = b, 4 * b + a + count += a + return count + b + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol4.py b/project_euler/problem_002/sol4.py new file mode 100644 index 000000000000..70b7d6a80a1d --- /dev/null +++ b/project_euler/problem_002/sol4.py @@ -0,0 +1,72 @@ +""" +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 +""" +import math +from decimal import Decimal, getcontext + + +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 + >>> solution(3.4) + 2 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter n must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") + if n <= 0: + raise ValueError("Parameter n must be greater than or equal to one.") + getcontext().prec = 100 + phi = (Decimal(5) ** Decimal(0.5) + 1) / Decimal(2) + + index = (math.floor(math.log(n * (phi + 2), phi) - 1) // 3) * 3 + 2 + num = Decimal(round(phi ** Decimal(index + 1))) / (phi + 2) + total = num // 2 + return int(total) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_002/sol5.py b/project_euler/problem_002/sol5.py new file mode 100644 index 000000000000..390fd19ef638 --- /dev/null +++ b/project_euler/problem_002/sol5.py @@ -0,0 +1,52 @@ +""" +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 + """ + + fib = [0, 1] + i = 0 + while fib[i] <= n: + fib.append(fib[i] + fib[i + 1]) + if fib[i + 2] > n: + break + i += 1 + total = 0 + for j in range(len(fib) - 1): + if fib[j] % 2 == 0: + total += fib[j] + + return total + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_003/__init__.py b/project_euler/problem_003/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_003/sol1.py b/project_euler/problem_003/sol1.py new file mode 100644 index 000000000000..3441dbf9e0b3 --- /dev/null +++ b/project_euler/problem_003/sol1.py @@ -0,0 +1,104 @@ +""" +Project Euler Problem 3: https://projecteuler.net/problem=3 + +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. + + >>> isprime(2) + True + >>> isprime(3) + True + >>> isprime(27) + False + >>> isprime(2999) + True + >>> isprime(0) + Traceback (most recent call last): + ... + ValueError: Parameter num must be greater than or equal to two. + >>> isprime(1) + Traceback (most recent call last): + ... + ValueError: Parameter num must be greater than or equal to two. + """ + + if num <= 1: + raise ValueError("Parameter num must be greater than or equal to two.") + if num == 2: + return True + elif num % 2 == 0: + return False + for i in range(3, int(math.sqrt(num)) + 1, 2): + if num % i == 0: + return False + return True + + +def solution(n: int = 600851475143) -> int: + """ + Returns the largest prime factor of a given number n. + + >>> solution(13195) + 29 + >>> solution(10) + 5 + >>> solution(17) + 17 + >>> solution(3.4) + 3 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter n must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") + if n <= 0: + raise ValueError("Parameter n must be greater than or equal to one.") + max_number = 0 + if isprime(n): + return n + while n % 2 == 0: + n //= 2 + if isprime(n): + return n + for i in range(3, int(math.sqrt(n)) + 1, 2): + if n % i == 0: + if isprime(n / i): + max_number = n / i + break + elif isprime(i): + max_number = i + return max_number + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_003/sol2.py b/project_euler/problem_003/sol2.py new file mode 100644 index 000000000000..0af0daceed06 --- /dev/null +++ b/project_euler/problem_003/sol2.py @@ -0,0 +1,64 @@ +""" +Project Euler Problem 3: https://projecteuler.net/problem=3 + +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. + + >>> solution(13195) + 29 + >>> solution(10) + 5 + >>> solution(17) + 17 + >>> solution(3.4) + 3 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter n must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") + if n <= 0: + raise ValueError("Parameter n must be greater than or equal to one.") + prime = 1 + i = 2 + while i * i <= n: + while n % i == 0: + prime = i + n //= i + i += 1 + if n > 1: + prime = n + return int(prime) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_003/sol3.py b/project_euler/problem_003/sol3.py new file mode 100644 index 000000000000..bc6f1d2f61ca --- /dev/null +++ b/project_euler/problem_003/sol3.py @@ -0,0 +1,66 @@ +""" +Project Euler Problem 3: https://projecteuler.net/problem=3 + +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. + + >>> solution(13195) + 29 + >>> solution(10) + 5 + >>> solution(17) + 17 + >>> solution(3.4) + 3 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter n must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") + if n <= 0: + raise ValueError("Parameter n must be greater than or equal to one.") + i = 2 + ans = 0 + if n == 2: + return 2 + while n > 2: + while n % i != 0: + i += 1 + ans = i + while n % i == 0: + n = n / i + i += 1 + return int(ans) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_004/__init__.py b/project_euler/problem_004/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_004/sol1.py b/project_euler/problem_004/sol1.py new file mode 100644 index 000000000000..db6133a1a1d2 --- /dev/null +++ b/project_euler/problem_004/sol1.py @@ -0,0 +1,53 @@ +""" +Project Euler Problem 4: https://projecteuler.net/problem=4 + +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 + numbers which is less than n. + + >>> solution(20000) + 19591 + >>> solution(30000) + 29992 + >>> solution(40000) + 39893 + >>> solution(10000) + Traceback (most recent call last): + ... + ValueError: That number is larger than our acceptable range. + """ + + # fetches the next number + for number in range(n - 1, 9999, -1): + + str_number = str(number) + + # checks whether 'str_number' is a palindrome. + if str_number == str_number[::-1]: + + divisor = 999 + + # if 'number' is a product of two 3-digit numbers + # then number is the answer otherwise fetch next number. + while divisor != 99: + if (number % divisor == 0) and (len(str(number // divisor)) == 3.0): + return number + divisor -= 1 + raise ValueError("That number is larger than our acceptable range.") + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_004/sol2.py b/project_euler/problem_004/sol2.py new file mode 100644 index 000000000000..abc880966d58 --- /dev/null +++ b/project_euler/problem_004/sol2.py @@ -0,0 +1,39 @@ +""" +Project Euler Problem 4: https://projecteuler.net/problem=4 + +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 + numbers which is less than n. + + >>> solution(20000) + 19591 + >>> solution(30000) + 29992 + >>> 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): + product_string = str(i * j) + if product_string == product_string[::-1] and i * j < n: + answer = max(answer, i * j) + return answer + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_005/__init__.py b/project_euler/problem_005/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_005/sol1.py b/project_euler/problem_005/sol1.py new file mode 100644 index 000000000000..f272c102d2bb --- /dev/null +++ b/project_euler/problem_005/sol1.py @@ -0,0 +1,69 @@ +""" +Project Euler Problem 5: https://projecteuler.net/problem=5 + +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 + with no remainder) by all of the numbers from 1 to n. + + >>> solution(10) + 2520 + >>> solution(15) + 360360 + >>> solution(22) + 232792560 + >>> solution(3.4) + 6 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter n must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter n must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") + if n <= 0: + raise ValueError("Parameter n must be greater than or equal to one.") + i = 0 + while 1: + i += n * (n - 1) + nfound = 0 + for j in range(2, n): + if i % j != 0: + nfound = 1 + break + if nfound == 0: + if i == 0: + i = 1 + return i + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_005/sol2.py b/project_euler/problem_005/sol2.py new file mode 100644 index 000000000000..c88044487d20 --- /dev/null +++ b/project_euler/problem_005/sol2.py @@ -0,0 +1,75 @@ +""" +Project Euler Problem 5: https://projecteuler.net/problem=5 + +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 +""" + + +def gcd(x: int, y: int) -> int: + """ + Euclidean GCD algorithm (Greatest Common Divisor) + + >>> gcd(0, 0) + 0 + >>> gcd(23, 42) + 1 + >>> gcd(15, 33) + 3 + >>> gcd(12345, 67890) + 15 + """ + + 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 + with no remainder) by all of the numbers from 1 to n. + + >>> solution(10) + 2520 + >>> solution(15) + 360360 + >>> solution(22) + 232792560 + """ + + g = 1 + for i in range(1, n + 1): + g = lcm(g, i) + return g + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_006/__init__.py b/project_euler/problem_006/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_006/sol1.py b/project_euler/problem_006/sol1.py new file mode 100644 index 000000000000..61dd7a321011 --- /dev/null +++ b/project_euler/problem_006/sol1.py @@ -0,0 +1,44 @@ +""" +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 + +The square of the sum of the first ten natural numbers is, + (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. + +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 + natural numbers and the square of the sum. + + >>> solution(10) + 2640 + >>> solution(15) + 13160 + >>> solution(20) + 41230 + >>> solution(50) + 1582700 + """ + + sum_of_squares = 0 + sum_of_ints = 0 + for i in range(1, n + 1): + sum_of_squares += i ** 2 + sum_of_ints += i + return sum_of_ints ** 2 - sum_of_squares + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol2.py b/project_euler/problem_006/sol2.py new file mode 100644 index 000000000000..cd1bc5071e0e --- /dev/null +++ b/project_euler/problem_006/sol2.py @@ -0,0 +1,41 @@ +""" +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 + +The square of the sum of the first ten natural numbers is, + (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. + +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 + natural numbers and the square of the sum. + + >>> solution(10) + 2640 + >>> solution(15) + 13160 + >>> solution(20) + 41230 + >>> solution(50) + 1582700 + """ + + sum_cubes = (n * (n + 1) // 2) ** 2 + sum_squares = n * (n + 1) * (2 * n + 1) // 6 + return sum_cubes - sum_squares + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol3.py b/project_euler/problem_006/sol3.py new file mode 100644 index 000000000000..c87931309574 --- /dev/null +++ b/project_euler/problem_006/sol3.py @@ -0,0 +1,42 @@ +""" +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 + +The square of the sum of the first ten natural numbers is, + (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. + +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 + natural numbers and the square of the sum. + + >>> solution(10) + 2640 + >>> solution(15) + 13160 + >>> solution(20) + 41230 + >>> solution(50) + 1582700 + """ + + 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__": + print(f"{solution() = }") diff --git a/project_euler/problem_006/sol4.py b/project_euler/problem_006/sol4.py new file mode 100644 index 000000000000..748b141490a0 --- /dev/null +++ b/project_euler/problem_006/sol4.py @@ -0,0 +1,41 @@ +""" +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 + +The square of the sum of the first ten natural numbers is, + (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. + +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 + natural numbers and the square of the sum. + + >>> solution(10) + 2640 + >>> solution(15) + 13160 + >>> solution(20) + 41230 + >>> solution(50) + 1582700 + """ + + 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__": + print(f"{solution() = }") diff --git a/project_euler/problem_007/__init__.py b/project_euler/problem_007/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_007/sol1.py b/project_euler/problem_007/sol1.py new file mode 100644 index 000000000000..78fbcb511611 --- /dev/null +++ b/project_euler/problem_007/sol1.py @@ -0,0 +1,76 @@ +""" +Project Euler Problem 7: https://projecteuler.net/problem=7 + +10001st prime + +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. + +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 + + >>> 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: + return False + else: + sq = int(sqrt(num)) + 1 + for i in range(3, sq, 2): + if num % i == 0: + return False + return True + + +def solution(nth: int = 10001) -> int: + """ + Returns the n-th prime number. + + >>> solution(6) + 13 + >>> solution(1) + 2 + >>> solution(3) + 5 + >>> solution(20) + 71 + >>> solution(50) + 229 + >>> solution(100) + 541 + """ + + count = 0 + number = 1 + while count != nth and number < 3: + number += 1 + if is_prime(number): + count += 1 + while count != nth: + number += 2 + if is_prime(number): + count += 1 + return number + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_007/sol2.py b/project_euler/problem_007/sol2.py new file mode 100644 index 000000000000..b395c631b766 --- /dev/null +++ b/project_euler/problem_007/sol2.py @@ -0,0 +1,88 @@ +""" +Project Euler Problem 7: https://projecteuler.net/problem=7 + +10001st prime + +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. + +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 + + >>> 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 + return True + + +def solution(nth: int = 10001) -> int: + """ + Returns the n-th prime number. + + >>> solution(6) + 13 + >>> solution(1) + 2 + >>> solution(3) + 5 + >>> solution(20) + 71 + >>> solution(50) + 229 + >>> solution(100) + 541 + >>> solution(3.4) + 5 + >>> solution(0) + Traceback (most recent call last): + ... + ValueError: Parameter nth must be greater than or equal to one. + >>> solution(-17) + Traceback (most recent call last): + ... + ValueError: Parameter nth must be greater than or equal to one. + >>> solution([]) + Traceback (most recent call last): + ... + TypeError: Parameter nth must be int or castable to int. + >>> solution("asd") + Traceback (most recent call last): + ... + 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 castable to int.") from None + if nth <= 0: + raise ValueError("Parameter nth must be greater than or equal to one.") + primes = [] + num = 2 + while len(primes) < nth: + if isprime(num): + primes.append(num) + num += 1 + else: + num += 1 + return primes[len(primes) - 1] + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_007/sol3.py b/project_euler/problem_007/sol3.py new file mode 100644 index 000000000000..7911fa3e9d6f --- /dev/null +++ b/project_euler/problem_007/sol3.py @@ -0,0 +1,68 @@ +""" +Project Euler Problem 7: https://projecteuler.net/problem=7 + +10001st prime + +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we +can see that the 6th prime is 13. + +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 + + >>> 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): + yield num + num += 1 + + +def solution(nth: int = 10001) -> int: + """ + Returns the n-th prime number. + + >>> solution(6) + 13 + >>> solution(1) + 2 + >>> solution(3) + 5 + >>> solution(20) + 71 + >>> solution(50) + 229 + >>> solution(100) + 541 + """ + return next(itertools.islice(prime_generator(), nth - 1, nth)) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_008/__init__.py b/project_euler/problem_008/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_008/sol1.py b/project_euler/problem_008/sol1.py new file mode 100644 index 000000000000..796080127778 --- /dev/null +++ b/project_euler/problem_008/sol1.py @@ -0,0 +1,82 @@ +""" +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 + +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\ +96983520312774506326239578318016984801869478851843\ +85861560789112949495459501737958331952853208805511\ +12540698747158523863050715693290963295227443043557\ +66896648950445244523161731856403098711121722383113\ +62229893423380308135336276614282806444486645238749\ +30358907296290491560440772390713810515859307960866\ +70172427121883998797908792274921901699720888093776\ +65727333001053367881220235421809751254540594752243\ +52584907711670556013604839586446706324415722155397\ +53697817977846174064955149290862569321978468622482\ +83972241375657056057490261407972968652414535100474\ +82166370484403199890008895243450658541227588666881\ +16427171479924442928230863465674813919123162824586\ +17866458359124566529476545682848912883142607690042\ +24219022671055626321111109370544217506941658960408\ +07198403850962455444362981230987879927244284909188\ +84580156166097919133875499200524063689912560717606\ +05886116467109405077541002256983155200055935729725\ +71636269561882670428252483600823257530420752963450""" + + +def solution(n: str = N) -> int: + """ + Find the thirteen adjacent digits in the 1000-digit number n that have + the greatest product and returns it. + + >>> solution("13978431290823798458352374") + 609638400 + >>> solution("13978431295823798458352374") + 2612736000 + >>> solution("1397843129582379841238352374") + 209018880 + """ + + largest_product = -sys.maxsize - 1 + for i in range(len(n) - 12): + product = 1 + for j in range(13): + product *= int(n[i + j]) + if product > largest_product: + largest_product = product + return largest_product + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_008/sol2.py b/project_euler/problem_008/sol2.py new file mode 100644 index 000000000000..d2c1b4f7ca48 --- /dev/null +++ b/project_euler/problem_008/sol2.py @@ -0,0 +1,81 @@ +""" +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 + +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 = ( + "73167176531330624919225119674426574742355349194934" + "96983520312774506326239578318016984801869478851843" + "85861560789112949495459501737958331952853208805511" + "12540698747158523863050715693290963295227443043557" + "66896648950445244523161731856403098711121722383113" + "62229893423380308135336276614282806444486645238749" + "30358907296290491560440772390713810515859307960866" + "70172427121883998797908792274921901699720888093776" + "65727333001053367881220235421809751254540594752243" + "52584907711670556013604839586446706324415722155397" + "53697817977846174064955149290862569321978468622482" + "83972241375657056057490261407972968652414535100474" + "82166370484403199890008895243450658541227588666881" + "16427171479924442928230863465674813919123162824586" + "17866458359124566529476545682848912883142607690042" + "24219022671055626321111109370544217506941658960408" + "07198403850962455444362981230987879927244284909188" + "84580156166097919133875499200524063689912560717606" + "05886116467109405077541002256983155200055935729725" + "71636269561882670428252483600823257530420752963450" +) + + +def solution(n: str = N) -> int: + """ + Find the thirteen adjacent digits in the 1000-digit number n that have + the greatest product and returns it. + + >>> solution("13978431290823798458352374") + 609638400 + >>> solution("13978431295823798458352374") + 2612736000 + >>> solution("1397843129582379841238352374") + 209018880 + """ + + return max( + [ + reduce(lambda x, y: int(x) * int(y), n[i : i + 13]) + for i in range(len(n) - 12) + ] + ) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_008/sol3.py b/project_euler/problem_008/sol3.py new file mode 100644 index 000000000000..4b99d0ea6e76 --- /dev/null +++ b/project_euler/problem_008/sol3.py @@ -0,0 +1,94 @@ +""" +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 + +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\ +96983520312774506326239578318016984801869478851843\ +85861560789112949495459501737958331952853208805511\ +12540698747158523863050715693290963295227443043557\ +66896648950445244523161731856403098711121722383113\ +62229893423380308135336276614282806444486645238749\ +30358907296290491560440772390713810515859307960866\ +70172427121883998797908792274921901699720888093776\ +65727333001053367881220235421809751254540594752243\ +52584907711670556013604839586446706324415722155397\ +53697817977846174064955149290862569321978468622482\ +83972241375657056057490261407972968652414535100474\ +82166370484403199890008895243450658541227588666881\ +16427171479924442928230863465674813919123162824586\ +17866458359124566529476545682848912883142607690042\ +24219022671055626321111109370544217506941658960408\ +07198403850962455444362981230987879927244284909188\ +84580156166097919133875499200524063689912560717606\ +05886116467109405077541002256983155200055935729725\ +71636269561882670428252483600823257530420752963450""" + + +def str_eval(s: str) -> int: + """ + 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) + return product + + +def solution(n: str = N) -> int: + """ + Find the thirteen adjacent digits in the 1000-digit number n that have + the greatest product and returns it. + """ + + largest_product = -sys.maxsize - 1 + substr = n[:13] + cur_index = 13 + while cur_index < len(n) - 13: + if int(n[cur_index]) >= int(substr[0]): + substr = substr[1:] + n[cur_index] + cur_index += 1 + else: + largest_product = max(largest_product, str_eval(substr)) + substr = n[cur_index : cur_index + 13] + cur_index += 13 + return largest_product + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_009/__init__.py b/project_euler/problem_009/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_009/sol1.py b/project_euler/problem_009/sol1.py new file mode 100644 index 000000000000..a58ea943e48b --- /dev/null +++ b/project_euler/problem_009/sol1.py @@ -0,0 +1,79 @@ +""" +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 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 + + # 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): + if a < b < c: + if (a ** 2) + (b ** 2) == (c ** 2): + if (a + b + c) == 1000: + return a * b * c + + +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 + + # 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 + if a < b < c and (a ** 2) + (b ** 2) == (c ** 2): + return a * b * c + + +def benchmark() -> None: + """ + Benchmark code comparing two different version function. + """ + import timeit + + print( + timeit.timeit("solution()", setup="from __main__ import solution", number=1000) + ) + print( + timeit.timeit( + "solution_fast()", setup="from __main__ import solution_fast", number=1000 + ) + ) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_009/sol2.py b/project_euler/problem_009/sol2.py new file mode 100644 index 000000000000..722ad522ee45 --- /dev/null +++ b/project_euler/problem_009/sol2.py @@ -0,0 +1,48 @@ +""" +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 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(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 + b = (n * n - 2 * a * n) // (2 * n - 2 * a) + c = n - a - b + if c * c == (a * a + b * b): + candidate = a * b * c + if candidate >= product: + product = candidate + return product + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_009/sol3.py b/project_euler/problem_009/sol3.py new file mode 100644 index 000000000000..03aed4b70761 --- /dev/null +++ b/project_euler/problem_009/sol3.py @@ -0,0 +1,41 @@ +""" +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 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 + + # 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) + for b in range(a, 999) + if (a * a + b * b == (1000 - a - b) ** 2) + ][0] + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_010/__init__.py b/project_euler/problem_010/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_010/sol1.py b/project_euler/problem_010/sol1.py new file mode 100644 index 000000000000..bd49b3523c97 --- /dev/null +++ b/project_euler/problem_010/sol1.py @@ -0,0 +1,65 @@ +""" +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes + +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. + + >>> is_prime(2) + True + >>> is_prime(3) + True + >>> is_prime(27) + False + >>> is_prime(2999) + True + """ + + for i in range(2, int(sqrt(n)) + 1): + if n % i == 0: + return False + + return True + + +def solution(n: int = 2000000) -> int: + """ + Returns the sum of all the primes below n. + + >>> solution(1000) + 76127 + >>> solution(5000) + 1548136 + >>> solution(10000) + 5736396 + >>> solution(7) + 10 + """ + + if n > 2: + sum_of_primes = 2 + else: + return 0 + + for i in range(3, n, 2): + if is_prime(i): + sum_of_primes += i + + return sum_of_primes + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_010/sol2.py b/project_euler/problem_010/sol2.py new file mode 100644 index 000000000000..3a2f485dde50 --- /dev/null +++ b/project_euler/problem_010/sol2.py @@ -0,0 +1,67 @@ +""" +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes + +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 +from typing import Iterator + + +def is_prime(number: int) -> bool: + """ + Returns boolean representing primality of given number num. + + >>> is_prime(2) + True + >>> is_prime(3) + True + >>> is_prime(27) + False + >>> 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): + yield num + num += 1 + + +def solution(n: int = 2000000) -> int: + """ + Returns the sum of all the primes below n. + + >>> solution(1000) + 76127 + >>> solution(5000) + 1548136 + >>> solution(10000) + 5736396 + >>> solution(7) + 10 + """ + + return sum(takewhile(lambda x: x < n, prime_generator())) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_010/sol3.py b/project_euler/problem_010/sol3.py new file mode 100644 index 000000000000..f49d9393c7af --- /dev/null +++ b/project_euler/problem_010/sol3.py @@ -0,0 +1,61 @@ +""" +Project Euler Problem 10: https://projecteuler.net/problem=10 + +Summation of primes + +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: + + 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(1000) + 76127 + >>> solution(5000) + 1548136 + >>> solution(10000) + 5736396 + >>> solution(7) + 10 + >>> solution(7.1) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: 'float' object cannot be interpreted as an integer + >>> solution(-7) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + IndexError: list assignment index out of range + >>> 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 + + for i in range(2, int(n ** 0.5) + 1): + if primality_list[i] == 0: + for j in range(i * i, n + 1, i): + primality_list[j] = 1 + sum_of_primes = 0 + for i in range(n): + if primality_list[i] == 0: + sum_of_primes += i + return sum_of_primes + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_011/__init__.py b/project_euler/problem_011/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_011/grid.txt b/project_euler/problem_011/grid.txt new file mode 100644 index 000000000000..4ac24518973e --- /dev/null +++ b/project_euler/problem_011/grid.txt @@ -0,0 +1,20 @@ +08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 +49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 +81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 +52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 +22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 +24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 +32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 +67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 +24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 +21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 +78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 +16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 +86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 +19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 +04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 +88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 +04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 +20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 +20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 +01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48 diff --git a/project_euler/problem_011/sol1.py b/project_euler/problem_011/sol1.py new file mode 100644 index 000000000000..9dea73e8cef2 --- /dev/null +++ b/project_euler/problem_011/sol1.py @@ -0,0 +1,88 @@ +""" +What is the greatest product of four adjacent numbers (horizontally, +vertically, or diagonally) in this 20x20 array? + +08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 +49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 +81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 +52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 +22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 +24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 +32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 +67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 +24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 +21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 +78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 +16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 +86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 +19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 +04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 +88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 +04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 +20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 +20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 +01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48 +""" + +import os + + +def largest_product(grid): + nColumns = len(grid[0]) + nRows = len(grid) + + largest = 0 + lrDiagProduct = 0 + rlDiagProduct = 0 + + # Check vertically, horizontally, diagonally at the same time (only works + # for nxn grid) + for i in range(nColumns): + for j in range(nRows - 3): + vertProduct = grid[j][i] * grid[j + 1][i] * grid[j + 2][i] * grid[j + 3][i] + horzProduct = grid[i][j] * grid[i][j + 1] * grid[i][j + 2] * grid[i][j + 3] + + # Left-to-right diagonal (\) product + if i < nColumns - 3: + lrDiagProduct = ( + grid[i][j] + * grid[i + 1][j + 1] + * grid[i + 2][j + 2] + * grid[i + 3][j + 3] + ) + + # Right-to-left diagonal(/) product + if i > 2: + rlDiagProduct = ( + grid[i][j] + * grid[i - 1][j + 1] + * grid[i - 2][j + 2] + * grid[i - 3][j + 3] + ) + + maxProduct = max(vertProduct, horzProduct, lrDiagProduct, rlDiagProduct) + if maxProduct > largest: + largest = maxProduct + + return largest + + +def solution(): + """Returns the greatest product of four adjacent numbers (horizontally, + vertically, or diagonally). + + >>> solution() + 70600674 + """ + grid = [] + with open(os.path.dirname(__file__) + "/grid.txt") as file: + for line in file: + grid.append(line.strip("\n").split(" ")) + + grid = [[int(i) for i in grid[j]] for j in range(len(grid))] + + return largest_product(grid) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_011/sol2.py b/project_euler/problem_011/sol2.py new file mode 100644 index 000000000000..839ca6717571 --- /dev/null +++ b/project_euler/problem_011/sol2.py @@ -0,0 +1,75 @@ +""" +What is the greatest product of four adjacent numbers (horizontally, +vertically, or diagonally) in this 20x20 array? + +08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 +49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 +81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 +52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 +22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 +24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 +32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 +67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 +24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 +21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 +78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 +16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 +86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 +19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 +04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 +88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 +04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 +20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 +20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 +01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48 +""" + +import os + + +def solution(): + """Returns the greatest product of four adjacent numbers (horizontally, + vertically, or diagonally). + + >>> solution() + 70600674 + """ + with open(os.path.dirname(__file__) + "/grid.txt") as f: + l = [] # noqa: E741 + for i in range(20): + l.append([int(x) for x in f.readline().split()]) + + maximum = 0 + + # right + for i in range(20): + for j in range(17): + temp = l[i][j] * l[i][j + 1] * l[i][j + 2] * l[i][j + 3] + if temp > maximum: + maximum = temp + + # down + for i in range(17): + for j in range(20): + temp = l[i][j] * l[i + 1][j] * l[i + 2][j] * l[i + 3][j] + if temp > maximum: + maximum = temp + + # diagonal 1 + for i in range(17): + for j in range(17): + temp = l[i][j] * l[i + 1][j + 1] * l[i + 2][j + 2] * l[i + 3][j + 3] + if temp > maximum: + maximum = temp + + # diagonal 2 + for i in range(17): + for j in range(3, 20): + temp = l[i][j] * l[i + 1][j - 1] * l[i + 2][j - 2] * l[i + 3][j - 3] + if temp > maximum: + maximum = temp + return maximum + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_012/__init__.py b/project_euler/problem_012/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_012/sol1.py b/project_euler/problem_012/sol1.py new file mode 100644 index 000000000000..7e080c4e45a1 --- /dev/null +++ b/project_euler/problem_012/sol1.py @@ -0,0 +1,60 @@ +""" +Highly divisible triangular numbers +Problem 12 +The sequence of triangle numbers is generated by adding the natural numbers. So +the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten +terms would be: + +1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ... + +Let us list the factors of the first seven triangle numbers: + + 1: 1 + 3: 1,3 + 6: 1,2,3,6 +10: 1,2,5,10 +15: 1,3,5,15 +21: 1,3,7,21 +28: 1,2,4,7,14,28 +We can see that 28 is the first triangle number to have over five divisors. + +What is the value of the first triangle number to have over five hundred +divisors? +""" +from math import sqrt + + +def count_divisors(n): + nDivisors = 0 + for i in range(1, int(sqrt(n)) + 1): + if n % i == 0: + nDivisors += 2 + # check if n is perfect square + if n ** 0.5 == int(n ** 0.5): + nDivisors -= 1 + return nDivisors + + +def solution(): + """Returns the value of the first triangle number to have over five hundred + divisors. + + # The code below has been commented due to slow execution affecting Travis. + # >>> solution() + # 76576500 + """ + tNum = 1 + i = 1 + + while True: + i += 1 + tNum += i + + if count_divisors(tNum) > 500: + break + + return tNum + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_012/sol2.py b/project_euler/problem_012/sol2.py new file mode 100644 index 000000000000..5ff0d8349b90 --- /dev/null +++ b/project_euler/problem_012/sol2.py @@ -0,0 +1,47 @@ +""" +Highly divisible triangular numbers +Problem 12 +The sequence of triangle numbers is generated by adding the natural numbers. So +the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten +terms would be: + +1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ... + +Let us list the factors of the first seven triangle numbers: + + 1: 1 + 3: 1,3 + 6: 1,2,3,6 +10: 1,2,5,10 +15: 1,3,5,15 +21: 1,3,7,21 +28: 1,2,4,7,14,28 +We can see that 28 is the first triangle number to have over five divisors. + +What is the value of the first triangle number to have over five hundred +divisors? +""" + + +def triangle_number_generator(): + for n in range(1, 1000000): + yield n * (n + 1) // 2 + + +def count_divisors(n): + return sum([2 for i in range(1, int(n ** 0.5) + 1) if n % i == 0 and i * i != n]) + + +def solution(): + """Returns the value of the first triangle number to have over five hundred + divisors. + + # The code below has been commented due to slow execution affecting Travis. + # >>> solution() + # 76576500 + """ + return next(i for i in triangle_number_generator() if count_divisors(i) > 500) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_013/__init__.py b/project_euler/problem_013/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_013/num.txt b/project_euler/problem_013/num.txt new file mode 100644 index 000000000000..43b568e812a8 --- /dev/null +++ b/project_euler/problem_013/num.txt @@ -0,0 +1,100 @@ +37107287533902102798797998220837590246510135740250 +46376937677490009712648124896970078050417018260538 +74324986199524741059474233309513058123726617309629 +91942213363574161572522430563301811072406154908250 +23067588207539346171171980310421047513778063246676 +89261670696623633820136378418383684178734361726757 +28112879812849979408065481931592621691275889832738 +44274228917432520321923589422876796487670272189318 +47451445736001306439091167216856844588711603153276 +70386486105843025439939619828917593665686757934951 +62176457141856560629502157223196586755079324193331 +64906352462741904929101432445813822663347944758178 +92575867718337217661963751590579239728245598838407 +58203565325359399008402633568948830189458628227828 +80181199384826282014278194139940567587151170094390 +35398664372827112653829987240784473053190104293586 +86515506006295864861532075273371959191420517255829 +71693888707715466499115593487603532921714970056938 +54370070576826684624621495650076471787294438377604 +53282654108756828443191190634694037855217779295145 +36123272525000296071075082563815656710885258350721 +45876576172410976447339110607218265236877223636045 +17423706905851860660448207621209813287860733969412 +81142660418086830619328460811191061556940512689692 +51934325451728388641918047049293215058642563049483 +62467221648435076201727918039944693004732956340691 +15732444386908125794514089057706229429197107928209 +55037687525678773091862540744969844508330393682126 +18336384825330154686196124348767681297534375946515 +80386287592878490201521685554828717201219257766954 +78182833757993103614740356856449095527097864797581 +16726320100436897842553539920931837441497806860984 +48403098129077791799088218795327364475675590848030 +87086987551392711854517078544161852424320693150332 +59959406895756536782107074926966537676326235447210 +69793950679652694742597709739166693763042633987085 +41052684708299085211399427365734116182760315001271 +65378607361501080857009149939512557028198746004375 +35829035317434717326932123578154982629742552737307 +94953759765105305946966067683156574377167401875275 +88902802571733229619176668713819931811048770190271 +25267680276078003013678680992525463401061632866526 +36270218540497705585629946580636237993140746255962 +24074486908231174977792365466257246923322810917141 +91430288197103288597806669760892938638285025333403 +34413065578016127815921815005561868836468420090470 +23053081172816430487623791969842487255036638784583 +11487696932154902810424020138335124462181441773470 +63783299490636259666498587618221225225512486764533 +67720186971698544312419572409913959008952310058822 +95548255300263520781532296796249481641953868218774 +76085327132285723110424803456124867697064507995236 +37774242535411291684276865538926205024910326572967 +23701913275725675285653248258265463092207058596522 +29798860272258331913126375147341994889534765745501 +18495701454879288984856827726077713721403798879715 +38298203783031473527721580348144513491373226651381 +34829543829199918180278916522431027392251122869539 +40957953066405232632538044100059654939159879593635 +29746152185502371307642255121183693803580388584903 +41698116222072977186158236678424689157993532961922 +62467957194401269043877107275048102390895523597457 +23189706772547915061505504953922979530901129967519 +86188088225875314529584099251203829009407770775672 +11306739708304724483816533873502340845647058077308 +82959174767140363198008187129011875491310547126581 +97623331044818386269515456334926366572897563400500 +42846280183517070527831839425882145521227251250327 +55121603546981200581762165212827652751691296897789 +32238195734329339946437501907836945765883352399886 +75506164965184775180738168837861091527357929701337 +62177842752192623401942399639168044983993173312731 +32924185707147349566916674687634660915035914677504 +99518671430235219628894890102423325116913619626622 +73267460800591547471830798392868535206946944540724 +76841822524674417161514036427982273348055556214818 +97142617910342598647204516893989422179826088076852 +87783646182799346313767754307809363333018982642090 +10848802521674670883215120185883543223812876952786 +71329612474782464538636993009049310363619763878039 +62184073572399794223406235393808339651327408011116 +66627891981488087797941876876144230030984490851411 +60661826293682836764744779239180335110989069790714 +85786944089552990653640447425576083659976645795096 +66024396409905389607120198219976047599490197230297 +64913982680032973156037120041377903785566085089252 +16730939319872750275468906903707539413042652315011 +94809377245048795150954100921645863754710598436791 +78639167021187492431995700641917969777599028300699 +15368713711936614952811305876380278410754449733078 +40789923115535562561142322423255033685442488917353 +44889911501440648020369068063960672322193204149535 +41503128880339536053299340368006977710650566631954 +81234880673210146739058568557934581403627822703280 +82616570773948327592232845941706525094512325230608 +22918802058777319719839450180888072429661980811197 +77158542502016545090413245809786882778948721859617 +72107838435069186155435662884062257473692284509516 +20849603980134001723930671666823555245252804609722 +53503534226472524250874054075591789781264330331690 diff --git a/project_euler/problem_013/sol1.py b/project_euler/problem_013/sol1.py new file mode 100644 index 000000000000..1ea08b12ee93 --- /dev/null +++ b/project_euler/problem_013/sol1.py @@ -0,0 +1,25 @@ +""" +Problem 13: https://projecteuler.net/problem=13 + +Problem Statement: +Work out the first ten digits of the sum of the following one-hundred 50-digit +numbers. +""" +import os + + +def solution(): + """ + Returns the first ten digits of the sum of the array elements + from the file num.txt + + >>> solution() + '5537376230' + """ + file_path = os.path.join(os.path.dirname(__file__), "num.txt") + with open(file_path) as file_hand: + return str(sum([int(line) for line in file_hand]))[:10] + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_014/__init__.py b/project_euler/problem_014/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_014/sol1.py b/project_euler/problem_014/sol1.py new file mode 100644 index 000000000000..1745ec931e5a --- /dev/null +++ b/project_euler/problem_014/sol1.py @@ -0,0 +1,60 @@ +""" +Problem 14: https://projecteuler.net/problem=14 + +Problem Statement: +The following iterative sequence is defined for the set of positive integers: + + n → n/2 (n is even) + n → 3n + 1 (n is odd) + +Using the rule above and starting with 13, we generate the following sequence: + + 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 + +It can be seen that this sequence (starting at 13 and finishing at 1) contains +10 terms. Although it has not been proved yet (Collatz Problem), it is thought +that all starting numbers finish at 1. + +Which starting number, under one million, produces the longest chain? +""" + + +def solution(n: int = 1000000) -> int: + """Returns the number under n that generates the longest sequence using the + formula: + n → n/2 (n is even) + n → 3n + 1 (n is odd) + + # The code below has been commented due to slow execution affecting Travis. + # >>> solution(1000000) + # 837799 + >>> solution(200) + 171 + >>> solution(5000) + 3711 + >>> solution(15000) + 13255 + """ + largest_number = 0 + pre_counter = 0 + + for input1 in range(n): + counter = 1 + number = input1 + + while number > 1: + if number % 2 == 0: + number /= 2 + counter += 1 + else: + number = (3 * number) + 1 + counter += 1 + + if counter > pre_counter: + largest_number = input1 + pre_counter = counter + return largest_number + + +if __name__ == "__main__": + print(solution(int(input().strip()))) diff --git a/project_euler/problem_014/sol2.py b/project_euler/problem_014/sol2.py new file mode 100644 index 000000000000..20ad96327498 --- /dev/null +++ b/project_euler/problem_014/sol2.py @@ -0,0 +1,62 @@ +""" +Problem 14: https://projecteuler.net/problem=14 + +Collatz conjecture: start with any positive integer n. Next term obtained from +the previous term as follows: + +If the previous term is even, the next term is one half the previous term. +If the previous term is odd, the next term is 3 times the previous term plus 1. +The conjecture states the sequence will always reach 1 regardless of starting +n. + +Problem Statement: +The following iterative sequence is defined for the set of positive integers: + + n → n/2 (n is even) + n → 3n + 1 (n is odd) + +Using the rule above and starting with 13, we generate the following sequence: + + 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 + +It can be seen that this sequence (starting at 13 and finishing at 1) contains +10 terms. Although it has not been proved yet (Collatz Problem), it is thought +that all starting numbers finish at 1. + +Which starting number, under one million, produces the longest chain? +""" +from typing import List + + +def collatz_sequence(n: int) -> List[int]: + """Returns the Collatz sequence for n.""" + sequence = [n] + while n != 1: + if n % 2 == 0: + n //= 2 + else: + n = 3 * n + 1 + sequence.append(n) + return sequence + + +def solution(n: int = 1000000) -> int: + """Returns the number under n that generates the longest Collatz sequence. + + # The code below has been commented due to slow execution affecting Travis. + # >>> solution(1000000) + # 837799 + >>> solution(200) + 171 + >>> solution(5000) + 3711 + >>> solution(15000) + 13255 + """ + + result = max([(len(collatz_sequence(i)), i) for i in range(1, n)]) + return result[1] + + +if __name__ == "__main__": + print(solution(int(input().strip()))) diff --git a/project_euler/problem_015/__init__.py b/project_euler/problem_015/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_015/sol1.py b/project_euler/problem_015/sol1.py new file mode 100644 index 000000000000..da079d26120a --- /dev/null +++ b/project_euler/problem_015/sol1.py @@ -0,0 +1,44 @@ +""" +Problem 15: https://projecteuler.net/problem=15 + +Starting in the top left corner of a 2×2 grid, and only being able to move to +the right and down, there are exactly 6 routes to the bottom right corner. +How many such routes are there through a 20×20 grid? +""" +from math import factorial + + +def solution(n: int = 20) -> int: + """ + Returns the number of paths possible in a n x n grid starting at top left + corner going to bottom right corner and being able to move right and down + only. + >>> solution(25) + 126410606437752 + >>> solution(23) + 8233430727600 + >>> solution(20) + 137846528820 + >>> solution(15) + 155117520 + >>> solution(1) + 2 + """ + n = 2 * n # middle entry of odd rows starting at row 3 is the solution for n = 1, + # 2, 3,... + k = n / 2 + + return int(factorial(n) / (factorial(k) * factorial(n - k))) + + +if __name__ == "__main__": + import sys + + if len(sys.argv) == 1: + print(solution(20)) + else: + try: + n = int(sys.argv[1]) + print(solution(n)) + except ValueError: + print("Invalid entry - please enter a number.") diff --git a/project_euler/problem_016/__init__.py b/project_euler/problem_016/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_016/sol1.py b/project_euler/problem_016/sol1.py new file mode 100644 index 000000000000..f6620aa9482f --- /dev/null +++ b/project_euler/problem_016/sol1.py @@ -0,0 +1,36 @@ +""" +Problem 16: https://projecteuler.net/problem=16 + +2^15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26. + +What is the sum of the digits of the number 2^1000? +""" + + +def solution(power: int = 1000) -> int: + """Returns the sum of the digits of the number 2^power. + >>> solution(1000) + 1366 + >>> solution(50) + 76 + >>> solution(20) + 31 + >>> solution(15) + 26 + """ + num = 2 ** power + string_num = str(num) + list_num = list(string_num) + sum_of_num = 0 + + for i in list_num: + sum_of_num += int(i) + + return sum_of_num + + +if __name__ == "__main__": + power = int(input("Enter the power of 2: ").strip()) + print("2 ^ ", power, " = ", 2 ** power) + result = solution(power) + print("Sum of the digits is: ", result) diff --git a/project_euler/problem_016/sol2.py b/project_euler/problem_016/sol2.py new file mode 100644 index 000000000000..304d27d1e5d0 --- /dev/null +++ b/project_euler/problem_016/sol2.py @@ -0,0 +1,30 @@ +""" +Problem 16: https://projecteuler.net/problem=16 + +2^15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26. + +What is the sum of the digits of the number 2^1000? +""" + + +def solution(power: int = 1000) -> int: + """Returns the sum of the digits of the number 2^power. + + >>> solution(1000) + 1366 + >>> solution(50) + 76 + >>> solution(20) + 31 + >>> solution(15) + 26 + """ + n = 2 ** power + r = 0 + while n: + r, n = r + n % 10, n // 10 + return r + + +if __name__ == "__main__": + print(solution(int(str(input()).strip()))) diff --git a/project_euler/problem_017/__init__.py b/project_euler/problem_017/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_017/sol1.py b/project_euler/problem_017/sol1.py new file mode 100644 index 000000000000..d4db1beb5a4e --- /dev/null +++ b/project_euler/problem_017/sol1.py @@ -0,0 +1,63 @@ +""" +Number letter counts +Problem 17: https://projecteuler.net/problem=17 + +If the numbers 1 to 5 are written out in words: one, two, three, four, five, +then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total. + +If all the numbers from 1 to 1000 (one thousand) inclusive were written out in +words, how many letters would be used? + + +NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and +forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 +letters. The use of "and" when writing out numbers is in compliance withBritish +usage. +""" + + +def solution(n: int = 1000) -> int: + """Returns the number of letters used to write all numbers from 1 to n. + where n is lower or equals to 1000. + >>> solution(1000) + 21124 + >>> solution(5) + 19 + """ + # number of letters in zero, one, two, ..., nineteen (0 for zero since it's + # never said aloud) + ones_counts = [0, 3, 3, 5, 4, 4, 3, 5, 5, 4, 3, 6, 6, 8, 8, 7, 7, 9, 8, 8] + # number of letters in twenty, thirty, ..., ninety (0 for numbers less than + # 20 due to inconsistency in teens) + tens_counts = [0, 0, 6, 6, 5, 5, 5, 7, 6, 6] + + count = 0 + + for i in range(1, n + 1): + if i < 1000: + if i >= 100: + # add number of letters for "n hundred" + count += ones_counts[i // 100] + 7 + + if i % 100 != 0: + # add number of letters for "and" if number is not multiple + # of 100 + count += 3 + + if 0 < i % 100 < 20: + # add number of letters for one, two, three, ..., nineteen + # (could be combined with below if not for inconsistency in + # teens) + count += ones_counts[i % 100] + else: + # add number of letters for twenty, twenty one, ..., ninety + # nine + count += ones_counts[i % 10] + count += tens_counts[(i % 100 - i % 10) // 10] + else: + count += ones_counts[i // 1000] + 8 + return count + + +if __name__ == "__main__": + print(solution(int(input().strip()))) diff --git a/project_euler/problem_018/__init__.py b/project_euler/problem_018/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_018/solution.py b/project_euler/problem_018/solution.py new file mode 100644 index 000000000000..82fc3ce3c9db --- /dev/null +++ b/project_euler/problem_018/solution.py @@ -0,0 +1,64 @@ +""" +By starting at the top of the triangle below and moving to adjacent numbers on +the row below, the maximum total from top to bottom is 23. + +3 +7 4 +2 4 6 +8 5 9 3 + +That is, 3 + 7 + 4 + 9 = 23. + +Find the maximum total from top to bottom of the triangle below: + +75 +95 64 +17 47 82 +18 35 87 10 +20 04 82 47 65 +19 01 23 75 03 34 +88 02 77 73 07 63 67 +99 65 04 28 06 16 70 92 +41 41 26 56 83 40 80 70 33 +41 48 72 33 47 32 37 16 94 29 +53 71 44 65 25 43 91 52 97 51 14 +70 11 33 28 77 73 17 78 39 68 17 57 +91 71 52 38 17 14 91 43 58 50 27 29 48 +63 66 04 68 89 53 67 30 73 16 69 87 40 31 +04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 +""" +import os + + +def solution(): + """ + Finds the maximum total in a triangle as described by the problem statement + above. + + >>> solution() + 1074 + """ + script_dir = os.path.dirname(os.path.realpath(__file__)) + triangle = os.path.join(script_dir, "triangle.txt") + + with open(triangle) as f: + triangle = f.readlines() + + a = [[int(y) for y in x.rstrip("\r\n").split(" ")] for x in triangle] + + for i in range(1, len(a)): + for j in range(len(a[i])): + if j != len(a[i - 1]): + number1 = a[i - 1][j] + else: + number1 = 0 + if j > 0: + number2 = a[i - 1][j - 1] + else: + number2 = 0 + a[i][j] += max(number1, number2) + return max(a[-1]) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_018/triangle.txt b/project_euler/problem_018/triangle.txt new file mode 100644 index 000000000000..e236c2ff7ee2 --- /dev/null +++ b/project_euler/problem_018/triangle.txt @@ -0,0 +1,15 @@ +75 +95 64 +17 47 82 +18 35 87 10 +20 04 82 47 65 +19 01 23 75 03 34 +88 02 77 73 07 63 67 +99 65 04 28 06 16 70 92 +41 41 26 56 83 40 80 70 33 +41 48 72 33 47 32 37 16 94 29 +53 71 44 65 25 43 91 52 97 51 14 +70 11 33 28 77 73 17 78 39 68 17 57 +91 71 52 38 17 14 91 43 58 50 27 29 48 +63 66 04 68 89 53 67 30 73 16 69 87 40 31 +04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 diff --git a/project_euler/problem_019/__init__.py b/project_euler/problem_019/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_019/sol1.py b/project_euler/problem_019/sol1.py new file mode 100644 index 000000000000..ab59365843b2 --- /dev/null +++ b/project_euler/problem_019/sol1.py @@ -0,0 +1,64 @@ +""" +Counting Sundays +Problem 19 + +You are given the following information, but you may prefer to do some research +for yourself. + +1 Jan 1900 was a Monday. +Thirty days has September, +April, June and November. +All the rest have thirty-one, +Saving February alone, +Which has twenty-eight, rain or shine. +And on leap years, twenty-nine. + +A leap year occurs on any year evenly divisible by 4, but not on a century +unless it is divisible by 400. + +How many Sundays fell on the first of the month during the twentieth century +(1 Jan 1901 to 31 Dec 2000)? +""" + + +def solution(): + """Returns the number of mondays that fall on the first of the month during + the twentieth century (1 Jan 1901 to 31 Dec 2000)? + + >>> solution() + 171 + """ + days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + day = 6 + month = 1 + year = 1901 + + sundays = 0 + + while year < 2001: + day += 7 + + if (year % 4 == 0 and not year % 100 == 0) or (year % 400 == 0): + if day > days_per_month[month - 1] and month != 2: + month += 1 + day = day - days_per_month[month - 2] + elif day > 29 and month == 2: + month += 1 + day = day - 29 + else: + if day > days_per_month[month - 1]: + month += 1 + day = day - days_per_month[month - 2] + + if month > 12: + year += 1 + month = 1 + + if year < 2001 and day == 1: + sundays += 1 + return sundays + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_020/__init__.py b/project_euler/problem_020/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_020/sol1.py b/project_euler/problem_020/sol1.py new file mode 100644 index 000000000000..b472024e54c0 --- /dev/null +++ b/project_euler/problem_020/sol1.py @@ -0,0 +1,54 @@ +""" +Problem 20: https://projecteuler.net/problem=20 + +n! means n × (n − 1) × ... × 3 × 2 × 1 + +For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. + +Find the sum of the digits in the number 100! +""" + + +def factorial(num: int) -> int: + """Find the factorial of a given number n""" + fact = 1 + for i in range(1, num + 1): + fact *= i + return fact + + +def split_and_add(number: int) -> int: + """Split number digits and add them.""" + sum_of_digits = 0 + while number > 0: + last_digit = number % 10 + sum_of_digits += last_digit + number = number // 10 # Removing the last_digit from the given number + return sum_of_digits + + +def solution(num: int = 100) -> int: + """Returns the sum of the digits in the factorial of num + >>> solution(100) + 648 + >>> solution(50) + 216 + >>> solution(10) + 27 + >>> solution(5) + 3 + >>> solution(3) + 6 + >>> solution(2) + 2 + >>> solution(1) + 1 + """ + nfact = factorial(num) + result = split_and_add(nfact) + return result + + +if __name__ == "__main__": + print(solution(int(input("Enter the Number: ").strip()))) diff --git a/project_euler/problem_020/sol2.py b/project_euler/problem_020/sol2.py new file mode 100644 index 000000000000..92e1e724a647 --- /dev/null +++ b/project_euler/problem_020/sol2.py @@ -0,0 +1,35 @@ +""" +Problem 20: https://projecteuler.net/problem=20 + +n! means n × (n − 1) × ... × 3 × 2 × 1 + +For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. + +Find the sum of the digits in the number 100! +""" +from math import factorial + + +def solution(num: int = 100) -> int: + """Returns the sum of the digits in the factorial of num + >>> solution(100) + 648 + >>> solution(50) + 216 + >>> solution(10) + 27 + >>> solution(5) + 3 + >>> solution(3) + 6 + >>> solution(2) + 2 + >>> solution(1) + 1 + """ + return sum([int(x) for x in str(factorial(num))]) + + +if __name__ == "__main__": + print(solution(int(input("Enter the Number: ").strip()))) diff --git a/project_euler/problem_020/sol3.py b/project_euler/problem_020/sol3.py new file mode 100644 index 000000000000..4f28ac5fcfde --- /dev/null +++ b/project_euler/problem_020/sol3.py @@ -0,0 +1,41 @@ +""" +Problem 20: https://projecteuler.net/problem=20 + +n! means n × (n − 1) × ... × 3 × 2 × 1 + +For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. + +Find the sum of the digits in the number 100! +""" +from math import factorial + + +def solution(num: int = 100) -> int: + """Returns the sum of the digits in the factorial of num + >>> solution(1000) + 10539 + >>> solution(200) + 1404 + >>> solution(100) + 648 + >>> solution(50) + 216 + >>> solution(10) + 27 + >>> solution(5) + 3 + >>> solution(3) + 6 + >>> solution(2) + 2 + >>> solution(1) + 1 + >>> solution(0) + 1 + """ + return sum(map(int, str(factorial(num)))) + + +if __name__ == "__main__": + print(solution(int(input("Enter the Number: ").strip()))) diff --git a/project_euler/problem_020/sol4.py b/project_euler/problem_020/sol4.py new file mode 100644 index 000000000000..b32ce309dfa6 --- /dev/null +++ b/project_euler/problem_020/sol4.py @@ -0,0 +1,42 @@ +""" +Problem 20: https://projecteuler.net/problem=20 + +n! means n × (n − 1) × ... × 3 × 2 × 1 + +For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, +and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27. + +Find the sum of the digits in the number 100! +""" + + +def solution(num: int = 100) -> int: + """Returns the sum of the digits in the factorial of num + >>> solution(100) + 648 + >>> solution(50) + 216 + >>> solution(10) + 27 + >>> solution(5) + 3 + >>> solution(3) + 6 + >>> solution(2) + 2 + >>> solution(1) + 1 + """ + fact = 1 + result = 0 + for i in range(1, num + 1): + fact *= i + + for j in str(fact): + result += int(j) + + return result + + +if __name__ == "__main__": + print(solution(int(input("Enter the Number: ").strip()))) diff --git a/project_euler/problem_021/__init__.py b/project_euler/problem_021/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_021/sol1.py b/project_euler/problem_021/sol1.py new file mode 100644 index 000000000000..3fac79156e41 --- /dev/null +++ b/project_euler/problem_021/sol1.py @@ -0,0 +1,54 @@ +""" +Amicable Numbers +Problem 21 + +Let d(n) be defined as the sum of proper divisors of n (numbers less than n +which divide evenly into n). +If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and +each of a and b are called amicable numbers. + +For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 +and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and +142; so d(284) = 220. + +Evaluate the sum of all the amicable numbers under 10000. +""" +from math import sqrt + + +def sum_of_divisors(n: int) -> int: + total = 0 + for i in range(1, int(sqrt(n) + 1)): + if n % i == 0 and i != sqrt(n): + total += i + n // i + elif i == sqrt(n): + total += i + return total - n + + +def solution(n: int = 10000) -> int: + """Returns the sum of all the amicable numbers under n. + + >>> solution(10000) + 31626 + >>> solution(5000) + 8442 + >>> solution(1000) + 504 + >>> solution(100) + 0 + >>> solution(50) + 0 + """ + total = sum( + [ + i + for i in range(1, n) + if sum_of_divisors(sum_of_divisors(i)) == i and sum_of_divisors(i) != i + ] + ) + return total + + +if __name__ == "__main__": + print(solution(int(str(input()).strip()))) diff --git a/project_euler/problem_022/__init__.py b/project_euler/problem_022/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_022/p022_names.txt b/project_euler/problem_022/p022_names.txt new file mode 100644 index 000000000000..7b8986bf6ce9 --- /dev/null +++ b/project_euler/problem_022/p022_names.txt @@ -0,0 +1 @@ +"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER","MARIA","SUSAN","MARGARET","DOROTHY","LISA","NANCY","KAREN","BETTY","HELEN","SANDRA","DONNA","CAROL","RUTH","SHARON","MICHELLE","LAURA","SARAH","KIMBERLY","DEBORAH","JESSICA","SHIRLEY","CYNTHIA","ANGELA","MELISSA","BRENDA","AMY","ANNA","REBECCA","VIRGINIA","KATHLEEN","PAMELA","MARTHA","DEBRA","AMANDA","STEPHANIE","CAROLYN","CHRISTINE","MARIE","JANET","CATHERINE","FRANCES","ANN","JOYCE","DIANE","ALICE","JULIE","HEATHER","TERESA","DORIS","GLORIA","EVELYN","JEAN","CHERYL","MILDRED","KATHERINE","JOAN","ASHLEY","JUDITH","ROSE","JANICE","KELLY","NICOLE","JUDY","CHRISTINA","KATHY","THERESA","BEVERLY","DENISE","TAMMY","IRENE","JANE","LORI","RACHEL","MARILYN","ANDREA","KATHRYN","LOUISE","SARA","ANNE","JACQUELINE","WANDA","BONNIE","JULIA","RUBY","LOIS","TINA","PHYLLIS","NORMA","PAULA","DIANA","ANNIE","LILLIAN","EMILY","ROBIN","PEGGY","CRYSTAL","GLADYS","RITA","DAWN","CONNIE","FLORENCE","TRACY","EDNA","TIFFANY","CARMEN","ROSA","CINDY","GRACE","WENDY","VICTORIA","EDITH","KIM","SHERRY","SYLVIA","JOSEPHINE","THELMA","SHANNON","SHEILA","ETHEL","ELLEN","ELAINE","MARJORIE","CARRIE","CHARLOTTE","MONICA","ESTHER","PAULINE","EMMA","JUANITA","ANITA","RHONDA","HAZEL","AMBER","EVA","DEBBIE","APRIL","LESLIE","CLARA","LUCILLE","JAMIE","JOANNE","ELEANOR","VALERIE","DANIELLE","MEGAN","ALICIA","SUZANNE","MICHELE","GAIL","BERTHA","DARLENE","VERONICA","JILL","ERIN","GERALDINE","LAUREN","CATHY","JOANN","LORRAINE","LYNN","SALLY","REGINA","ERICA","BEATRICE","DOLORES","BERNICE","AUDREY","YVONNE","ANNETTE","JUNE","SAMANTHA","MARION","DANA","STACY","ANA","RENEE","IDA","VIVIAN","ROBERTA","HOLLY","BRITTANY","MELANIE","LORETTA","YOLANDA","JEANETTE","LAURIE","KATIE","KRISTEN","VANESSA","ALMA","SUE","ELSIE","BETH","JEANNE","VICKI","CARLA","TARA","ROSEMARY","EILEEN","TERRI","GERTRUDE","LUCY","TONYA","ELLA","STACEY","WILMA","GINA","KRISTIN","JESSIE","NATALIE","AGNES","VERA","WILLIE","CHARLENE","BESSIE","DELORES","MELINDA","PEARL","ARLENE","MAUREEN","COLLEEN","ALLISON","TAMARA","JOY","GEORGIA","CONSTANCE","LILLIE","CLAUDIA","JACKIE","MARCIA","TANYA","NELLIE","MINNIE","MARLENE","HEIDI","GLENDA","LYDIA","VIOLA","COURTNEY","MARIAN","STELLA","CAROLINE","DORA","JO","VICKIE","MATTIE","TERRY","MAXINE","IRMA","MABEL","MARSHA","MYRTLE","LENA","CHRISTY","DEANNA","PATSY","HILDA","GWENDOLYN","JENNIE","NORA","MARGIE","NINA","CASSANDRA","LEAH","PENNY","KAY","PRISCILLA","NAOMI","CAROLE","BRANDY","OLGA","BILLIE","DIANNE","TRACEY","LEONA","JENNY","FELICIA","SONIA","MIRIAM","VELMA","BECKY","BOBBIE","VIOLET","KRISTINA","TONI","MISTY","MAE","SHELLY","DAISY","RAMONA","SHERRI","ERIKA","KATRINA","CLAIRE","LINDSEY","LINDSAY","GENEVA","GUADALUPE","BELINDA","MARGARITA","SHERYL","CORA","FAYE","ADA","NATASHA","SABRINA","ISABEL","MARGUERITE","HATTIE","HARRIET","MOLLY","CECILIA","KRISTI","BRANDI","BLANCHE","SANDY","ROSIE","JOANNA","IRIS","EUNICE","ANGIE","INEZ","LYNDA","MADELINE","AMELIA","ALBERTA","GENEVIEVE","MONIQUE","JODI","JANIE","MAGGIE","KAYLA","SONYA","JAN","LEE","KRISTINE","CANDACE","FANNIE","MARYANN","OPAL","ALISON","YVETTE","MELODY","LUZ","SUSIE","OLIVIA","FLORA","SHELLEY","KRISTY","MAMIE","LULA","LOLA","VERNA","BEULAH","ANTOINETTE","CANDICE","JUANA","JEANNETTE","PAM","KELLI","HANNAH","WHITNEY","BRIDGET","KARLA","CELIA","LATOYA","PATTY","SHELIA","GAYLE","DELLA","VICKY","LYNNE","SHERI","MARIANNE","KARA","JACQUELYN","ERMA","BLANCA","MYRA","LETICIA","PAT","KRISTA","ROXANNE","ANGELICA","JOHNNIE","ROBYN","FRANCIS","ADRIENNE","ROSALIE","ALEXANDRA","BROOKE","BETHANY","SADIE","BERNADETTE","TRACI","JODY","KENDRA","JASMINE","NICHOLE","RACHAEL","CHELSEA","MABLE","ERNESTINE","MURIEL","MARCELLA","ELENA","KRYSTAL","ANGELINA","NADINE","KARI","ESTELLE","DIANNA","PAULETTE","LORA","MONA","DOREEN","ROSEMARIE","ANGEL","DESIREE","ANTONIA","HOPE","GINGER","JANIS","BETSY","CHRISTIE","FREDA","MERCEDES","MEREDITH","LYNETTE","TERI","CRISTINA","EULA","LEIGH","MEGHAN","SOPHIA","ELOISE","ROCHELLE","GRETCHEN","CECELIA","RAQUEL","HENRIETTA","ALYSSA","JANA","KELLEY","GWEN","KERRY","JENNA","TRICIA","LAVERNE","OLIVE","ALEXIS","TASHA","SILVIA","ELVIRA","CASEY","DELIA","SOPHIE","KATE","PATTI","LORENA","KELLIE","SONJA","LILA","LANA","DARLA","MAY","MINDY","ESSIE","MANDY","LORENE","ELSA","JOSEFINA","JEANNIE","MIRANDA","DIXIE","LUCIA","MARTA","FAITH","LELA","JOHANNA","SHARI","CAMILLE","TAMI","SHAWNA","ELISA","EBONY","MELBA","ORA","NETTIE","TABITHA","OLLIE","JAIME","WINIFRED","KRISTIE","MARINA","ALISHA","AIMEE","RENA","MYRNA","MARLA","TAMMIE","LATASHA","BONITA","PATRICE","RONDA","SHERRIE","ADDIE","FRANCINE","DELORIS","STACIE","ADRIANA","CHERI","SHELBY","ABIGAIL","CELESTE","JEWEL","CARA","ADELE","REBEKAH","LUCINDA","DORTHY","CHRIS","EFFIE","TRINA","REBA","SHAWN","SALLIE","AURORA","LENORA","ETTA","LOTTIE","KERRI","TRISHA","NIKKI","ESTELLA","FRANCISCA","JOSIE","TRACIE","MARISSA","KARIN","BRITTNEY","JANELLE","LOURDES","LAUREL","HELENE","FERN","ELVA","CORINNE","KELSEY","INA","BETTIE","ELISABETH","AIDA","CAITLIN","INGRID","IVA","EUGENIA","CHRISTA","GOLDIE","CASSIE","MAUDE","JENIFER","THERESE","FRANKIE","DENA","LORNA","JANETTE","LATONYA","CANDY","MORGAN","CONSUELO","TAMIKA","ROSETTA","DEBORA","CHERIE","POLLY","DINA","JEWELL","FAY","JILLIAN","DOROTHEA","NELL","TRUDY","ESPERANZA","PATRICA","KIMBERLEY","SHANNA","HELENA","CAROLINA","CLEO","STEFANIE","ROSARIO","OLA","JANINE","MOLLIE","LUPE","ALISA","LOU","MARIBEL","SUSANNE","BETTE","SUSANA","ELISE","CECILE","ISABELLE","LESLEY","JOCELYN","PAIGE","JONI","RACHELLE","LEOLA","DAPHNE","ALTA","ESTER","PETRA","GRACIELA","IMOGENE","JOLENE","KEISHA","LACEY","GLENNA","GABRIELA","KERI","URSULA","LIZZIE","KIRSTEN","SHANA","ADELINE","MAYRA","JAYNE","JACLYN","GRACIE","SONDRA","CARMELA","MARISA","ROSALIND","CHARITY","TONIA","BEATRIZ","MARISOL","CLARICE","JEANINE","SHEENA","ANGELINE","FRIEDA","LILY","ROBBIE","SHAUNA","MILLIE","CLAUDETTE","CATHLEEN","ANGELIA","GABRIELLE","AUTUMN","KATHARINE","SUMMER","JODIE","STACI","LEA","CHRISTI","JIMMIE","JUSTINE","ELMA","LUELLA","MARGRET","DOMINIQUE","SOCORRO","RENE","MARTINA","MARGO","MAVIS","CALLIE","BOBBI","MARITZA","LUCILE","LEANNE","JEANNINE","DEANA","AILEEN","LORIE","LADONNA","WILLA","MANUELA","GALE","SELMA","DOLLY","SYBIL","ABBY","LARA","DALE","IVY","DEE","WINNIE","MARCY","LUISA","JERI","MAGDALENA","OFELIA","MEAGAN","AUDRA","MATILDA","LEILA","CORNELIA","BIANCA","SIMONE","BETTYE","RANDI","VIRGIE","LATISHA","BARBRA","GEORGINA","ELIZA","LEANN","BRIDGETTE","RHODA","HALEY","ADELA","NOLA","BERNADINE","FLOSSIE","ILA","GRETA","RUTHIE","NELDA","MINERVA","LILLY","TERRIE","LETHA","HILARY","ESTELA","VALARIE","BRIANNA","ROSALYN","EARLINE","CATALINA","AVA","MIA","CLARISSA","LIDIA","CORRINE","ALEXANDRIA","CONCEPCION","TIA","SHARRON","RAE","DONA","ERICKA","JAMI","ELNORA","CHANDRA","LENORE","NEVA","MARYLOU","MELISA","TABATHA","SERENA","AVIS","ALLIE","SOFIA","JEANIE","ODESSA","NANNIE","HARRIETT","LORAINE","PENELOPE","MILAGROS","EMILIA","BENITA","ALLYSON","ASHLEE","TANIA","TOMMIE","ESMERALDA","KARINA","EVE","PEARLIE","ZELMA","MALINDA","NOREEN","TAMEKA","SAUNDRA","HILLARY","AMIE","ALTHEA","ROSALINDA","JORDAN","LILIA","ALANA","GAY","CLARE","ALEJANDRA","ELINOR","MICHAEL","LORRIE","JERRI","DARCY","EARNESTINE","CARMELLA","TAYLOR","NOEMI","MARCIE","LIZA","ANNABELLE","LOUISA","EARLENE","MALLORY","CARLENE","NITA","SELENA","TANISHA","KATY","JULIANNE","JOHN","LAKISHA","EDWINA","MARICELA","MARGERY","KENYA","DOLLIE","ROXIE","ROSLYN","KATHRINE","NANETTE","CHARMAINE","LAVONNE","ILENE","KRIS","TAMMI","SUZETTE","CORINE","KAYE","JERRY","MERLE","CHRYSTAL","LINA","DEANNE","LILIAN","JULIANA","ALINE","LUANN","KASEY","MARYANNE","EVANGELINE","COLETTE","MELVA","LAWANDA","YESENIA","NADIA","MADGE","KATHIE","EDDIE","OPHELIA","VALERIA","NONA","MITZI","MARI","GEORGETTE","CLAUDINE","FRAN","ALISSA","ROSEANN","LAKEISHA","SUSANNA","REVA","DEIDRE","CHASITY","SHEREE","CARLY","JAMES","ELVIA","ALYCE","DEIRDRE","GENA","BRIANA","ARACELI","KATELYN","ROSANNE","WENDI","TESSA","BERTA","MARVA","IMELDA","MARIETTA","MARCI","LEONOR","ARLINE","SASHA","MADELYN","JANNA","JULIETTE","DEENA","AURELIA","JOSEFA","AUGUSTA","LILIANA","YOUNG","CHRISTIAN","LESSIE","AMALIA","SAVANNAH","ANASTASIA","VILMA","NATALIA","ROSELLA","LYNNETTE","CORINA","ALFREDA","LEANNA","CAREY","AMPARO","COLEEN","TAMRA","AISHA","WILDA","KARYN","CHERRY","QUEEN","MAURA","MAI","EVANGELINA","ROSANNA","HALLIE","ERNA","ENID","MARIANA","LACY","JULIET","JACKLYN","FREIDA","MADELEINE","MARA","HESTER","CATHRYN","LELIA","CASANDRA","BRIDGETT","ANGELITA","JANNIE","DIONNE","ANNMARIE","KATINA","BERYL","PHOEBE","MILLICENT","KATHERYN","DIANN","CARISSA","MARYELLEN","LIZ","LAURI","HELGA","GILDA","ADRIAN","RHEA","MARQUITA","HOLLIE","TISHA","TAMERA","ANGELIQUE","FRANCESCA","BRITNEY","KAITLIN","LOLITA","FLORINE","ROWENA","REYNA","TWILA","FANNY","JANELL","INES","CONCETTA","BERTIE","ALBA","BRIGITTE","ALYSON","VONDA","PANSY","ELBA","NOELLE","LETITIA","KITTY","DEANN","BRANDIE","LOUELLA","LETA","FELECIA","SHARLENE","LESA","BEVERLEY","ROBERT","ISABELLA","HERMINIA","TERRA","CELINA","TORI","OCTAVIA","JADE","DENICE","GERMAINE","SIERRA","MICHELL","CORTNEY","NELLY","DORETHA","SYDNEY","DEIDRA","MONIKA","LASHONDA","JUDI","CHELSEY","ANTIONETTE","MARGOT","BOBBY","ADELAIDE","NAN","LEEANN","ELISHA","DESSIE","LIBBY","KATHI","GAYLA","LATANYA","MINA","MELLISA","KIMBERLEE","JASMIN","RENAE","ZELDA","ELDA","MA","JUSTINA","GUSSIE","EMILIE","CAMILLA","ABBIE","ROCIO","KAITLYN","JESSE","EDYTHE","ASHLEIGH","SELINA","LAKESHA","GERI","ALLENE","PAMALA","MICHAELA","DAYNA","CARYN","ROSALIA","SUN","JACQULINE","REBECA","MARYBETH","KRYSTLE","IOLA","DOTTIE","BENNIE","BELLE","AUBREY","GRISELDA","ERNESTINA","ELIDA","ADRIANNE","DEMETRIA","DELMA","CHONG","JAQUELINE","DESTINY","ARLEEN","VIRGINA","RETHA","FATIMA","TILLIE","ELEANORE","CARI","TREVA","BIRDIE","WILHELMINA","ROSALEE","MAURINE","LATRICE","YONG","JENA","TARYN","ELIA","DEBBY","MAUDIE","JEANNA","DELILAH","CATRINA","SHONDA","HORTENCIA","THEODORA","TERESITA","ROBBIN","DANETTE","MARYJANE","FREDDIE","DELPHINE","BRIANNE","NILDA","DANNA","CINDI","BESS","IONA","HANNA","ARIEL","WINONA","VIDA","ROSITA","MARIANNA","WILLIAM","RACHEAL","GUILLERMINA","ELOISA","CELESTINE","CAREN","MALISSA","LONA","CHANTEL","SHELLIE","MARISELA","LEORA","AGATHA","SOLEDAD","MIGDALIA","IVETTE","CHRISTEN","ATHENA","JANEL","CHLOE","VEDA","PATTIE","TESSIE","TERA","MARILYNN","LUCRETIA","KARRIE","DINAH","DANIELA","ALECIA","ADELINA","VERNICE","SHIELA","PORTIA","MERRY","LASHAWN","DEVON","DARA","TAWANA","OMA","VERDA","CHRISTIN","ALENE","ZELLA","SANDI","RAFAELA","MAYA","KIRA","CANDIDA","ALVINA","SUZAN","SHAYLA","LYN","LETTIE","ALVA","SAMATHA","ORALIA","MATILDE","MADONNA","LARISSA","VESTA","RENITA","INDIA","DELOIS","SHANDA","PHILLIS","LORRI","ERLINDA","CRUZ","CATHRINE","BARB","ZOE","ISABELL","IONE","GISELA","CHARLIE","VALENCIA","ROXANNA","MAYME","KISHA","ELLIE","MELLISSA","DORRIS","DALIA","BELLA","ANNETTA","ZOILA","RETA","REINA","LAURETTA","KYLIE","CHRISTAL","PILAR","CHARLA","ELISSA","TIFFANI","TANA","PAULINA","LEOTA","BREANNA","JAYME","CARMEL","VERNELL","TOMASA","MANDI","DOMINGA","SANTA","MELODIE","LURA","ALEXA","TAMELA","RYAN","MIRNA","KERRIE","VENUS","NOEL","FELICITA","CRISTY","CARMELITA","BERNIECE","ANNEMARIE","TIARA","ROSEANNE","MISSY","CORI","ROXANA","PRICILLA","KRISTAL","JUNG","ELYSE","HAYDEE","ALETHA","BETTINA","MARGE","GILLIAN","FILOMENA","CHARLES","ZENAIDA","HARRIETTE","CARIDAD","VADA","UNA","ARETHA","PEARLINE","MARJORY","MARCELA","FLOR","EVETTE","ELOUISE","ALINA","TRINIDAD","DAVID","DAMARIS","CATHARINE","CARROLL","BELVA","NAKIA","MARLENA","LUANNE","LORINE","KARON","DORENE","DANITA","BRENNA","TATIANA","SAMMIE","LOUANN","LOREN","JULIANNA","ANDRIA","PHILOMENA","LUCILA","LEONORA","DOVIE","ROMONA","MIMI","JACQUELIN","GAYE","TONJA","MISTI","JOE","GENE","CHASTITY","STACIA","ROXANN","MICAELA","NIKITA","MEI","VELDA","MARLYS","JOHNNA","AURA","LAVERN","IVONNE","HAYLEY","NICKI","MAJORIE","HERLINDA","GEORGE","ALPHA","YADIRA","PERLA","GREGORIA","DANIEL","ANTONETTE","SHELLI","MOZELLE","MARIAH","JOELLE","CORDELIA","JOSETTE","CHIQUITA","TRISTA","LOUIS","LAQUITA","GEORGIANA","CANDI","SHANON","LONNIE","HILDEGARD","CECIL","VALENTINA","STEPHANY","MAGDA","KAROL","GERRY","GABRIELLA","TIANA","ROMA","RICHELLE","RAY","PRINCESS","OLETA","JACQUE","IDELLA","ALAINA","SUZANNA","JOVITA","BLAIR","TOSHA","RAVEN","NEREIDA","MARLYN","KYLA","JOSEPH","DELFINA","TENA","STEPHENIE","SABINA","NATHALIE","MARCELLE","GERTIE","DARLEEN","THEA","SHARONDA","SHANTEL","BELEN","VENESSA","ROSALINA","ONA","GENOVEVA","COREY","CLEMENTINE","ROSALBA","RENATE","RENATA","MI","IVORY","GEORGIANNA","FLOY","DORCAS","ARIANA","TYRA","THEDA","MARIAM","JULI","JESICA","DONNIE","VIKKI","VERLA","ROSELYN","MELVINA","JANNETTE","GINNY","DEBRAH","CORRIE","ASIA","VIOLETA","MYRTIS","LATRICIA","COLLETTE","CHARLEEN","ANISSA","VIVIANA","TWYLA","PRECIOUS","NEDRA","LATONIA","LAN","HELLEN","FABIOLA","ANNAMARIE","ADELL","SHARYN","CHANTAL","NIKI","MAUD","LIZETTE","LINDY","KIA","KESHA","JEANA","DANELLE","CHARLINE","CHANEL","CARROL","VALORIE","LIA","DORTHA","CRISTAL","SUNNY","LEONE","LEILANI","GERRI","DEBI","ANDRA","KESHIA","IMA","EULALIA","EASTER","DULCE","NATIVIDAD","LINNIE","KAMI","GEORGIE","CATINA","BROOK","ALDA","WINNIFRED","SHARLA","RUTHANN","MEAGHAN","MAGDALENE","LISSETTE","ADELAIDA","VENITA","TRENA","SHIRLENE","SHAMEKA","ELIZEBETH","DIAN","SHANTA","MICKEY","LATOSHA","CARLOTTA","WINDY","SOON","ROSINA","MARIANN","LEISA","JONNIE","DAWNA","CATHIE","BILLY","ASTRID","SIDNEY","LAUREEN","JANEEN","HOLLI","FAWN","VICKEY","TERESSA","SHANTE","RUBYE","MARCELINA","CHANDA","CARY","TERESE","SCARLETT","MARTY","MARNIE","LULU","LISETTE","JENIFFER","ELENOR","DORINDA","DONITA","CARMAN","BERNITA","ALTAGRACIA","ALETA","ADRIANNA","ZORAIDA","RONNIE","NICOLA","LYNDSEY","KENDALL","JANINA","CHRISSY","AMI","STARLA","PHYLIS","PHUONG","KYRA","CHARISSE","BLANCH","SANJUANITA","RONA","NANCI","MARILEE","MARANDA","CORY","BRIGETTE","SANJUANA","MARITA","KASSANDRA","JOYCELYN","IRA","FELIPA","CHELSIE","BONNY","MIREYA","LORENZA","KYONG","ILEANA","CANDELARIA","TONY","TOBY","SHERIE","OK","MARK","LUCIE","LEATRICE","LAKESHIA","GERDA","EDIE","BAMBI","MARYLIN","LAVON","HORTENSE","GARNET","EVIE","TRESSA","SHAYNA","LAVINA","KYUNG","JEANETTA","SHERRILL","SHARA","PHYLISS","MITTIE","ANABEL","ALESIA","THUY","TAWANDA","RICHARD","JOANIE","TIFFANIE","LASHANDA","KARISSA","ENRIQUETA","DARIA","DANIELLA","CORINNA","ALANNA","ABBEY","ROXANE","ROSEANNA","MAGNOLIA","LIDA","KYLE","JOELLEN","ERA","CORAL","CARLEEN","TRESA","PEGGIE","NOVELLA","NILA","MAYBELLE","JENELLE","CARINA","NOVA","MELINA","MARQUERITE","MARGARETTE","JOSEPHINA","EVONNE","DEVIN","CINTHIA","ALBINA","TOYA","TAWNYA","SHERITA","SANTOS","MYRIAM","LIZABETH","LISE","KEELY","JENNI","GISELLE","CHERYLE","ARDITH","ARDIS","ALESHA","ADRIANE","SHAINA","LINNEA","KAROLYN","HONG","FLORIDA","FELISHA","DORI","DARCI","ARTIE","ARMIDA","ZOLA","XIOMARA","VERGIE","SHAMIKA","NENA","NANNETTE","MAXIE","LOVIE","JEANE","JAIMIE","INGE","FARRAH","ELAINA","CAITLYN","STARR","FELICITAS","CHERLY","CARYL","YOLONDA","YASMIN","TEENA","PRUDENCE","PENNIE","NYDIA","MACKENZIE","ORPHA","MARVEL","LIZBETH","LAURETTE","JERRIE","HERMELINDA","CAROLEE","TIERRA","MIRIAN","META","MELONY","KORI","JENNETTE","JAMILA","ENA","ANH","YOSHIKO","SUSANNAH","SALINA","RHIANNON","JOLEEN","CRISTINE","ASHTON","ARACELY","TOMEKA","SHALONDA","MARTI","LACIE","KALA","JADA","ILSE","HAILEY","BRITTANI","ZONA","SYBLE","SHERRYL","RANDY","NIDIA","MARLO","KANDICE","KANDI","DEB","DEAN","AMERICA","ALYCIA","TOMMY","RONNA","NORENE","MERCY","JOSE","INGEBORG","GIOVANNA","GEMMA","CHRISTEL","AUDRY","ZORA","VITA","VAN","TRISH","STEPHAINE","SHIRLEE","SHANIKA","MELONIE","MAZIE","JAZMIN","INGA","HOA","HETTIE","GERALYN","FONDA","ESTRELLA","ADELLA","SU","SARITA","RINA","MILISSA","MARIBETH","GOLDA","EVON","ETHELYN","ENEDINA","CHERISE","CHANA","VELVA","TAWANNA","SADE","MIRTA","LI","KARIE","JACINTA","ELNA","DAVINA","CIERRA","ASHLIE","ALBERTHA","TANESHA","STEPHANI","NELLE","MINDI","LU","LORINDA","LARUE","FLORENE","DEMETRA","DEDRA","CIARA","CHANTELLE","ASHLY","SUZY","ROSALVA","NOELIA","LYDA","LEATHA","KRYSTYNA","KRISTAN","KARRI","DARLINE","DARCIE","CINDA","CHEYENNE","CHERRIE","AWILDA","ALMEDA","ROLANDA","LANETTE","JERILYN","GISELE","EVALYN","CYNDI","CLETA","CARIN","ZINA","ZENA","VELIA","TANIKA","PAUL","CHARISSA","THOMAS","TALIA","MARGARETE","LAVONDA","KAYLEE","KATHLENE","JONNA","IRENA","ILONA","IDALIA","CANDIS","CANDANCE","BRANDEE","ANITRA","ALIDA","SIGRID","NICOLETTE","MARYJO","LINETTE","HEDWIG","CHRISTIANA","CASSIDY","ALEXIA","TRESSIE","MODESTA","LUPITA","LITA","GLADIS","EVELIA","DAVIDA","CHERRI","CECILY","ASHELY","ANNABEL","AGUSTINA","WANITA","SHIRLY","ROSAURA","HULDA","EUN","BAILEY","YETTA","VERONA","THOMASINA","SIBYL","SHANNAN","MECHELLE","LUE","LEANDRA","LANI","KYLEE","KANDY","JOLYNN","FERNE","EBONI","CORENE","ALYSIA","ZULA","NADA","MOIRA","LYNDSAY","LORRETTA","JUAN","JAMMIE","HORTENSIA","GAYNELL","CAMERON","ADRIA","VINA","VICENTA","TANGELA","STEPHINE","NORINE","NELLA","LIANA","LESLEE","KIMBERELY","ILIANA","GLORY","FELICA","EMOGENE","ELFRIEDE","EDEN","EARTHA","CARMA","BEA","OCIE","MARRY","LENNIE","KIARA","JACALYN","CARLOTA","ARIELLE","YU","STAR","OTILIA","KIRSTIN","KACEY","JOHNETTA","JOEY","JOETTA","JERALDINE","JAUNITA","ELANA","DORTHEA","CAMI","AMADA","ADELIA","VERNITA","TAMAR","SIOBHAN","RENEA","RASHIDA","OUIDA","ODELL","NILSA","MERYL","KRISTYN","JULIETA","DANICA","BREANNE","AUREA","ANGLEA","SHERRON","ODETTE","MALIA","LORELEI","LIN","LEESA","KENNA","KATHLYN","FIONA","CHARLETTE","SUZIE","SHANTELL","SABRA","RACQUEL","MYONG","MIRA","MARTINE","LUCIENNE","LAVADA","JULIANN","JOHNIE","ELVERA","DELPHIA","CLAIR","CHRISTIANE","CHAROLETTE","CARRI","AUGUSTINE","ASHA","ANGELLA","PAOLA","NINFA","LEDA","LAI","EDA","SUNSHINE","STEFANI","SHANELL","PALMA","MACHELLE","LISSA","KECIA","KATHRYNE","KARLENE","JULISSA","JETTIE","JENNIFFER","HUI","CORRINA","CHRISTOPHER","CAROLANN","ALENA","TESS","ROSARIA","MYRTICE","MARYLEE","LIANE","KENYATTA","JUDIE","JANEY","IN","ELMIRA","ELDORA","DENNA","CRISTI","CATHI","ZAIDA","VONNIE","VIVA","VERNIE","ROSALINE","MARIELA","LUCIANA","LESLI","KARAN","FELICE","DENEEN","ADINA","WYNONA","TARSHA","SHERON","SHASTA","SHANITA","SHANI","SHANDRA","RANDA","PINKIE","PARIS","NELIDA","MARILOU","LYLA","LAURENE","LACI","JOI","JANENE","DOROTHA","DANIELE","DANI","CAROLYNN","CARLYN","BERENICE","AYESHA","ANNELIESE","ALETHEA","THERSA","TAMIKO","RUFINA","OLIVA","MOZELL","MARYLYN","MADISON","KRISTIAN","KATHYRN","KASANDRA","KANDACE","JANAE","GABRIEL","DOMENICA","DEBBRA","DANNIELLE","CHUN","BUFFY","BARBIE","ARCELIA","AJA","ZENOBIA","SHAREN","SHAREE","PATRICK","PAGE","MY","LAVINIA","KUM","KACIE","JACKELINE","HUONG","FELISA","EMELIA","ELEANORA","CYTHIA","CRISTIN","CLYDE","CLARIBEL","CARON","ANASTACIA","ZULMA","ZANDRA","YOKO","TENISHA","SUSANN","SHERILYN","SHAY","SHAWANDA","SABINE","ROMANA","MATHILDA","LINSEY","KEIKO","JOANA","ISELA","GRETTA","GEORGETTA","EUGENIE","DUSTY","DESIRAE","DELORA","CORAZON","ANTONINA","ANIKA","WILLENE","TRACEE","TAMATHA","REGAN","NICHELLE","MICKIE","MAEGAN","LUANA","LANITA","KELSIE","EDELMIRA","BREE","AFTON","TEODORA","TAMIE","SHENA","MEG","LINH","KELI","KACI","DANYELLE","BRITT","ARLETTE","ALBERTINE","ADELLE","TIFFINY","STORMY","SIMONA","NUMBERS","NICOLASA","NICHOL","NIA","NAKISHA","MEE","MAIRA","LOREEN","KIZZY","JOHNNY","JAY","FALLON","CHRISTENE","BOBBYE","ANTHONY","YING","VINCENZA","TANJA","RUBIE","RONI","QUEENIE","MARGARETT","KIMBERLI","IRMGARD","IDELL","HILMA","EVELINA","ESTA","EMILEE","DENNISE","DANIA","CARL","CARIE","ANTONIO","WAI","SANG","RISA","RIKKI","PARTICIA","MUI","MASAKO","MARIO","LUVENIA","LOREE","LONI","LIEN","KEVIN","GIGI","FLORENCIA","DORIAN","DENITA","DALLAS","CHI","BILLYE","ALEXANDER","TOMIKA","SHARITA","RANA","NIKOLE","NEOMA","MARGARITE","MADALYN","LUCINA","LAILA","KALI","JENETTE","GABRIELE","EVELYNE","ELENORA","CLEMENTINA","ALEJANDRINA","ZULEMA","VIOLETTE","VANNESSA","THRESA","RETTA","PIA","PATIENCE","NOELLA","NICKIE","JONELL","DELTA","CHUNG","CHAYA","CAMELIA","BETHEL","ANYA","ANDREW","THANH","SUZANN","SPRING","SHU","MILA","LILLA","LAVERNA","KEESHA","KATTIE","GIA","GEORGENE","EVELINE","ESTELL","ELIZBETH","VIVIENNE","VALLIE","TRUDIE","STEPHANE","MICHEL","MAGALY","MADIE","KENYETTA","KARREN","JANETTA","HERMINE","HARMONY","DRUCILLA","DEBBI","CELESTINA","CANDIE","BRITNI","BECKIE","AMINA","ZITA","YUN","YOLANDE","VIVIEN","VERNETTA","TRUDI","SOMMER","PEARLE","PATRINA","OSSIE","NICOLLE","LOYCE","LETTY","LARISA","KATHARINA","JOSELYN","JONELLE","JENELL","IESHA","HEIDE","FLORINDA","FLORENTINA","FLO","ELODIA","DORINE","BRUNILDA","BRIGID","ASHLI","ARDELLA","TWANA","THU","TARAH","SUNG","SHEA","SHAVON","SHANE","SERINA","RAYNA","RAMONITA","NGA","MARGURITE","LUCRECIA","KOURTNEY","KATI","JESUS","JESENIA","DIAMOND","CRISTA","AYANA","ALICA","ALIA","VINNIE","SUELLEN","ROMELIA","RACHELL","PIPER","OLYMPIA","MICHIKO","KATHALEEN","JOLIE","JESSI","JANESSA","HANA","HA","ELEASE","CARLETTA","BRITANY","SHONA","SALOME","ROSAMOND","REGENA","RAINA","NGOC","NELIA","LOUVENIA","LESIA","LATRINA","LATICIA","LARHONDA","JINA","JACKI","HOLLIS","HOLLEY","EMMY","DEEANN","CORETTA","ARNETTA","VELVET","THALIA","SHANICE","NETA","MIKKI","MICKI","LONNA","LEANA","LASHUNDA","KILEY","JOYE","JACQULYN","IGNACIA","HYUN","HIROKO","HENRY","HENRIETTE","ELAYNE","DELINDA","DARNELL","DAHLIA","COREEN","CONSUELA","CONCHITA","CELINE","BABETTE","AYANNA","ANETTE","ALBERTINA","SKYE","SHAWNEE","SHANEKA","QUIANA","PAMELIA","MIN","MERRI","MERLENE","MARGIT","KIESHA","KIERA","KAYLENE","JODEE","JENISE","ERLENE","EMMIE","ELSE","DARYL","DALILA","DAISEY","CODY","CASIE","BELIA","BABARA","VERSIE","VANESA","SHELBA","SHAWNDA","SAM","NORMAN","NIKIA","NAOMA","MARNA","MARGERET","MADALINE","LAWANA","KINDRA","JUTTA","JAZMINE","JANETT","HANNELORE","GLENDORA","GERTRUD","GARNETT","FREEDA","FREDERICA","FLORANCE","FLAVIA","DENNIS","CARLINE","BEVERLEE","ANJANETTE","VALDA","TRINITY","TAMALA","STEVIE","SHONNA","SHA","SARINA","ONEIDA","MICAH","MERILYN","MARLEEN","LURLINE","LENNA","KATHERIN","JIN","JENI","HAE","GRACIA","GLADY","FARAH","ERIC","ENOLA","EMA","DOMINQUE","DEVONA","DELANA","CECILA","CAPRICE","ALYSHA","ALI","ALETHIA","VENA","THERESIA","TAWNY","SONG","SHAKIRA","SAMARA","SACHIKO","RACHELE","PAMELLA","NICKY","MARNI","MARIEL","MAREN","MALISA","LIGIA","LERA","LATORIA","LARAE","KIMBER","KATHERN","KAREY","JENNEFER","JANETH","HALINA","FREDIA","DELISA","DEBROAH","CIERA","CHIN","ANGELIKA","ANDREE","ALTHA","YEN","VIVAN","TERRESA","TANNA","SUK","SUDIE","SOO","SIGNE","SALENA","RONNI","REBBECCA","MYRTIE","MCKENZIE","MALIKA","MAIDA","LOAN","LEONARDA","KAYLEIGH","FRANCE","ETHYL","ELLYN","DAYLE","CAMMIE","BRITTNI","BIRGIT","AVELINA","ASUNCION","ARIANNA","AKIKO","VENICE","TYESHA","TONIE","TIESHA","TAKISHA","STEFFANIE","SINDY","SANTANA","MEGHANN","MANDA","MACIE","LADY","KELLYE","KELLEE","JOSLYN","JASON","INGER","INDIRA","GLINDA","GLENNIS","FERNANDA","FAUSTINA","ENEIDA","ELICIA","DOT","DIGNA","DELL","ARLETTA","ANDRE","WILLIA","TAMMARA","TABETHA","SHERRELL","SARI","REFUGIO","REBBECA","PAULETTA","NIEVES","NATOSHA","NAKITA","MAMMIE","KENISHA","KAZUKO","KASSIE","GARY","EARLEAN","DAPHINE","CORLISS","CLOTILDE","CAROLYNE","BERNETTA","AUGUSTINA","AUDREA","ANNIS","ANNABELL","YAN","TENNILLE","TAMICA","SELENE","SEAN","ROSANA","REGENIA","QIANA","MARKITA","MACY","LEEANNE","LAURINE","KYM","JESSENIA","JANITA","GEORGINE","GENIE","EMIKO","ELVIE","DEANDRA","DAGMAR","CORIE","COLLEN","CHERISH","ROMAINE","PORSHA","PEARLENE","MICHELINE","MERNA","MARGORIE","MARGARETTA","LORE","KENNETH","JENINE","HERMINA","FREDERICKA","ELKE","DRUSILLA","DORATHY","DIONE","DESIRE","CELENA","BRIGIDA","ANGELES","ALLEGRA","THEO","TAMEKIA","SYNTHIA","STEPHEN","SOOK","SLYVIA","ROSANN","REATHA","RAYE","MARQUETTA","MARGART","LING","LAYLA","KYMBERLY","KIANA","KAYLEEN","KATLYN","KARMEN","JOELLA","IRINA","EMELDA","ELENI","DETRA","CLEMMIE","CHERYLL","CHANTELL","CATHEY","ARNITA","ARLA","ANGLE","ANGELIC","ALYSE","ZOFIA","THOMASINE","TENNIE","SON","SHERLY","SHERLEY","SHARYL","REMEDIOS","PETRINA","NICKOLE","MYUNG","MYRLE","MOZELLA","LOUANNE","LISHA","LATIA","LANE","KRYSTA","JULIENNE","JOEL","JEANENE","JACQUALINE","ISAURA","GWENDA","EARLEEN","DONALD","CLEOPATRA","CARLIE","AUDIE","ANTONIETTA","ALISE","ALEX","VERDELL","VAL","TYLER","TOMOKO","THAO","TALISHA","STEVEN","SO","SHEMIKA","SHAUN","SCARLET","SAVANNA","SANTINA","ROSIA","RAEANN","ODILIA","NANA","MINNA","MAGAN","LYNELLE","LE","KARMA","JOEANN","IVANA","INELL","ILANA","HYE","HONEY","HEE","GUDRUN","FRANK","DREAMA","CRISSY","CHANTE","CARMELINA","ARVILLA","ARTHUR","ANNAMAE","ALVERA","ALEIDA","AARON","YEE","YANIRA","VANDA","TIANNA","TAM","STEFANIA","SHIRA","PERRY","NICOL","NANCIE","MONSERRATE","MINH","MELYNDA","MELANY","MATTHEW","LOVELLA","LAURE","KIRBY","KACY","JACQUELYNN","HYON","GERTHA","FRANCISCO","ELIANA","CHRISTENA","CHRISTEEN","CHARISE","CATERINA","CARLEY","CANDYCE","ARLENA","AMMIE","YANG","WILLETTE","VANITA","TUYET","TINY","SYREETA","SILVA","SCOTT","RONALD","PENNEY","NYLA","MICHAL","MAURICE","MARYAM","MARYA","MAGEN","LUDIE","LOMA","LIVIA","LANELL","KIMBERLIE","JULEE","DONETTA","DIEDRA","DENISHA","DEANE","DAWNE","CLARINE","CHERRYL","BRONWYN","BRANDON","ALLA","VALERY","TONDA","SUEANN","SORAYA","SHOSHANA","SHELA","SHARLEEN","SHANELLE","NERISSA","MICHEAL","MERIDITH","MELLIE","MAYE","MAPLE","MAGARET","LUIS","LILI","LEONILA","LEONIE","LEEANNA","LAVONIA","LAVERA","KRISTEL","KATHEY","KATHE","JUSTIN","JULIAN","JIMMY","JANN","ILDA","HILDRED","HILDEGARDE","GENIA","FUMIKO","EVELIN","ERMELINDA","ELLY","DUNG","DOLORIS","DIONNA","DANAE","BERNEICE","ANNICE","ALIX","VERENA","VERDIE","TRISTAN","SHAWNNA","SHAWANA","SHAUNNA","ROZELLA","RANDEE","RANAE","MILAGRO","LYNELL","LUISE","LOUIE","LOIDA","LISBETH","KARLEEN","JUNITA","JONA","ISIS","HYACINTH","HEDY","GWENN","ETHELENE","ERLINE","EDWARD","DONYA","DOMONIQUE","DELICIA","DANNETTE","CICELY","BRANDA","BLYTHE","BETHANN","ASHLYN","ANNALEE","ALLINE","YUKO","VELLA","TRANG","TOWANDA","TESHA","SHERLYN","NARCISA","MIGUELINA","MERI","MAYBELL","MARLANA","MARGUERITA","MADLYN","LUNA","LORY","LORIANN","LIBERTY","LEONORE","LEIGHANN","LAURICE","LATESHA","LARONDA","KATRICE","KASIE","KARL","KALEY","JADWIGA","GLENNIE","GEARLDINE","FRANCINA","EPIFANIA","DYAN","DORIE","DIEDRE","DENESE","DEMETRICE","DELENA","DARBY","CRISTIE","CLEORA","CATARINA","CARISA","BERNIE","BARBERA","ALMETA","TRULA","TEREASA","SOLANGE","SHEILAH","SHAVONNE","SANORA","ROCHELL","MATHILDE","MARGARETA","MAIA","LYNSEY","LAWANNA","LAUNA","KENA","KEENA","KATIA","JAMEY","GLYNDA","GAYLENE","ELVINA","ELANOR","DANUTA","DANIKA","CRISTEN","CORDIE","COLETTA","CLARITA","CARMON","BRYNN","AZUCENA","AUNDREA","ANGELE","YI","WALTER","VERLIE","VERLENE","TAMESHA","SILVANA","SEBRINA","SAMIRA","REDA","RAYLENE","PENNI","PANDORA","NORAH","NOMA","MIREILLE","MELISSIA","MARYALICE","LARAINE","KIMBERY","KARYL","KARINE","KAM","JOLANDA","JOHANA","JESUSA","JALEESA","JAE","JACQUELYNE","IRISH","ILUMINADA","HILARIA","HANH","GENNIE","FRANCIE","FLORETTA","EXIE","EDDA","DREMA","DELPHA","BEV","BARBAR","ASSUNTA","ARDELL","ANNALISA","ALISIA","YUKIKO","YOLANDO","WONDA","WEI","WALTRAUD","VETA","TEQUILA","TEMEKA","TAMEIKA","SHIRLEEN","SHENITA","PIEDAD","OZELLA","MIRTHA","MARILU","KIMIKO","JULIANE","JENICE","JEN","JANAY","JACQUILINE","HILDE","FE","FAE","EVAN","EUGENE","ELOIS","ECHO","DEVORAH","CHAU","BRINDA","BETSEY","ARMINDA","ARACELIS","APRYL","ANNETT","ALISHIA","VEOLA","USHA","TOSHIKO","THEOLA","TASHIA","TALITHA","SHERY","RUDY","RENETTA","REIKO","RASHEEDA","OMEGA","OBDULIA","MIKA","MELAINE","MEGGAN","MARTIN","MARLEN","MARGET","MARCELINE","MANA","MAGDALEN","LIBRADA","LEZLIE","LEXIE","LATASHIA","LASANDRA","KELLE","ISIDRA","ISA","INOCENCIA","GWYN","FRANCOISE","ERMINIA","ERINN","DIMPLE","DEVORA","CRISELDA","ARMANDA","ARIE","ARIANE","ANGELO","ANGELENA","ALLEN","ALIZA","ADRIENE","ADALINE","XOCHITL","TWANNA","TRAN","TOMIKO","TAMISHA","TAISHA","SUSY","SIU","RUTHA","ROXY","RHONA","RAYMOND","OTHA","NORIKO","NATASHIA","MERRIE","MELVIN","MARINDA","MARIKO","MARGERT","LORIS","LIZZETTE","LEISHA","KAILA","KA","JOANNIE","JERRICA","JENE","JANNET","JANEE","JACINDA","HERTA","ELENORE","DORETTA","DELAINE","DANIELL","CLAUDIE","CHINA","BRITTA","APOLONIA","AMBERLY","ALEASE","YURI","YUK","WEN","WANETA","UTE","TOMI","SHARRI","SANDIE","ROSELLE","REYNALDA","RAGUEL","PHYLICIA","PATRIA","OLIMPIA","ODELIA","MITZIE","MITCHELL","MISS","MINDA","MIGNON","MICA","MENDY","MARIVEL","MAILE","LYNETTA","LAVETTE","LAURYN","LATRISHA","LAKIESHA","KIERSTEN","KARY","JOSPHINE","JOLYN","JETTA","JANISE","JACQUIE","IVELISSE","GLYNIS","GIANNA","GAYNELLE","EMERALD","DEMETRIUS","DANYELL","DANILLE","DACIA","CORALEE","CHER","CEOLA","BRETT","BELL","ARIANNE","ALESHIA","YUNG","WILLIEMAE","TROY","TRINH","THORA","TAI","SVETLANA","SHERIKA","SHEMEKA","SHAUNDA","ROSELINE","RICKI","MELDA","MALLIE","LAVONNA","LATINA","LARRY","LAQUANDA","LALA","LACHELLE","KLARA","KANDIS","JOHNA","JEANMARIE","JAYE","HANG","GRAYCE","GERTUDE","EMERITA","EBONIE","CLORINDA","CHING","CHERY","CAROLA","BREANN","BLOSSOM","BERNARDINE","BECKI","ARLETHA","ARGELIA","ARA","ALITA","YULANDA","YON","YESSENIA","TOBI","TASIA","SYLVIE","SHIRL","SHIRELY","SHERIDAN","SHELLA","SHANTELLE","SACHA","ROYCE","REBECKA","REAGAN","PROVIDENCIA","PAULENE","MISHA","MIKI","MARLINE","MARICA","LORITA","LATOYIA","LASONYA","KERSTIN","KENDA","KEITHA","KATHRIN","JAYMIE","JACK","GRICELDA","GINETTE","ERYN","ELINA","ELFRIEDA","DANYEL","CHEREE","CHANELLE","BARRIE","AVERY","AURORE","ANNAMARIA","ALLEEN","AILENE","AIDE","YASMINE","VASHTI","VALENTINE","TREASA","TORY","TIFFANEY","SHERYLL","SHARIE","SHANAE","SAU","RAISA","PA","NEDA","MITSUKO","MIRELLA","MILDA","MARYANNA","MARAGRET","MABELLE","LUETTA","LORINA","LETISHA","LATARSHA","LANELLE","LAJUANA","KRISSY","KARLY","KARENA","JON","JESSIKA","JERICA","JEANELLE","JANUARY","JALISA","JACELYN","IZOLA","IVEY","GREGORY","EUNA","ETHA","DREW","DOMITILA","DOMINICA","DAINA","CREOLA","CARLI","CAMIE","BUNNY","BRITTNY","ASHANTI","ANISHA","ALEEN","ADAH","YASUKO","WINTER","VIKI","VALRIE","TONA","TINISHA","THI","TERISA","TATUM","TANEKA","SIMONNE","SHALANDA","SERITA","RESSIE","REFUGIA","PAZ","OLENE","NA","MERRILL","MARGHERITA","MANDIE","MAN","MAIRE","LYNDIA","LUCI","LORRIANE","LORETA","LEONIA","LAVONA","LASHAWNDA","LAKIA","KYOKO","KRYSTINA","KRYSTEN","KENIA","KELSI","JUDE","JEANICE","ISOBEL","GEORGIANN","GENNY","FELICIDAD","EILENE","DEON","DELOISE","DEEDEE","DANNIE","CONCEPTION","CLORA","CHERILYN","CHANG","CALANDRA","BERRY","ARMANDINA","ANISA","ULA","TIMOTHY","TIERA","THERESSA","STEPHANIA","SIMA","SHYLA","SHONTA","SHERA","SHAQUITA","SHALA","SAMMY","ROSSANA","NOHEMI","NERY","MORIAH","MELITA","MELIDA","MELANI","MARYLYNN","MARISHA","MARIETTE","MALORIE","MADELENE","LUDIVINA","LORIA","LORETTE","LORALEE","LIANNE","LEON","LAVENIA","LAURINDA","LASHON","KIT","KIMI","KEILA","KATELYNN","KAI","JONE","JOANE","JI","JAYNA","JANELLA","JA","HUE","HERTHA","FRANCENE","ELINORE","DESPINA","DELSIE","DEEDRA","CLEMENCIA","CARRY","CAROLIN","CARLOS","BULAH","BRITTANIE","BOK","BLONDELL","BIBI","BEAULAH","BEATA","ANNITA","AGRIPINA","VIRGEN","VALENE","UN","TWANDA","TOMMYE","TOI","TARRA","TARI","TAMMERA","SHAKIA","SADYE","RUTHANNE","ROCHEL","RIVKA","PURA","NENITA","NATISHA","MING","MERRILEE","MELODEE","MARVIS","LUCILLA","LEENA","LAVETA","LARITA","LANIE","KEREN","ILEEN","GEORGEANN","GENNA","GENESIS","FRIDA","EWA","EUFEMIA","EMELY","ELA","EDYTH","DEONNA","DEADRA","DARLENA","CHANELL","CHAN","CATHERN","CASSONDRA","CASSAUNDRA","BERNARDA","BERNA","ARLINDA","ANAMARIA","ALBERT","WESLEY","VERTIE","VALERI","TORRI","TATYANA","STASIA","SHERISE","SHERILL","SEASON","SCOTTIE","SANDA","RUTHE","ROSY","ROBERTO","ROBBI","RANEE","QUYEN","PEARLY","PALMIRA","ONITA","NISHA","NIESHA","NIDA","NEVADA","NAM","MERLYN","MAYOLA","MARYLOUISE","MARYLAND","MARX","MARTH","MARGENE","MADELAINE","LONDA","LEONTINE","LEOMA","LEIA","LAWRENCE","LAURALEE","LANORA","LAKITA","KIYOKO","KETURAH","KATELIN","KAREEN","JONIE","JOHNETTE","JENEE","JEANETT","IZETTA","HIEDI","HEIKE","HASSIE","HAROLD","GIUSEPPINA","GEORGANN","FIDELA","FERNANDE","ELWANDA","ELLAMAE","ELIZ","DUSTI","DOTTY","CYNDY","CORALIE","CELESTA","ARGENTINA","ALVERTA","XENIA","WAVA","VANETTA","TORRIE","TASHINA","TANDY","TAMBRA","TAMA","STEPANIE","SHILA","SHAUNTA","SHARAN","SHANIQUA","SHAE","SETSUKO","SERAFINA","SANDEE","ROSAMARIA","PRISCILA","OLINDA","NADENE","MUOI","MICHELINA","MERCEDEZ","MARYROSE","MARIN","MARCENE","MAO","MAGALI","MAFALDA","LOGAN","LINN","LANNIE","KAYCE","KAROLINE","KAMILAH","KAMALA","JUSTA","JOLINE","JENNINE","JACQUETTA","IRAIDA","GERALD","GEORGEANNA","FRANCHESCA","FAIRY","EMELINE","ELANE","EHTEL","EARLIE","DULCIE","DALENE","CRIS","CLASSIE","CHERE","CHARIS","CAROYLN","CARMINA","CARITA","BRIAN","BETHANIE","AYAKO","ARICA","AN","ALYSA","ALESSANDRA","AKILAH","ADRIEN","ZETTA","YOULANDA","YELENA","YAHAIRA","XUAN","WENDOLYN","VICTOR","TIJUANA","TERRELL","TERINA","TERESIA","SUZI","SUNDAY","SHERELL","SHAVONDA","SHAUNTE","SHARDA","SHAKITA","SENA","RYANN","RUBI","RIVA","REGINIA","REA","RACHAL","PARTHENIA","PAMULA","MONNIE","MONET","MICHAELE","MELIA","MARINE","MALKA","MAISHA","LISANDRA","LEO","LEKISHA","LEAN","LAURENCE","LAKENDRA","KRYSTIN","KORTNEY","KIZZIE","KITTIE","KERA","KENDAL","KEMBERLY","KANISHA","JULENE","JULE","JOSHUA","JOHANNE","JEFFREY","JAMEE","HAN","HALLEY","GIDGET","GALINA","FREDRICKA","FLETA","FATIMAH","EUSEBIA","ELZA","ELEONORE","DORTHEY","DORIA","DONELLA","DINORAH","DELORSE","CLARETHA","CHRISTINIA","CHARLYN","BONG","BELKIS","AZZIE","ANDERA","AIKO","ADENA","YER","YAJAIRA","WAN","VANIA","ULRIKE","TOSHIA","TIFANY","STEFANY","SHIZUE","SHENIKA","SHAWANNA","SHAROLYN","SHARILYN","SHAQUANA","SHANTAY","SEE","ROZANNE","ROSELEE","RICKIE","REMONA","REANNA","RAELENE","QUINN","PHUNG","PETRONILA","NATACHA","NANCEY","MYRL","MIYOKO","MIESHA","MERIDETH","MARVELLA","MARQUITTA","MARHTA","MARCHELLE","LIZETH","LIBBIE","LAHOMA","LADAWN","KINA","KATHELEEN","KATHARYN","KARISA","KALEIGH","JUNIE","JULIEANN","JOHNSIE","JANEAN","JAIMEE","JACKQUELINE","HISAKO","HERMA","HELAINE","GWYNETH","GLENN","GITA","EUSTOLIA","EMELINA","ELIN","EDRIS","DONNETTE","DONNETTA","DIERDRE","DENAE","DARCEL","CLAUDE","CLARISA","CINDERELLA","CHIA","CHARLESETTA","CHARITA","CELSA","CASSY","CASSI","CARLEE","BRUNA","BRITTANEY","BRANDE","BILLI","BAO","ANTONETTA","ANGLA","ANGELYN","ANALISA","ALANE","WENONA","WENDIE","VERONIQUE","VANNESA","TOBIE","TEMPIE","SUMIKO","SULEMA","SPARKLE","SOMER","SHEBA","SHAYNE","SHARICE","SHANEL","SHALON","SAGE","ROY","ROSIO","ROSELIA","RENAY","REMA","REENA","PORSCHE","PING","PEG","OZIE","ORETHA","ORALEE","ODA","NU","NGAN","NAKESHA","MILLY","MARYBELLE","MARLIN","MARIS","MARGRETT","MARAGARET","MANIE","LURLENE","LILLIA","LIESELOTTE","LAVELLE","LASHAUNDA","LAKEESHA","KEITH","KAYCEE","KALYN","JOYA","JOETTE","JENAE","JANIECE","ILLA","GRISEL","GLAYDS","GENEVIE","GALA","FREDDA","FRED","ELMER","ELEONOR","DEBERA","DEANDREA","DAN","CORRINNE","CORDIA","CONTESSA","COLENE","CLEOTILDE","CHARLOTT","CHANTAY","CECILLE","BEATRIS","AZALEE","ARLEAN","ARDATH","ANJELICA","ANJA","ALFREDIA","ALEISHA","ADAM","ZADA","YUONNE","XIAO","WILLODEAN","WHITLEY","VENNIE","VANNA","TYISHA","TOVA","TORIE","TONISHA","TILDA","TIEN","TEMPLE","SIRENA","SHERRIL","SHANTI","SHAN","SENAIDA","SAMELLA","ROBBYN","RENDA","REITA","PHEBE","PAULITA","NOBUKO","NGUYET","NEOMI","MOON","MIKAELA","MELANIA","MAXIMINA","MARG","MAISIE","LYNNA","LILLI","LAYNE","LASHAUN","LAKENYA","LAEL","KIRSTIE","KATHLINE","KASHA","KARLYN","KARIMA","JOVAN","JOSEFINE","JENNELL","JACQUI","JACKELYN","HYO","HIEN","GRAZYNA","FLORRIE","FLORIA","ELEONORA","DWANA","DORLA","DONG","DELMY","DEJA","DEDE","DANN","CRYSTA","CLELIA","CLARIS","CLARENCE","CHIEKO","CHERLYN","CHERELLE","CHARMAIN","CHARA","CAMMY","BEE","ARNETTE","ARDELLE","ANNIKA","AMIEE","AMEE","ALLENA","YVONE","YUKI","YOSHIE","YEVETTE","YAEL","WILLETTA","VONCILE","VENETTA","TULA","TONETTE","TIMIKA","TEMIKA","TELMA","TEISHA","TAREN","TA","STACEE","SHIN","SHAWNTA","SATURNINA","RICARDA","POK","PASTY","ONIE","NUBIA","MORA","MIKE","MARIELLE","MARIELLA","MARIANELA","MARDELL","MANY","LUANNA","LOISE","LISABETH","LINDSY","LILLIANA","LILLIAM","LELAH","LEIGHA","LEANORA","LANG","KRISTEEN","KHALILAH","KEELEY","KANDRA","JUNKO","JOAQUINA","JERLENE","JANI","JAMIKA","JAME","HSIU","HERMILA","GOLDEN","GENEVIVE","EVIA","EUGENA","EMMALINE","ELFREDA","ELENE","DONETTE","DELCIE","DEEANNA","DARCEY","CUC","CLARINDA","CIRA","CHAE","CELINDA","CATHERYN","CATHERIN","CASIMIRA","CARMELIA","CAMELLIA","BREANA","BOBETTE","BERNARDINA","BEBE","BASILIA","ARLYNE","AMAL","ALAYNA","ZONIA","ZENIA","YURIKO","YAEKO","WYNELL","WILLOW","WILLENA","VERNIA","TU","TRAVIS","TORA","TERRILYN","TERICA","TENESHA","TAWNA","TAJUANA","TAINA","STEPHNIE","SONA","SOL","SINA","SHONDRA","SHIZUKO","SHERLENE","SHERICE","SHARIKA","ROSSIE","ROSENA","RORY","RIMA","RIA","RHEBA","RENNA","PETER","NATALYA","NANCEE","MELODI","MEDA","MAXIMA","MATHA","MARKETTA","MARICRUZ","MARCELENE","MALVINA","LUBA","LOUETTA","LEIDA","LECIA","LAURAN","LASHAWNA","LAINE","KHADIJAH","KATERINE","KASI","KALLIE","JULIETTA","JESUSITA","JESTINE","JESSIA","JEREMY","JEFFIE","JANYCE","ISADORA","GEORGIANNE","FIDELIA","EVITA","EURA","EULAH","ESTEFANA","ELSY","ELIZABET","ELADIA","DODIE","DION","DIA","DENISSE","DELORAS","DELILA","DAYSI","DAKOTA","CURTIS","CRYSTLE","CONCHA","COLBY","CLARETTA","CHU","CHRISTIA","CHARLSIE","CHARLENA","CARYLON","BETTYANN","ASLEY","ASHLEA","AMIRA","AI","AGUEDA","AGNUS","YUETTE","VINITA","VICTORINA","TYNISHA","TREENA","TOCCARA","TISH","THOMASENA","TEGAN","SOILA","SHILOH","SHENNA","SHARMAINE","SHANTAE","SHANDI","SEPTEMBER","SARAN","SARAI","SANA","SAMUEL","SALLEY","ROSETTE","ROLANDE","REGINE","OTELIA","OSCAR","OLEVIA","NICHOLLE","NECOLE","NAIDA","MYRTA","MYESHA","MITSUE","MINTA","MERTIE","MARGY","MAHALIA","MADALENE","LOVE","LOURA","LOREAN","LEWIS","LESHA","LEONIDA","LENITA","LAVONE","LASHELL","LASHANDRA","LAMONICA","KIMBRA","KATHERINA","KARRY","KANESHA","JULIO","JONG","JENEVA","JAQUELYN","HWA","GILMA","GHISLAINE","GERTRUDIS","FRANSISCA","FERMINA","ETTIE","ETSUKO","ELLIS","ELLAN","ELIDIA","EDRA","DORETHEA","DOREATHA","DENYSE","DENNY","DEETTA","DAINE","CYRSTAL","CORRIN","CAYLA","CARLITA","CAMILA","BURMA","BULA","BUENA","BLAKE","BARABARA","AVRIL","AUSTIN","ALAINE","ZANA","WILHEMINA","WANETTA","VIRGIL","VI","VERONIKA","VERNON","VERLINE","VASILIKI","TONITA","TISA","TEOFILA","TAYNA","TAUNYA","TANDRA","TAKAKO","SUNNI","SUANNE","SIXTA","SHARELL","SEEMA","RUSSELL","ROSENDA","ROBENA","RAYMONDE","PEI","PAMILA","OZELL","NEIDA","NEELY","MISTIE","MICHA","MERISSA","MAURITA","MARYLN","MARYETTA","MARSHALL","MARCELL","MALENA","MAKEDA","MADDIE","LOVETTA","LOURIE","LORRINE","LORILEE","LESTER","LAURENA","LASHAY","LARRAINE","LAREE","LACRESHA","KRISTLE","KRISHNA","KEVA","KEIRA","KAROLE","JOIE","JINNY","JEANNETTA","JAMA","HEIDY","GILBERTE","GEMA","FAVIOLA","EVELYNN","ENDA","ELLI","ELLENA","DIVINA","DAGNY","COLLENE","CODI","CINDIE","CHASSIDY","CHASIDY","CATRICE","CATHERINA","CASSEY","CAROLL","CARLENA","CANDRA","CALISTA","BRYANNA","BRITTENY","BEULA","BARI","AUDRIE","AUDRIA","ARDELIA","ANNELLE","ANGILA","ALONA","ALLYN","DOUGLAS","ROGER","JONATHAN","RALPH","NICHOLAS","BENJAMIN","BRUCE","HARRY","WAYNE","STEVE","HOWARD","ERNEST","PHILLIP","TODD","CRAIG","ALAN","PHILIP","EARL","DANNY","BRYAN","STANLEY","LEONARD","NATHAN","MANUEL","RODNEY","MARVIN","VINCENT","JEFFERY","JEFF","CHAD","JACOB","ALFRED","BRADLEY","HERBERT","FREDERICK","EDWIN","DON","RICKY","RANDALL","BARRY","BERNARD","LEROY","MARCUS","THEODORE","CLIFFORD","MIGUEL","JIM","TOM","CALVIN","BILL","LLOYD","DEREK","WARREN","DARRELL","JEROME","FLOYD","ALVIN","TIM","GORDON","GREG","JORGE","DUSTIN","PEDRO","DERRICK","ZACHARY","HERMAN","GLEN","HECTOR","RICARDO","RICK","BRENT","RAMON","GILBERT","MARC","REGINALD","RUBEN","NATHANIEL","RAFAEL","EDGAR","MILTON","RAUL","BEN","CHESTER","DUANE","FRANKLIN","BRAD","RON","ROLAND","ARNOLD","HARVEY","JARED","ERIK","DARRYL","NEIL","JAVIER","FERNANDO","CLINTON","TED","MATHEW","TYRONE","DARREN","LANCE","KURT","ALLAN","NELSON","GUY","CLAYTON","HUGH","MAX","DWAYNE","DWIGHT","ARMANDO","FELIX","EVERETT","IAN","WALLACE","KEN","BOB","ALFREDO","ALBERTO","DAVE","IVAN","BYRON","ISAAC","MORRIS","CLIFTON","WILLARD","ROSS","ANDY","SALVADOR","KIRK","SERGIO","SETH","KENT","TERRANCE","EDUARDO","TERRENCE","ENRIQUE","WADE","STUART","FREDRICK","ARTURO","ALEJANDRO","NICK","LUTHER","WENDELL","JEREMIAH","JULIUS","OTIS","TREVOR","OLIVER","LUKE","HOMER","GERARD","DOUG","KENNY","HUBERT","LYLE","MATT","ALFONSO","ORLANDO","REX","CARLTON","ERNESTO","NEAL","PABLO","LORENZO","OMAR","WILBUR","GRANT","HORACE","RODERICK","ABRAHAM","WILLIS","RICKEY","ANDRES","CESAR","JOHNATHAN","MALCOLM","RUDOLPH","DAMON","KELVIN","PRESTON","ALTON","ARCHIE","MARCO","WM","PETE","RANDOLPH","GARRY","GEOFFREY","JONATHON","FELIPE","GERARDO","ED","DOMINIC","DELBERT","COLIN","GUILLERMO","EARNEST","LUCAS","BENNY","SPENCER","RODOLFO","MYRON","EDMUND","GARRETT","SALVATORE","CEDRIC","LOWELL","GREGG","SHERMAN","WILSON","SYLVESTER","ROOSEVELT","ISRAEL","JERMAINE","FORREST","WILBERT","LELAND","SIMON","CLARK","IRVING","BRYANT","OWEN","RUFUS","WOODROW","KRISTOPHER","MACK","LEVI","MARCOS","GUSTAVO","JAKE","LIONEL","GILBERTO","CLINT","NICOLAS","ISMAEL","ORVILLE","ERVIN","DEWEY","AL","WILFRED","JOSH","HUGO","IGNACIO","CALEB","TOMAS","SHELDON","ERICK","STEWART","DOYLE","DARREL","ROGELIO","TERENCE","SANTIAGO","ALONZO","ELIAS","BERT","ELBERT","RAMIRO","CONRAD","NOAH","GRADY","PHIL","CORNELIUS","LAMAR","ROLANDO","CLAY","PERCY","DEXTER","BRADFORD","DARIN","AMOS","MOSES","IRVIN","SAUL","ROMAN","RANDAL","TIMMY","DARRIN","WINSTON","BRENDAN","ABEL","DOMINICK","BOYD","EMILIO","ELIJAH","DOMINGO","EMMETT","MARLON","EMANUEL","JERALD","EDMOND","EMIL","DEWAYNE","WILL","OTTO","TEDDY","REYNALDO","BRET","JESS","TRENT","HUMBERTO","EMMANUEL","STEPHAN","VICENTE","LAMONT","GARLAND","MILES","EFRAIN","HEATH","RODGER","HARLEY","ETHAN","ELDON","ROCKY","PIERRE","JUNIOR","FREDDY","ELI","BRYCE","ANTOINE","STERLING","CHASE","GROVER","ELTON","CLEVELAND","DYLAN","CHUCK","DAMIAN","REUBEN","STAN","AUGUST","LEONARDO","JASPER","RUSSEL","ERWIN","BENITO","HANS","MONTE","BLAINE","ERNIE","CURT","QUENTIN","AGUSTIN","MURRAY","JAMAL","ADOLFO","HARRISON","TYSON","BURTON","BRADY","ELLIOTT","WILFREDO","BART","JARROD","VANCE","DENIS","DAMIEN","JOAQUIN","HARLAN","DESMOND","ELLIOT","DARWIN","GREGORIO","BUDDY","XAVIER","KERMIT","ROSCOE","ESTEBAN","ANTON","SOLOMON","SCOTTY","NORBERT","ELVIN","WILLIAMS","NOLAN","ROD","QUINTON","HAL","BRAIN","ROB","ELWOOD","KENDRICK","DARIUS","MOISES","FIDEL","THADDEUS","CLIFF","MARCEL","JACKSON","RAPHAEL","BRYON","ARMAND","ALVARO","JEFFRY","DANE","JOESPH","THURMAN","NED","RUSTY","MONTY","FABIAN","REGGIE","MASON","GRAHAM","ISAIAH","VAUGHN","GUS","LOYD","DIEGO","ADOLPH","NORRIS","MILLARD","ROCCO","GONZALO","DERICK","RODRIGO","WILEY","RIGOBERTO","ALPHONSO","TY","NOE","VERN","REED","JEFFERSON","ELVIS","BERNARDO","MAURICIO","HIRAM","DONOVAN","BASIL","RILEY","NICKOLAS","MAYNARD","SCOT","VINCE","QUINCY","EDDY","SEBASTIAN","FEDERICO","ULYSSES","HERIBERTO","DONNELL","COLE","DAVIS","GAVIN","EMERY","WARD","ROMEO","JAYSON","DANTE","CLEMENT","COY","MAXWELL","JARVIS","BRUNO","ISSAC","DUDLEY","BROCK","SANFORD","CARMELO","BARNEY","NESTOR","STEFAN","DONNY","ART","LINWOOD","BEAU","WELDON","GALEN","ISIDRO","TRUMAN","DELMAR","JOHNATHON","SILAS","FREDERIC","DICK","IRWIN","MERLIN","CHARLEY","MARCELINO","HARRIS","CARLO","TRENTON","KURTIS","HUNTER","AURELIO","WINFRED","VITO","COLLIN","DENVER","CARTER","LEONEL","EMORY","PASQUALE","MOHAMMAD","MARIANO","DANIAL","LANDON","DIRK","BRANDEN","ADAN","BUFORD","GERMAN","WILMER","EMERSON","ZACHERY","FLETCHER","JACQUES","ERROL","DALTON","MONROE","JOSUE","EDWARDO","BOOKER","WILFORD","SONNY","SHELTON","CARSON","THERON","RAYMUNDO","DAREN","HOUSTON","ROBBY","LINCOLN","GENARO","BENNETT","OCTAVIO","CORNELL","HUNG","ARRON","ANTONY","HERSCHEL","GIOVANNI","GARTH","CYRUS","CYRIL","RONNY","LON","FREEMAN","DUNCAN","KENNITH","CARMINE","ERICH","CHADWICK","WILBURN","RUSS","REID","MYLES","ANDERSON","MORTON","JONAS","FOREST","MITCHEL","MERVIN","ZANE","RICH","JAMEL","LAZARO","ALPHONSE","RANDELL","MAJOR","JARRETT","BROOKS","ABDUL","LUCIANO","SEYMOUR","EUGENIO","MOHAMMED","VALENTIN","CHANCE","ARNULFO","LUCIEN","FERDINAND","THAD","EZRA","ALDO","RUBIN","ROYAL","MITCH","EARLE","ABE","WYATT","MARQUIS","LANNY","KAREEM","JAMAR","BORIS","ISIAH","EMILE","ELMO","ARON","LEOPOLDO","EVERETTE","JOSEF","ELOY","RODRICK","REINALDO","LUCIO","JERROD","WESTON","HERSHEL","BARTON","PARKER","LEMUEL","BURT","JULES","GIL","ELISEO","AHMAD","NIGEL","EFREN","ANTWAN","ALDEN","MARGARITO","COLEMAN","DINO","OSVALDO","LES","DEANDRE","NORMAND","KIETH","TREY","NORBERTO","NAPOLEON","JEROLD","FRITZ","ROSENDO","MILFORD","CHRISTOPER","ALFONZO","LYMAN","JOSIAH","BRANT","WILTON","RICO","JAMAAL","DEWITT","BRENTON","OLIN","FOSTER","FAUSTINO","CLAUDIO","JUDSON","GINO","EDGARDO","ALEC","TANNER","JARRED","DONN","TAD","PRINCE","PORFIRIO","ODIS","LENARD","CHAUNCEY","TOD","MEL","MARCELO","KORY","AUGUSTUS","KEVEN","HILARIO","BUD","SAL","ORVAL","MAURO","ZACHARIAH","OLEN","ANIBAL","MILO","JED","DILLON","AMADO","NEWTON","LENNY","RICHIE","HORACIO","BRICE","MOHAMED","DELMER","DARIO","REYES","MAC","JONAH","JERROLD","ROBT","HANK","RUPERT","ROLLAND","KENTON","DAMION","ANTONE","WALDO","FREDRIC","BRADLY","KIP","BURL","WALKER","TYREE","JEFFEREY","AHMED","WILLY","STANFORD","OREN","NOBLE","MOSHE","MIKEL","ENOCH","BRENDON","QUINTIN","JAMISON","FLORENCIO","DARRICK","TOBIAS","HASSAN","GIUSEPPE","DEMARCUS","CLETUS","TYRELL","LYNDON","KEENAN","WERNER","GERALDO","COLUMBUS","CHET","BERTRAM","MARKUS","HUEY","HILTON","DWAIN","DONTE","TYRON","OMER","ISAIAS","HIPOLITO","FERMIN","ADALBERTO","BO","BARRETT","TEODORO","MCKINLEY","MAXIMO","GARFIELD","RALEIGH","LAWERENCE","ABRAM","RASHAD","KING","EMMITT","DARON","SAMUAL","MIQUEL","EUSEBIO","DOMENIC","DARRON","BUSTER","WILBER","RENATO","JC","HOYT","HAYWOOD","EZEKIEL","CHAS","FLORENTINO","ELROY","CLEMENTE","ARDEN","NEVILLE","EDISON","DESHAWN","NATHANIAL","JORDON","DANILO","CLAUD","SHERWOOD","RAYMON","RAYFORD","CRISTOBAL","AMBROSE","TITUS","HYMAN","FELTON","EZEQUIEL","ERASMO","STANTON","LONNY","LEN","IKE","MILAN","LINO","JAROD","HERB","ANDREAS","WALTON","RHETT","PALMER","DOUGLASS","CORDELL","OSWALDO","ELLSWORTH","VIRGILIO","TONEY","NATHANAEL","DEL","BENEDICT","MOSE","JOHNSON","ISREAL","GARRET","FAUSTO","ASA","ARLEN","ZACK","WARNER","MODESTO","FRANCESCO","MANUAL","GAYLORD","GASTON","FILIBERTO","DEANGELO","MICHALE","GRANVILLE","WES","MALIK","ZACKARY","TUAN","ELDRIDGE","CRISTOPHER","CORTEZ","ANTIONE","MALCOM","LONG","KOREY","JOSPEH","COLTON","WAYLON","VON","HOSEA","SHAD","SANTO","RUDOLF","ROLF","REY","RENALDO","MARCELLUS","LUCIUS","KRISTOFER","BOYCE","BENTON","HAYDEN","HARLAND","ARNOLDO","RUEBEN","LEANDRO","KRAIG","JERRELL","JEROMY","HOBERT","CEDRICK","ARLIE","WINFORD","WALLY","LUIGI","KENETH","JACINTO","GRAIG","FRANKLYN","EDMUNDO","SID","PORTER","LEIF","JERAMY","BUCK","WILLIAN","VINCENZO","SHON","LYNWOOD","JERE","HAI","ELDEN","DORSEY","DARELL","BRODERICK","ALONSO" \ No newline at end of file diff --git a/project_euler/problem_022/sol1.py b/project_euler/problem_022/sol1.py new file mode 100644 index 000000000000..982906245e87 --- /dev/null +++ b/project_euler/problem_022/sol1.py @@ -0,0 +1,45 @@ +""" +Name scores +Problem 22 + +Using names.txt (right click and 'Save Link/Target As...'), a 46K text file +containing over five-thousand first names, begin by sorting it into +alphabetical order. Then working out the alphabetical value for each name, +multiply this value by its alphabetical position in the list to obtain a name +score. + +For example, when the list is sorted into alphabetical order, COLIN, which is +worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would +obtain a score of 938 × 53 = 49714. + +What is the total of all the name scores in the file? +""" +import os + + +def solution(): + """Returns the total of all the name scores in the file. + + >>> solution() + 871198282 + """ + with open(os.path.dirname(__file__) + "/p022_names.txt") as file: + names = str(file.readlines()[0]) + names = names.replace('"', "").split(",") + + names.sort() + + name_score = 0 + total_score = 0 + + for i, name in enumerate(names): + for letter in name: + name_score += ord(letter) - 64 + + total_score += (i + 1) * name_score + name_score = 0 + return total_score + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_022/sol2.py b/project_euler/problem_022/sol2.py new file mode 100644 index 000000000000..5ae41c84686e --- /dev/null +++ b/project_euler/problem_022/sol2.py @@ -0,0 +1,42 @@ +""" +Name scores +Problem 22 + +Using names.txt (right click and 'Save Link/Target As...'), a 46K text file +containing over five-thousand first names, begin by sorting it into +alphabetical order. Then working out the alphabetical value for each name, +multiply this value by its alphabetical position in the list to obtain a name +score. + +For example, when the list is sorted into alphabetical order, COLIN, which is +worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would +obtain a score of 938 × 53 = 49714. + +What is the total of all the name scores in the file? +""" +import os + + +def solution(): + """Returns the total of all the name scores in the file. + + >>> solution() + 871198282 + """ + total_sum = 0 + temp_sum = 0 + with open(os.path.dirname(__file__) + "/p022_names.txt") as file: + name = str(file.readlines()[0]) + name = name.replace('"', "").split(",") + + name.sort() + for i in range(len(name)): + for j in name[i]: + temp_sum += ord(j) - ord("A") + 1 + total_sum += (i + 1) * temp_sum + temp_sum = 0 + return total_sum + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_023/__init__.py b/project_euler/problem_023/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_023/sol1.py b/project_euler/problem_023/sol1.py new file mode 100644 index 000000000000..a72b6123e3ee --- /dev/null +++ b/project_euler/problem_023/sol1.py @@ -0,0 +1,52 @@ +""" +A perfect number is a number for which the sum of its proper divisors is exactly +equal to the number. For example, the sum of the proper divisors of 28 would be +1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number. + +A number n is called deficient if the sum of its proper divisors is less than n +and it is called abundant if this sum exceeds n. + +As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest +number that can be written as the sum of two abundant numbers is 24. By +mathematical analysis, it can be shown that all integers greater than 28123 +can be written as the sum of two abundant numbers. However, this upper limit +cannot be reduced any further by analysis even though it is known that the +greatest number that cannot be expressed as the sum of two abundant numbers +is less than this limit. + +Find the sum of all the positive integers which cannot be written as the sum +of two abundant numbers. +""" + + +def solution(limit=28123): + """ + Finds the sum of all the positive integers which cannot be written as + the sum of two abundant numbers + as described by the statement above. + + >>> solution() + 4179871 + """ + sumDivs = [1] * (limit + 1) + + for i in range(2, int(limit ** 0.5) + 1): + sumDivs[i * i] += i + for k in range(i + 1, limit // i + 1): + sumDivs[k * i] += k + i + + abundants = set() + res = 0 + + for n in range(1, limit + 1): + if sumDivs[n] > n: + abundants.add(n) + + if not any((n - a in abundants) for a in abundants): + res += n + + return res + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_024/__init__.py b/project_euler/problem_024/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_024/sol1.py b/project_euler/problem_024/sol1.py new file mode 100644 index 000000000000..1c6378b38260 --- /dev/null +++ b/project_euler/problem_024/sol1.py @@ -0,0 +1,27 @@ +""" +A permutation is an ordered arrangement of objects. For example, 3124 is one +possible permutation of the digits 1, 2, 3 and 4. If all of the permutations +are listed numerically or alphabetically, we call it lexicographic order. The +lexicographic permutations of 0, 1 and 2 are: + + 012 021 102 120 201 210 + +What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, +6, 7, 8 and 9? +""" +from itertools import permutations + + +def solution(): + """Returns the millionth lexicographic permutation of the digits 0, 1, 2, + 3, 4, 5, 6, 7, 8 and 9. + + >>> solution() + '2783915460' + """ + result = list(map("".join, permutations("0123456789"))) + return result[999999] + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_025/__init__.py b/project_euler/problem_025/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_025/sol1.py b/project_euler/problem_025/sol1.py new file mode 100644 index 000000000000..c30a74a43cb0 --- /dev/null +++ b/project_euler/problem_025/sol1.py @@ -0,0 +1,101 @@ +""" +The Fibonacci sequence is defined by the recurrence relation: + + Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + +Hence the first 12 terms will be: + + F1 = 1 + F2 = 1 + F3 = 2 + F4 = 3 + F5 = 5 + F6 = 8 + F7 = 13 + F8 = 21 + F9 = 34 + F10 = 55 + F11 = 89 + F12 = 144 + +The 12th term, F12, is the first term to contain three digits. + +What is the index of the first term in the Fibonacci sequence to contain 1000 +digits? +""" + + +def fibonacci(n: int) -> int: + """ + Computes the Fibonacci number for input n by iterating through n numbers + and creating an array of ints using the Fibonacci formula. + Returns the nth element of the array. + + >>> fibonacci(2) + 1 + >>> fibonacci(3) + 2 + >>> fibonacci(5) + 5 + >>> fibonacci(10) + 55 + >>> fibonacci(12) + 144 + + """ + if n == 1 or type(n) is not int: + return 0 + elif n == 2: + return 1 + else: + sequence = [0, 1] + for i in range(2, n + 1): + sequence.append(sequence[i - 1] + sequence[i - 2]) + + return sequence[n] + + +def fibonacci_digits_index(n: int) -> int: + """ + Computes incrementing Fibonacci numbers starting from 3 until the length + of the resulting Fibonacci result is the input value n. Returns the term + of the Fibonacci sequence where this occurs. + + >>> fibonacci_digits_index(1000) + 4782 + >>> fibonacci_digits_index(100) + 476 + >>> fibonacci_digits_index(50) + 237 + >>> fibonacci_digits_index(3) + 12 + """ + digits = 0 + index = 2 + + while digits < n: + index += 1 + digits = len(str(fibonacci(index))) + + return index + + +def solution(n: int = 1000) -> int: + """ + Returns the index of the first term in the Fibonacci sequence to contain + n digits. + + >>> solution(1000) + 4782 + >>> solution(100) + 476 + >>> solution(50) + 237 + >>> solution(3) + 12 + """ + return fibonacci_digits_index(n) + + +if __name__ == "__main__": + print(solution(int(str(input()).strip()))) diff --git a/project_euler/problem_025/sol2.py b/project_euler/problem_025/sol2.py new file mode 100644 index 000000000000..ed3b54bb351f --- /dev/null +++ b/project_euler/problem_025/sol2.py @@ -0,0 +1,71 @@ +""" +The Fibonacci sequence is defined by the recurrence relation: + + Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + +Hence the first 12 terms will be: + + F1 = 1 + F2 = 1 + F3 = 2 + F4 = 3 + F5 = 5 + F6 = 8 + F7 = 13 + F8 = 21 + F9 = 34 + F10 = 55 + F11 = 89 + F12 = 144 + +The 12th term, F12, is the first term to contain three digits. + +What is the index of the first term in the Fibonacci sequence to contain 1000 +digits? +""" + + +def fibonacci_generator() -> int: + """ + A generator that produces numbers in the Fibonacci sequence + + >>> generator = fibonacci_generator() + >>> next(generator) + 1 + >>> next(generator) + 2 + >>> next(generator) + 3 + >>> next(generator) + 5 + >>> next(generator) + 8 + """ + a, b = 0, 1 + while True: + a, b = b, a + b + yield b + + +def solution(n: int = 1000) -> int: + """Returns the index of the first term in the Fibonacci sequence to contain + n digits. + + >>> solution(1000) + 4782 + >>> solution(100) + 476 + >>> solution(50) + 237 + >>> solution(3) + 12 + """ + answer = 1 + gen = fibonacci_generator() + while len(str(next(gen))) < n: + answer += 1 + return answer + 1 + + +if __name__ == "__main__": + print(solution(int(str(input()).strip()))) diff --git a/project_euler/problem_025/sol3.py b/project_euler/problem_025/sol3.py new file mode 100644 index 000000000000..c66411dc55fc --- /dev/null +++ b/project_euler/problem_025/sol3.py @@ -0,0 +1,56 @@ +""" +The Fibonacci sequence is defined by the recurrence relation: + + Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. + +Hence the first 12 terms will be: + + F1 = 1 + F2 = 1 + F3 = 2 + F4 = 3 + F5 = 5 + F6 = 8 + F7 = 13 + F8 = 21 + F9 = 34 + F10 = 55 + F11 = 89 + F12 = 144 + +The 12th term, F12, is the first term to contain three digits. + +What is the index of the first term in the Fibonacci sequence to contain 1000 +digits? +""" + + +def solution(n: int = 1000) -> int: + """Returns the index of the first term in the Fibonacci sequence to contain + n digits. + + >>> solution(1000) + 4782 + >>> solution(100) + 476 + >>> solution(50) + 237 + >>> solution(3) + 12 + """ + f1, f2 = 1, 1 + index = 2 + while True: + i = 0 + f = f1 + f2 + f1, f2 = f2, f + index += 1 + for j in str(f): + i += 1 + if i == n: + break + return index + + +if __name__ == "__main__": + print(solution(int(str(input()).strip()))) diff --git a/project_euler/problem_026/__init__.py b/project_euler/problem_026/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_026/sol1.py b/project_euler/problem_026/sol1.py new file mode 100644 index 000000000000..64e0bbfef472 --- /dev/null +++ b/project_euler/problem_026/sol1.py @@ -0,0 +1,60 @@ +""" +Euler Problem 26 +https://projecteuler.net/problem=26 + +Problem Statement: + +A unit fraction contains 1 in the numerator. The decimal representation of the +unit fractions with denominators 2 to 10 are given: + +1/2 = 0.5 +1/3 = 0.(3) +1/4 = 0.25 +1/5 = 0.2 +1/6 = 0.1(6) +1/7 = 0.(142857) +1/8 = 0.125 +1/9 = 0.(1) +1/10 = 0.1 +Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle. It can be +seen that 1/7 has a 6-digit recurring cycle. + +Find the value of d < 1000 for which 1/d contains the longest recurring cycle +in its decimal fraction part. +""" + + +def solution(numerator: int = 1, digit: int = 1000) -> int: + """ + Considering any range can be provided, + because as per the problem, the digit d < 1000 + >>> solution(1, 10) + 7 + >>> solution(10, 100) + 97 + >>> solution(10, 1000) + 983 + """ + the_digit = 1 + longest_list_length = 0 + + for divide_by_number in range(numerator, digit + 1): + has_been_divided = [] + now_divide = numerator + for division_cycle in range(1, digit + 1): + if now_divide in has_been_divided: + if longest_list_length < len(has_been_divided): + longest_list_length = len(has_been_divided) + the_digit = divide_by_number + else: + has_been_divided.append(now_divide) + now_divide = now_divide * 10 % divide_by_number + + return the_digit + + +# Tests +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/project_euler/problem_027/__init__.py b/project_euler/problem_027/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_027/sol1.py b/project_euler/problem_027/sol1.py new file mode 100644 index 000000000000..6f28b925be08 --- /dev/null +++ b/project_euler/problem_027/sol1.py @@ -0,0 +1,74 @@ +""" +Project Euler Problem 27 +https://projecteuler.net/problem=27 + +Problem Statement: + +Euler discovered the remarkable quadratic formula: +n2 + n + 41 +It turns out that the formula will produce 40 primes for the consecutive values +n = 0 to 39. However, when n = 40, 402 + 40 + 41 = 40(40 + 1) + 41 is divisible +by 41, and certainly when n = 41, 412 + 41 + 41 is clearly divisible by 41. +The incredible formula n2 − 79n + 1601 was discovered, which produces 80 primes +for the consecutive values n = 0 to 79. The product of the coefficients, −79 and +1601, is −126479. +Considering quadratics of the form: +n² + an + b, where |a| < 1000 and |b| < 1000 +where |n| is the modulus/absolute value of ne.g. |11| = 11 and |−4| = 4 +Find the product of the coefficients, a and b, for the quadratic expression that +produces the maximum number of primes for consecutive values of n, starting with +n = 0. +""" + +import math + + +def is_prime(k: int) -> bool: + """ + Determine if a number is prime + >>> is_prime(10) + False + >>> is_prime(11) + True + """ + if k < 2 or k % 2 == 0: + return False + elif k == 2: + return True + else: + for x in range(3, int(math.sqrt(k) + 1), 2): + if k % x == 0: + return False + return True + + +def solution(a_limit: int = 1000, b_limit: int = 1000) -> int: + """ + >>> solution(1000, 1000) + -59231 + >>> solution(200, 1000) + -59231 + >>> solution(200, 200) + -4925 + >>> solution(-1000, 1000) + 0 + >>> solution(-1000, -1000) + 0 + """ + longest = [0, 0, 0] # length, a, b + for a in range((a_limit * -1) + 1, a_limit): + for b in range(2, b_limit): + if is_prime(b): + count = 0 + n = 0 + while is_prime((n ** 2) + (a * n) + b): + count += 1 + n += 1 + if count > longest[0]: + longest = [count, a, b] + ans = longest[1] * longest[2] + return ans + + +if __name__ == "__main__": + print(solution(1000, 1000)) diff --git a/project_euler/problem_028/__init__.py b/project_euler/problem_028/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_028/sol1.py b/project_euler/problem_028/sol1.py new file mode 100644 index 000000000000..cbc7de6bea9a --- /dev/null +++ b/project_euler/problem_028/sol1.py @@ -0,0 +1,58 @@ +""" +Problem 28 +Url: https://projecteuler.net/problem=28 +Statement: +Starting with the number 1 and moving to the right in a clockwise direction a 5 +by 5 spiral is formed as follows: + + 21 22 23 24 25 + 20 7 8 9 10 + 19 6 1 2 11 + 18 5 4 3 12 + 17 16 15 14 13 + +It can be verified that the sum of the numbers on the diagonals is 101. + +What is the sum of the numbers on the diagonals in a 1001 by 1001 spiral formed +in the same way? +""" + +from math import ceil + + +def solution(n: int = 1001) -> int: + """Returns the sum of the numbers on the diagonals in a n by n spiral + formed in the same way. + + >>> solution(1001) + 669171001 + >>> solution(500) + 82959497 + >>> solution(100) + 651897 + >>> solution(50) + 79697 + >>> solution(10) + 537 + """ + total = 1 + + for i in range(1, int(ceil(n / 2.0))): + odd = 2 * i + 1 + even = 2 * i + total = total + 4 * odd ** 2 - 6 * even + + return total + + +if __name__ == "__main__": + import sys + + if len(sys.argv) == 1: + print(solution()) + else: + try: + n = int(sys.argv[1]) + print(solution(n)) + except ValueError: + print("Invalid entry - please enter a number") diff --git a/project_euler/problem_029/__init__.py b/project_euler/problem_029/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_029/sol1.py b/project_euler/problem_029/sol1.py new file mode 100644 index 000000000000..726bcaf6ebd8 --- /dev/null +++ b/project_euler/problem_029/sol1.py @@ -0,0 +1,50 @@ +""" +Consider all integer combinations of ab for 2 <= a <= 5 and 2 <= b <= 5: + +2^2=4, 2^3=8, 2^4=16, 2^5=32 +3^2=9, 3^3=27, 3^4=81, 3^5=243 +4^2=16, 4^3=64, 4^4=256, 4^5=1024 +5^2=25, 5^3=125, 5^4=625, 5^5=3125 + +If they are then placed in numerical order, with any repeats removed, we get +the following sequence of 15 distinct terms: + +4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125 + +How many distinct terms are in the sequence generated by ab +for 2 <= a <= 100 and 2 <= b <= 100? +""" + + +def solution(n: int = 100) -> int: + """Returns the number of distinct terms in the sequence generated by a^b + for 2 <= a <= 100 and 2 <= b <= 100. + + >>> solution(100) + 9183 + >>> solution(50) + 2184 + >>> solution(20) + 324 + >>> solution(5) + 15 + >>> solution(2) + 1 + >>> solution(1) + 0 + """ + collectPowers = set() + + currentPow = 0 + + N = n + 1 # maximum limit + + for a in range(2, N): + for b in range(2, N): + currentPow = a ** b # calculates the current power + collectPowers.add(currentPow) # adds the result to the set + return len(collectPowers) + + +if __name__ == "__main__": + print("Number of terms ", solution(int(str(input()).strip()))) diff --git a/project_euler/problem_030/__init__.py b/project_euler/problem_030/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_030/sol1.py b/project_euler/problem_030/sol1.py new file mode 100644 index 000000000000..c9f2d71965e3 --- /dev/null +++ b/project_euler/problem_030/sol1.py @@ -0,0 +1,39 @@ +""" Problem Statement (Digit Fifth Power ): https://projecteuler.net/problem=30 + +Surprisingly there are only three numbers that can be written as the sum of fourth +powers of their digits: + +1634 = 1^4 + 6^4 + 3^4 + 4^4 +8208 = 8^4 + 2^4 + 0^4 + 8^4 +9474 = 9^4 + 4^4 + 7^4 + 4^4 +As 1 = 1^4 is not a sum it is not included. + +The sum of these numbers is 1634 + 8208 + 9474 = 19316. + +Find the sum of all the numbers that can be written as the sum of fifth powers of their +digits. + +(9^5)=59,049‬ +59049*7=4,13,343 (which is only 6 digit number ) +So, number greater than 9,99,999 are rejected +and also 59049*3=1,77,147 (which exceeds the criteria of number being 3 digit) +So, n>999 +and hence a bound between (1000,1000000) +""" + + +def digitsum(s: str) -> int: + """ + >>> all(digitsum(str(i)) == (1 if i == 1 else 0) for i in range(100)) + True + """ + i = sum(pow(int(c), 5) for c in s) + return i if i == int(s) else 0 + + +def solution() -> int: + return sum(digitsum(str(i)) for i in range(1000, 1000000)) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_031/__init__.py b/project_euler/problem_031/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_031/sol1.py b/project_euler/problem_031/sol1.py new file mode 100644 index 000000000000..ba40cf383175 --- /dev/null +++ b/project_euler/problem_031/sol1.py @@ -0,0 +1,65 @@ +""" +Coin sums +Problem 31: https://projecteuler.net/problem=31 + +In England the currency is made up of pound, £, and pence, p, and there are +eight coins in general circulation: + +1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). +It is possible to make £2 in the following way: + +1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p +How many different ways can £2 be made using any number of coins? +""" + + +def one_pence() -> int: + return 1 + + +def two_pence(x: int) -> int: + return 0 if x < 0 else two_pence(x - 2) + one_pence() + + +def five_pence(x: int) -> int: + return 0 if x < 0 else five_pence(x - 5) + two_pence(x) + + +def ten_pence(x: int) -> int: + return 0 if x < 0 else ten_pence(x - 10) + five_pence(x) + + +def twenty_pence(x: int) -> int: + return 0 if x < 0 else twenty_pence(x - 20) + ten_pence(x) + + +def fifty_pence(x: int) -> int: + return 0 if x < 0 else fifty_pence(x - 50) + twenty_pence(x) + + +def one_pound(x: int) -> int: + return 0 if x < 0 else one_pound(x - 100) + fifty_pence(x) + + +def two_pound(x: int) -> int: + return 0 if x < 0 else two_pound(x - 200) + one_pound(x) + + +def solution(n: int = 200) -> int: + """Returns the number of different ways can n pence be made using any number of + coins? + + >>> solution(500) + 6295434 + >>> solution(200) + 73682 + >>> solution(50) + 451 + >>> solution(10) + 11 + """ + return two_pound(n) + + +if __name__ == "__main__": + print(solution(int(input().strip()))) diff --git a/project_euler/problem_031/sol2.py b/project_euler/problem_031/sol2.py new file mode 100644 index 000000000000..f9e4dc384bff --- /dev/null +++ b/project_euler/problem_031/sol2.py @@ -0,0 +1,59 @@ +""" +Problem 31: https://projecteuler.net/problem=31 + +Coin sums + +In England the currency is made up of pound, £, and pence, p, and there are +eight coins in general circulation: + +1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p). +It is possible to make £2 in the following way: + +1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p +How many different ways can £2 be made using any number of coins? + +Hint: + > There are 100 pence in a pound (£1 = 100p) + > There are coins(in pence) are available: 1, 2, 5, 10, 20, 50, 100 and 200. + > how many different ways you can combine these values to create 200 pence. + +Example: + to make 6p there are 5 ways + 1,1,1,1,1,1 + 1,1,1,1,2 + 1,1,2,2 + 2,2,2 + 1,5 + to make 5p there are 4 ways + 1,1,1,1,1 + 1,1,1,2 + 1,2,2 + 5 +""" + + +def solution(pence: int = 200) -> int: + """Returns the number of different ways to make X pence using any number of coins. + The solution is based on dynamic programming paradigm in a bottom-up fashion. + + >>> solution(500) + 6295434 + >>> solution(200) + 73682 + >>> solution(50) + 451 + >>> solution(10) + 11 + """ + coins = [1, 2, 5, 10, 20, 50, 100, 200] + number_of_ways = [0] * (pence + 1) + number_of_ways[0] = 1 # base case: 1 way to make 0 pence + + for coin in coins: + for i in range(coin, pence + 1, 1): + number_of_ways[i] += number_of_ways[i - coin] + return number_of_ways[pence] + + +if __name__ == "__main__": + assert solution(200) == 73682 diff --git a/project_euler/problem_032/__init__.py b/project_euler/problem_032/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_032/sol32.py b/project_euler/problem_032/sol32.py new file mode 100644 index 000000000000..393218339e9f --- /dev/null +++ b/project_euler/problem_032/sol32.py @@ -0,0 +1,58 @@ +""" +We shall say that an n-digit number is pandigital if it makes use of all the +digits 1 to n exactly once; for example, the 5-digit number, 15234, is 1 through +5 pandigital. + +The product 7254 is unusual, as the identity, 39 × 186 = 7254, containing +multiplicand, multiplier, and product is 1 through 9 pandigital. + +Find the sum of all products whose multiplicand/multiplier/product identity can +be written as a 1 through 9 pandigital. + +HINT: Some products can be obtained in more than one way so be sure to only +include it once in your sum. +""" +import itertools + + +def isCombinationValid(combination): + """ + Checks if a combination (a tuple of 9 digits) + is a valid product equation. + + >>> isCombinationValid(('3', '9', '1', '8', '6', '7', '2', '5', '4')) + True + + >>> isCombinationValid(('1', '2', '3', '4', '5', '6', '7', '8', '9')) + False + + """ + return ( + int("".join(combination[0:2])) * int("".join(combination[2:5])) + == int("".join(combination[5:9])) + ) or ( + int("".join(combination[0])) * int("".join(combination[1:5])) + == int("".join(combination[5:9])) + ) + + +def solution(): + """ + Finds the sum of all products whose multiplicand/multiplier/product identity + can be written as a 1 through 9 pandigital + + >>> solution() + 45228 + """ + + return sum( + { + int("".join(pandigital[5:9])) + for pandigital in itertools.permutations("123456789") + if isCombinationValid(pandigital) + } + ) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_033/__init__.py b/project_euler/problem_033/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_033/sol1.py b/project_euler/problem_033/sol1.py new file mode 100644 index 000000000000..ba6e553d8689 --- /dev/null +++ b/project_euler/problem_033/sol1.py @@ -0,0 +1,68 @@ +""" +Problem 33: https://projecteuler.net/problem=33 + +The fraction 49/98 is a curious fraction, as an inexperienced +mathematician in attempting to simplify it may incorrectly believe +that 49/98 = 4/8, which is correct, is obtained by cancelling the 9s. + +We shall consider fractions like, 30/50 = 3/5, to be trivial examples. + +There are exactly four non-trivial examples of this type of fraction, +less than one in value, and containing two digits in the numerator +and denominator. + +If the product of these four fractions is given in its lowest common +terms, find the value of the denominator. +""" +from fractions import Fraction +from typing import List + + +def is_digit_cancelling(num: int, den: int) -> bool: + if num != den: + if num % 10 == den // 10: + if (num // 10) / (den % 10) == num / den: + return True + return False + + +def fraction_list(digit_len: int) -> List[str]: + """ + >>> fraction_list(2) + ['16/64', '19/95', '26/65', '49/98'] + >>> fraction_list(3) + ['16/64', '19/95', '26/65', '49/98'] + >>> fraction_list(4) + ['16/64', '19/95', '26/65', '49/98'] + >>> fraction_list(0) + [] + >>> fraction_list(5) + ['16/64', '19/95', '26/65', '49/98'] + """ + solutions = [] + den = 11 + last_digit = int("1" + "0" * digit_len) + for num in range(den, last_digit): + while den <= 99: + if (num != den) and (num % 10 == den // 10) and (den % 10 != 0): + if is_digit_cancelling(num, den): + solutions.append(f"{num}/{den}") + den += 1 + num += 1 + den = 10 + return solutions + + +def solution(n: int = 2) -> int: + """ + Return the solution to the problem + """ + result = 1.0 + for fraction in fraction_list(n): + frac = Fraction(fraction) + result *= frac.denominator / frac.numerator + return int(result) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_034/__init__.py b/project_euler/problem_034/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_034/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_034/sol1.py b/project_euler/problem_034/sol1.py new file mode 100644 index 000000000000..2466fa346798 --- /dev/null +++ b/project_euler/problem_034/sol1.py @@ -0,0 +1,36 @@ +""" +Problem 34: https://projecteuler.net/problem=34 + +145 is a curious number, as 1! + 4! + 5! = 1 + 24 + 120 = 145. +Find the sum of all numbers which are equal to the sum of the factorial of their digits. +Note: As 1! = 1 and 2! = 2 are not sums they are not included. +""" + +from math import factorial + + +def sum_of_digit_factorial(n: int) -> int: + """ + Returns the sum of the digits in n + >>> sum_of_digit_factorial(15) + 121 + >>> sum_of_digit_factorial(0) + 1 + """ + return sum(factorial(int(char)) for char in str(n)) + + +def solution() -> int: + """ + Returns the sum of all numbers whose + sum of the factorials of all digits + add up to the number itself. + >>> solution() + 40730 + """ + limit = 7 * factorial(9) + 1 + return sum(i for i in range(3, limit) if sum_of_digit_factorial(i) == i) + + +if __name__ == "__main__": + print(f"{solution()} = ") diff --git a/project_euler/problem_035/__init__.py b/project_euler/problem_035/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_035/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_035/sol1.py b/project_euler/problem_035/sol1.py new file mode 100644 index 000000000000..fe69a9dc8f3c --- /dev/null +++ b/project_euler/problem_035/sol1.py @@ -0,0 +1,82 @@ +""" +Project Euler Problem 35 +https://projecteuler.net/problem=35 + +Problem Statement: + +The number 197 is called a circular prime because all rotations of the digits: +197, 971, and 719, are themselves prime. +There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, +79, and 97. +How many circular primes are there below one million? + +To solve this problem in an efficient manner, we will first mark all the primes +below 1 million using the Seive of Eratosthenes. Then, out of all these primes, +we will rule out the numbers which contain an even digit. After this we will +generate each circular combination of the number and check if all are prime. +""" +from __future__ import annotations + +seive = [True] * 1000001 +i = 2 +while i * i <= 1000000: + if seive[i]: + for j in range(i * i, 1000001, i): + seive[j] = False + i += 1 + + +def is_prime(n: int) -> bool: + """ + For 2 <= n <= 1000000, return True if n is prime. + >>> is_prime(87) + False + >>> is_prime(23) + True + >>> is_prime(25363) + False + """ + return seive[n] + + +def contains_an_even_digit(n: int) -> bool: + """ + Return True if n contains an even digit. + >>> contains_an_even_digit(0) + True + >>> contains_an_even_digit(975317933) + False + >>> contains_an_even_digit(-245679) + True + """ + return any(digit in "02468" for digit in str(n)) + + +def find_circular_primes(limit: int = 1000000) -> list[int]: + """ + Return circular primes below limit. + >>> len(find_circular_primes(100)) + 13 + >>> len(find_circular_primes(1000000)) + 55 + """ + result = [2] # result already includes the number 2. + for num in range(3, limit + 1, 2): + if is_prime(num) and not contains_an_even_digit(num): + str_num = str(num) + list_nums = [int(str_num[j:] + str_num[:j]) for j in range(len(str_num))] + if all(is_prime(i) for i in list_nums): + result.append(num) + return result + + +def solution() -> int: + """ + >>> solution() + 55 + """ + return len(find_circular_primes()) + + +if __name__ == "__main__": + print(f"{len(find_circular_primes()) = }") diff --git a/project_euler/problem_036/__init__.py b/project_euler/problem_036/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_036/sol1.py b/project_euler/problem_036/sol1.py new file mode 100644 index 000000000000..13a749862e5f --- /dev/null +++ b/project_euler/problem_036/sol1.py @@ -0,0 +1,69 @@ +""" +Project Euler Problem 36 +https://projecteuler.net/problem=36 + +Problem Statement: + +Double-base palindromes +Problem 36 +The decimal number, 585 = 10010010012 (binary), is palindromic in both bases. + +Find the sum of all numbers, less than one million, which are palindromic in +base 10 and base 2. + +(Please note that the palindromic number, in either base, may not include +leading zeros.) +""" + +from typing import Union + + +def is_palindrome(n: Union[int, str]) -> bool: + """ + Return true if the input n is a palindrome. + Otherwise return false. n can be an integer or a string. + + >>> is_palindrome(909) + True + >>> is_palindrome(908) + False + >>> is_palindrome('10101') + True + >>> is_palindrome('10111') + False + """ + n = str(n) + return True if n == n[::-1] else False + + +def solution(n: int = 1000000): + """Return the sum of all numbers, less than n , which are palindromic in + base 10 and base 2. + + >>> solution(1000000) + 872187 + >>> solution(500000) + 286602 + >>> solution(100000) + 286602 + >>> solution(1000) + 1772 + >>> solution(100) + 157 + >>> solution(10) + 25 + >>> solution(2) + 1 + >>> solution(1) + 0 + """ + total = 0 + + for i in range(1, n): + if is_palindrome(i) and is_palindrome(bin(i).split("b")[1]): + total += i + return total + + +if __name__ == "__main__": + print(solution(int(str(input().strip())))) diff --git a/project_euler/problem_037/__init__.py b/project_euler/problem_037/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_037/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_037/sol1.py b/project_euler/problem_037/sol1.py new file mode 100644 index 000000000000..c36b5324c79b --- /dev/null +++ b/project_euler/problem_037/sol1.py @@ -0,0 +1,98 @@ +""" +The number 3797 has an interesting property. Being prime itself, it is possible +to continuously remove digits from left to right, and remain prime at each stage: +3797, 797, 97, and 7. Similarly we can work from right to left: 3797, 379, 37, and 3. + +Find the sum of the only eleven primes that are both truncatable from left to right +and right to left. + +NOTE: 2, 3, 5, and 7 are not considered to be truncatable primes. +""" + +from __future__ import annotations + +seive = [True] * 1000001 +seive[1] = False +i = 2 +while i * i <= 1000000: + if seive[i]: + for j in range(i * i, 1000001, i): + seive[j] = False + i += 1 + + +def is_prime(n: int) -> bool: + """ + Returns True if n is prime, + False otherwise, for 1 <= n <= 1000000 + >>> is_prime(87) + False + >>> is_prime(1) + False + >>> is_prime(25363) + False + """ + return seive[n] + + +def list_truncated_nums(n: int) -> list[int]: + """ + Returns a list of all left and right truncated numbers of n + >>> list_truncated_nums(927628) + [927628, 27628, 92762, 7628, 9276, 628, 927, 28, 92, 8, 9] + >>> list_truncated_nums(467) + [467, 67, 46, 7, 4] + >>> list_truncated_nums(58) + [58, 8, 5] + """ + str_num = str(n) + list_nums = [n] + for i in range(1, len(str_num)): + list_nums.append(int(str_num[i:])) + list_nums.append(int(str_num[:-i])) + return list_nums + + +def validate(n: int) -> bool: + """ + To optimize the approach, we will rule out the numbers above 1000, + whose first or last three digits are not prime + >>> validate(74679) + False + >>> validate(235693) + False + >>> validate(3797) + True + """ + if len(str(n)) > 3: + if not is_prime(int(str(n)[-3:])) or not is_prime(int(str(n)[:3])): + return False + return True + + +def compute_truncated_primes(count: int = 11) -> list[int]: + """ + Returns the list of truncated primes + >>> compute_truncated_primes(11) + [23, 37, 53, 73, 313, 317, 373, 797, 3137, 3797, 739397] + """ + list_truncated_primes = [] + num = 13 + while len(list_truncated_primes) != count: + if validate(num): + list_nums = list_truncated_nums(num) + if all(is_prime(i) for i in list_nums): + list_truncated_primes.append(num) + num += 2 + return list_truncated_primes + + +def solution() -> int: + """ + Returns the sum of truncated primes + """ + return sum(compute_truncated_primes(11)) + + +if __name__ == "__main__": + print(f"{sum(compute_truncated_primes(11)) = }") 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() = }") diff --git a/project_euler/problem_039/__init__.py b/project_euler/problem_039/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_039/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_039/sol1.py b/project_euler/problem_039/sol1.py new file mode 100644 index 000000000000..4800833d177e --- /dev/null +++ b/project_euler/problem_039/sol1.py @@ -0,0 +1,55 @@ +""" +Problem 39: https://projecteuler.net/problem=39 + +If p is the perimeter of a right angle triangle with integral length sides, +{a,b,c}, there are exactly three solutions for p = 120. +{20,48,52}, {24,45,51}, {30,40,50} + +For which value of p ≤ 1000, is the number of solutions maximised? +""" + +from __future__ import annotations + +import typing +from collections import Counter + + +def pythagorean_triple(max_perimeter: int) -> typing.Counter[int]: + """ + Returns a dictionary with keys as the perimeter of a right angled triangle + and value as the number of corresponding triplets. + >>> pythagorean_triple(15) + Counter({12: 1}) + >>> pythagorean_triple(40) + Counter({12: 1, 30: 1, 24: 1, 40: 1, 36: 1}) + >>> pythagorean_triple(50) + Counter({12: 1, 30: 1, 24: 1, 40: 1, 36: 1, 48: 1}) + """ + triplets: typing.Counter[int] = Counter() + for base in range(1, max_perimeter + 1): + for perpendicular in range(base, max_perimeter + 1): + hypotenuse = (base * base + perpendicular * perpendicular) ** 0.5 + if hypotenuse == int(hypotenuse): + perimeter = int(base + perpendicular + hypotenuse) + if perimeter > max_perimeter: + continue + triplets[perimeter] += 1 + return triplets + + +def solution(n: int = 1000) -> int: + """ + Returns perimeter with maximum solutions. + >>> solution(100) + 90 + >>> solution(200) + 180 + >>> solution(1000) + 840 + """ + triplets = pythagorean_triple(n) + return triplets.most_common(1)[0][0] + + +if __name__ == "__main__": + print(f"Perimeter {solution()} has maximum solutions") diff --git a/project_euler/problem_040/__init__.py b/project_euler/problem_040/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_040/sol1.py b/project_euler/problem_040/sol1.py new file mode 100644 index 000000000000..69be377723a5 --- /dev/null +++ b/project_euler/problem_040/sol1.py @@ -0,0 +1,45 @@ +""" +Champernowne's constant +Problem 40 +An irrational decimal fraction is created by concatenating the positive +integers: + +0.123456789101112131415161718192021... + +It can be seen that the 12th digit of the fractional part is 1. + +If dn represents the nth digit of the fractional part, find the value of the +following expression. + +d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000 +""" + + +def solution(): + """Returns + + >>> solution() + 210 + """ + constant = [] + i = 1 + + while len(constant) < 1e6: + constant.append(str(i)) + i += 1 + + constant = "".join(constant) + + return ( + int(constant[0]) + * int(constant[9]) + * int(constant[99]) + * int(constant[999]) + * int(constant[9999]) + * int(constant[99999]) + * int(constant[999999]) + ) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_041/__init__.py b/project_euler/problem_041/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_041/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_041/sol1.py b/project_euler/problem_041/sol1.py new file mode 100644 index 000000000000..c5f2bee4d401 --- /dev/null +++ b/project_euler/problem_041/sol1.py @@ -0,0 +1,56 @@ +""" +Pandigital prime +Problem 41: https://projecteuler.net/problem=41 + +We shall say that an n-digit number is pandigital if it makes use of all the digits +1 to n exactly once. For example, 2143 is a 4-digit pandigital and is also prime. +What is the largest n-digit pandigital prime that exists? + +All pandigital numbers except for 1, 4 ,7 pandigital numbers are divisible by 3. +So we will check only 7 digit pandigital numbers to obtain the largest possible +pandigital prime. +""" +from __future__ import annotations + +from itertools import permutations +from math import sqrt + + +def is_prime(n: int) -> bool: + """ + Returns True if n is prime, + False otherwise. + >>> is_prime(67483) + False + >>> is_prime(563) + True + >>> is_prime(87) + False + """ + if n % 2 == 0: + return False + for i in range(3, int(sqrt(n) + 1), 2): + if n % i == 0: + return False + return True + + +def solution(n: int = 7) -> int: + """ + Returns the maximum pandigital prime number of length n. + If there are none, then it will return 0. + >>> solution(2) + 0 + >>> solution(4) + 4231 + >>> solution(7) + 7652413 + """ + pandigital_str = "".join(str(i) for i in range(1, n + 1)) + perm_list = [int("".join(i)) for i in permutations(pandigital_str, n)] + pandigitals = [num for num in perm_list if is_prime(num)] + return max(pandigitals) if pandigitals else 0 + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_042/__init__.py b/project_euler/problem_042/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_042/solution42.py b/project_euler/problem_042/solution42.py new file mode 100644 index 000000000000..b3aecf4cf144 --- /dev/null +++ b/project_euler/problem_042/solution42.py @@ -0,0 +1,47 @@ +""" +The nth term of the sequence of triangle numbers is given by, tn = ½n(n+1); so +the first ten triangle numbers are: + +1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ... + +By converting each letter in a word to a number corresponding to its +alphabetical position and adding these values we form a word value. For example, +the word value for SKY is 19 + 11 + 25 = 55 = t10. If the word value is a +triangle number then we shall call the word a triangle word. + +Using words.txt (right click and 'Save Link/Target As...'), a 16K text file +containing nearly two-thousand common English words, how many are triangle +words? +""" +import os + +# Precomputes a list of the 100 first triangular numbers +TRIANGULAR_NUMBERS = [int(0.5 * n * (n + 1)) for n in range(1, 101)] + + +def solution(): + """ + Finds the amount of triangular words in the words file. + + >>> solution() + 162 + """ + script_dir = os.path.dirname(os.path.realpath(__file__)) + wordsFilePath = os.path.join(script_dir, "words.txt") + + words = "" + with open(wordsFilePath) as f: + words = f.readline() + + words = list(map(lambda word: word.strip('"'), words.strip("\r\n").split(","))) + words = list( + filter( + lambda word: word in TRIANGULAR_NUMBERS, + map(lambda word: sum(map(lambda x: ord(x) - 64, word)), words), + ) + ) + return len(words) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_042/words.txt b/project_euler/problem_042/words.txt new file mode 100644 index 000000000000..af3aeb42f151 --- /dev/null +++ b/project_euler/problem_042/words.txt @@ -0,0 +1 @@ +"A","ABILITY","ABLE","ABOUT","ABOVE","ABSENCE","ABSOLUTELY","ACADEMIC","ACCEPT","ACCESS","ACCIDENT","ACCOMPANY","ACCORDING","ACCOUNT","ACHIEVE","ACHIEVEMENT","ACID","ACQUIRE","ACROSS","ACT","ACTION","ACTIVE","ACTIVITY","ACTUAL","ACTUALLY","ADD","ADDITION","ADDITIONAL","ADDRESS","ADMINISTRATION","ADMIT","ADOPT","ADULT","ADVANCE","ADVANTAGE","ADVICE","ADVISE","AFFAIR","AFFECT","AFFORD","AFRAID","AFTER","AFTERNOON","AFTERWARDS","AGAIN","AGAINST","AGE","AGENCY","AGENT","AGO","AGREE","AGREEMENT","AHEAD","AID","AIM","AIR","AIRCRAFT","ALL","ALLOW","ALMOST","ALONE","ALONG","ALREADY","ALRIGHT","ALSO","ALTERNATIVE","ALTHOUGH","ALWAYS","AMONG","AMONGST","AMOUNT","AN","ANALYSIS","ANCIENT","AND","ANIMAL","ANNOUNCE","ANNUAL","ANOTHER","ANSWER","ANY","ANYBODY","ANYONE","ANYTHING","ANYWAY","APART","APPARENT","APPARENTLY","APPEAL","APPEAR","APPEARANCE","APPLICATION","APPLY","APPOINT","APPOINTMENT","APPROACH","APPROPRIATE","APPROVE","AREA","ARGUE","ARGUMENT","ARISE","ARM","ARMY","AROUND","ARRANGE","ARRANGEMENT","ARRIVE","ART","ARTICLE","ARTIST","AS","ASK","ASPECT","ASSEMBLY","ASSESS","ASSESSMENT","ASSET","ASSOCIATE","ASSOCIATION","ASSUME","ASSUMPTION","AT","ATMOSPHERE","ATTACH","ATTACK","ATTEMPT","ATTEND","ATTENTION","ATTITUDE","ATTRACT","ATTRACTIVE","AUDIENCE","AUTHOR","AUTHORITY","AVAILABLE","AVERAGE","AVOID","AWARD","AWARE","AWAY","AYE","BABY","BACK","BACKGROUND","BAD","BAG","BALANCE","BALL","BAND","BANK","BAR","BASE","BASIC","BASIS","BATTLE","BE","BEAR","BEAT","BEAUTIFUL","BECAUSE","BECOME","BED","BEDROOM","BEFORE","BEGIN","BEGINNING","BEHAVIOUR","BEHIND","BELIEF","BELIEVE","BELONG","BELOW","BENEATH","BENEFIT","BESIDE","BEST","BETTER","BETWEEN","BEYOND","BIG","BILL","BIND","BIRD","BIRTH","BIT","BLACK","BLOCK","BLOOD","BLOODY","BLOW","BLUE","BOARD","BOAT","BODY","BONE","BOOK","BORDER","BOTH","BOTTLE","BOTTOM","BOX","BOY","BRAIN","BRANCH","BREAK","BREATH","BRIDGE","BRIEF","BRIGHT","BRING","BROAD","BROTHER","BUDGET","BUILD","BUILDING","BURN","BUS","BUSINESS","BUSY","BUT","BUY","BY","CABINET","CALL","CAMPAIGN","CAN","CANDIDATE","CAPABLE","CAPACITY","CAPITAL","CAR","CARD","CARE","CAREER","CAREFUL","CAREFULLY","CARRY","CASE","CASH","CAT","CATCH","CATEGORY","CAUSE","CELL","CENTRAL","CENTRE","CENTURY","CERTAIN","CERTAINLY","CHAIN","CHAIR","CHAIRMAN","CHALLENGE","CHANCE","CHANGE","CHANNEL","CHAPTER","CHARACTER","CHARACTERISTIC","CHARGE","CHEAP","CHECK","CHEMICAL","CHIEF","CHILD","CHOICE","CHOOSE","CHURCH","CIRCLE","CIRCUMSTANCE","CITIZEN","CITY","CIVIL","CLAIM","CLASS","CLEAN","CLEAR","CLEARLY","CLIENT","CLIMB","CLOSE","CLOSELY","CLOTHES","CLUB","COAL","CODE","COFFEE","COLD","COLLEAGUE","COLLECT","COLLECTION","COLLEGE","COLOUR","COMBINATION","COMBINE","COME","COMMENT","COMMERCIAL","COMMISSION","COMMIT","COMMITMENT","COMMITTEE","COMMON","COMMUNICATION","COMMUNITY","COMPANY","COMPARE","COMPARISON","COMPETITION","COMPLETE","COMPLETELY","COMPLEX","COMPONENT","COMPUTER","CONCENTRATE","CONCENTRATION","CONCEPT","CONCERN","CONCERNED","CONCLUDE","CONCLUSION","CONDITION","CONDUCT","CONFERENCE","CONFIDENCE","CONFIRM","CONFLICT","CONGRESS","CONNECT","CONNECTION","CONSEQUENCE","CONSERVATIVE","CONSIDER","CONSIDERABLE","CONSIDERATION","CONSIST","CONSTANT","CONSTRUCTION","CONSUMER","CONTACT","CONTAIN","CONTENT","CONTEXT","CONTINUE","CONTRACT","CONTRAST","CONTRIBUTE","CONTRIBUTION","CONTROL","CONVENTION","CONVERSATION","COPY","CORNER","CORPORATE","CORRECT","COS","COST","COULD","COUNCIL","COUNT","COUNTRY","COUNTY","COUPLE","COURSE","COURT","COVER","CREATE","CREATION","CREDIT","CRIME","CRIMINAL","CRISIS","CRITERION","CRITICAL","CRITICISM","CROSS","CROWD","CRY","CULTURAL","CULTURE","CUP","CURRENT","CURRENTLY","CURRICULUM","CUSTOMER","CUT","DAMAGE","DANGER","DANGEROUS","DARK","DATA","DATE","DAUGHTER","DAY","DEAD","DEAL","DEATH","DEBATE","DEBT","DECADE","DECIDE","DECISION","DECLARE","DEEP","DEFENCE","DEFENDANT","DEFINE","DEFINITION","DEGREE","DELIVER","DEMAND","DEMOCRATIC","DEMONSTRATE","DENY","DEPARTMENT","DEPEND","DEPUTY","DERIVE","DESCRIBE","DESCRIPTION","DESIGN","DESIRE","DESK","DESPITE","DESTROY","DETAIL","DETAILED","DETERMINE","DEVELOP","DEVELOPMENT","DEVICE","DIE","DIFFERENCE","DIFFERENT","DIFFICULT","DIFFICULTY","DINNER","DIRECT","DIRECTION","DIRECTLY","DIRECTOR","DISAPPEAR","DISCIPLINE","DISCOVER","DISCUSS","DISCUSSION","DISEASE","DISPLAY","DISTANCE","DISTINCTION","DISTRIBUTION","DISTRICT","DIVIDE","DIVISION","DO","DOCTOR","DOCUMENT","DOG","DOMESTIC","DOOR","DOUBLE","DOUBT","DOWN","DRAW","DRAWING","DREAM","DRESS","DRINK","DRIVE","DRIVER","DROP","DRUG","DRY","DUE","DURING","DUTY","EACH","EAR","EARLY","EARN","EARTH","EASILY","EAST","EASY","EAT","ECONOMIC","ECONOMY","EDGE","EDITOR","EDUCATION","EDUCATIONAL","EFFECT","EFFECTIVE","EFFECTIVELY","EFFORT","EGG","EITHER","ELDERLY","ELECTION","ELEMENT","ELSE","ELSEWHERE","EMERGE","EMPHASIS","EMPLOY","EMPLOYEE","EMPLOYER","EMPLOYMENT","EMPTY","ENABLE","ENCOURAGE","END","ENEMY","ENERGY","ENGINE","ENGINEERING","ENJOY","ENOUGH","ENSURE","ENTER","ENTERPRISE","ENTIRE","ENTIRELY","ENTITLE","ENTRY","ENVIRONMENT","ENVIRONMENTAL","EQUAL","EQUALLY","EQUIPMENT","ERROR","ESCAPE","ESPECIALLY","ESSENTIAL","ESTABLISH","ESTABLISHMENT","ESTATE","ESTIMATE","EVEN","EVENING","EVENT","EVENTUALLY","EVER","EVERY","EVERYBODY","EVERYONE","EVERYTHING","EVIDENCE","EXACTLY","EXAMINATION","EXAMINE","EXAMPLE","EXCELLENT","EXCEPT","EXCHANGE","EXECUTIVE","EXERCISE","EXHIBITION","EXIST","EXISTENCE","EXISTING","EXPECT","EXPECTATION","EXPENDITURE","EXPENSE","EXPENSIVE","EXPERIENCE","EXPERIMENT","EXPERT","EXPLAIN","EXPLANATION","EXPLORE","EXPRESS","EXPRESSION","EXTEND","EXTENT","EXTERNAL","EXTRA","EXTREMELY","EYE","FACE","FACILITY","FACT","FACTOR","FACTORY","FAIL","FAILURE","FAIR","FAIRLY","FAITH","FALL","FAMILIAR","FAMILY","FAMOUS","FAR","FARM","FARMER","FASHION","FAST","FATHER","FAVOUR","FEAR","FEATURE","FEE","FEEL","FEELING","FEMALE","FEW","FIELD","FIGHT","FIGURE","FILE","FILL","FILM","FINAL","FINALLY","FINANCE","FINANCIAL","FIND","FINDING","FINE","FINGER","FINISH","FIRE","FIRM","FIRST","FISH","FIT","FIX","FLAT","FLIGHT","FLOOR","FLOW","FLOWER","FLY","FOCUS","FOLLOW","FOLLOWING","FOOD","FOOT","FOOTBALL","FOR","FORCE","FOREIGN","FOREST","FORGET","FORM","FORMAL","FORMER","FORWARD","FOUNDATION","FREE","FREEDOM","FREQUENTLY","FRESH","FRIEND","FROM","FRONT","FRUIT","FUEL","FULL","FULLY","FUNCTION","FUND","FUNNY","FURTHER","FUTURE","GAIN","GAME","GARDEN","GAS","GATE","GATHER","GENERAL","GENERALLY","GENERATE","GENERATION","GENTLEMAN","GET","GIRL","GIVE","GLASS","GO","GOAL","GOD","GOLD","GOOD","GOVERNMENT","GRANT","GREAT","GREEN","GREY","GROUND","GROUP","GROW","GROWING","GROWTH","GUEST","GUIDE","GUN","HAIR","HALF","HALL","HAND","HANDLE","HANG","HAPPEN","HAPPY","HARD","HARDLY","HATE","HAVE","HE","HEAD","HEALTH","HEAR","HEART","HEAT","HEAVY","HELL","HELP","HENCE","HER","HERE","HERSELF","HIDE","HIGH","HIGHLY","HILL","HIM","HIMSELF","HIS","HISTORICAL","HISTORY","HIT","HOLD","HOLE","HOLIDAY","HOME","HOPE","HORSE","HOSPITAL","HOT","HOTEL","HOUR","HOUSE","HOUSEHOLD","HOUSING","HOW","HOWEVER","HUGE","HUMAN","HURT","HUSBAND","I","IDEA","IDENTIFY","IF","IGNORE","ILLUSTRATE","IMAGE","IMAGINE","IMMEDIATE","IMMEDIATELY","IMPACT","IMPLICATION","IMPLY","IMPORTANCE","IMPORTANT","IMPOSE","IMPOSSIBLE","IMPRESSION","IMPROVE","IMPROVEMENT","IN","INCIDENT","INCLUDE","INCLUDING","INCOME","INCREASE","INCREASED","INCREASINGLY","INDEED","INDEPENDENT","INDEX","INDICATE","INDIVIDUAL","INDUSTRIAL","INDUSTRY","INFLUENCE","INFORM","INFORMATION","INITIAL","INITIATIVE","INJURY","INSIDE","INSIST","INSTANCE","INSTEAD","INSTITUTE","INSTITUTION","INSTRUCTION","INSTRUMENT","INSURANCE","INTEND","INTENTION","INTEREST","INTERESTED","INTERESTING","INTERNAL","INTERNATIONAL","INTERPRETATION","INTERVIEW","INTO","INTRODUCE","INTRODUCTION","INVESTIGATE","INVESTIGATION","INVESTMENT","INVITE","INVOLVE","IRON","IS","ISLAND","ISSUE","IT","ITEM","ITS","ITSELF","JOB","JOIN","JOINT","JOURNEY","JUDGE","JUMP","JUST","JUSTICE","KEEP","KEY","KID","KILL","KIND","KING","KITCHEN","KNEE","KNOW","KNOWLEDGE","LABOUR","LACK","LADY","LAND","LANGUAGE","LARGE","LARGELY","LAST","LATE","LATER","LATTER","LAUGH","LAUNCH","LAW","LAWYER","LAY","LEAD","LEADER","LEADERSHIP","LEADING","LEAF","LEAGUE","LEAN","LEARN","LEAST","LEAVE","LEFT","LEG","LEGAL","LEGISLATION","LENGTH","LESS","LET","LETTER","LEVEL","LIABILITY","LIBERAL","LIBRARY","LIE","LIFE","LIFT","LIGHT","LIKE","LIKELY","LIMIT","LIMITED","LINE","LINK","LIP","LIST","LISTEN","LITERATURE","LITTLE","LIVE","LIVING","LOAN","LOCAL","LOCATION","LONG","LOOK","LORD","LOSE","LOSS","LOT","LOVE","LOVELY","LOW","LUNCH","MACHINE","MAGAZINE","MAIN","MAINLY","MAINTAIN","MAJOR","MAJORITY","MAKE","MALE","MAN","MANAGE","MANAGEMENT","MANAGER","MANNER","MANY","MAP","MARK","MARKET","MARRIAGE","MARRIED","MARRY","MASS","MASTER","MATCH","MATERIAL","MATTER","MAY","MAYBE","ME","MEAL","MEAN","MEANING","MEANS","MEANWHILE","MEASURE","MECHANISM","MEDIA","MEDICAL","MEET","MEETING","MEMBER","MEMBERSHIP","MEMORY","MENTAL","MENTION","MERELY","MESSAGE","METAL","METHOD","MIDDLE","MIGHT","MILE","MILITARY","MILK","MIND","MINE","MINISTER","MINISTRY","MINUTE","MISS","MISTAKE","MODEL","MODERN","MODULE","MOMENT","MONEY","MONTH","MORE","MORNING","MOST","MOTHER","MOTION","MOTOR","MOUNTAIN","MOUTH","MOVE","MOVEMENT","MUCH","MURDER","MUSEUM","MUSIC","MUST","MY","MYSELF","NAME","NARROW","NATION","NATIONAL","NATURAL","NATURE","NEAR","NEARLY","NECESSARILY","NECESSARY","NECK","NEED","NEGOTIATION","NEIGHBOUR","NEITHER","NETWORK","NEVER","NEVERTHELESS","NEW","NEWS","NEWSPAPER","NEXT","NICE","NIGHT","NO","NOBODY","NOD","NOISE","NONE","NOR","NORMAL","NORMALLY","NORTH","NORTHERN","NOSE","NOT","NOTE","NOTHING","NOTICE","NOTION","NOW","NUCLEAR","NUMBER","NURSE","OBJECT","OBJECTIVE","OBSERVATION","OBSERVE","OBTAIN","OBVIOUS","OBVIOUSLY","OCCASION","OCCUR","ODD","OF","OFF","OFFENCE","OFFER","OFFICE","OFFICER","OFFICIAL","OFTEN","OIL","OKAY","OLD","ON","ONCE","ONE","ONLY","ONTO","OPEN","OPERATE","OPERATION","OPINION","OPPORTUNITY","OPPOSITION","OPTION","OR","ORDER","ORDINARY","ORGANISATION","ORGANISE","ORGANIZATION","ORIGIN","ORIGINAL","OTHER","OTHERWISE","OUGHT","OUR","OURSELVES","OUT","OUTCOME","OUTPUT","OUTSIDE","OVER","OVERALL","OWN","OWNER","PACKAGE","PAGE","PAIN","PAINT","PAINTING","PAIR","PANEL","PAPER","PARENT","PARK","PARLIAMENT","PART","PARTICULAR","PARTICULARLY","PARTLY","PARTNER","PARTY","PASS","PASSAGE","PAST","PATH","PATIENT","PATTERN","PAY","PAYMENT","PEACE","PENSION","PEOPLE","PER","PERCENT","PERFECT","PERFORM","PERFORMANCE","PERHAPS","PERIOD","PERMANENT","PERSON","PERSONAL","PERSUADE","PHASE","PHONE","PHOTOGRAPH","PHYSICAL","PICK","PICTURE","PIECE","PLACE","PLAN","PLANNING","PLANT","PLASTIC","PLATE","PLAY","PLAYER","PLEASE","PLEASURE","PLENTY","PLUS","POCKET","POINT","POLICE","POLICY","POLITICAL","POLITICS","POOL","POOR","POPULAR","POPULATION","POSITION","POSITIVE","POSSIBILITY","POSSIBLE","POSSIBLY","POST","POTENTIAL","POUND","POWER","POWERFUL","PRACTICAL","PRACTICE","PREFER","PREPARE","PRESENCE","PRESENT","PRESIDENT","PRESS","PRESSURE","PRETTY","PREVENT","PREVIOUS","PREVIOUSLY","PRICE","PRIMARY","PRIME","PRINCIPLE","PRIORITY","PRISON","PRISONER","PRIVATE","PROBABLY","PROBLEM","PROCEDURE","PROCESS","PRODUCE","PRODUCT","PRODUCTION","PROFESSIONAL","PROFIT","PROGRAM","PROGRAMME","PROGRESS","PROJECT","PROMISE","PROMOTE","PROPER","PROPERLY","PROPERTY","PROPORTION","PROPOSE","PROPOSAL","PROSPECT","PROTECT","PROTECTION","PROVE","PROVIDE","PROVIDED","PROVISION","PUB","PUBLIC","PUBLICATION","PUBLISH","PULL","PUPIL","PURPOSE","PUSH","PUT","QUALITY","QUARTER","QUESTION","QUICK","QUICKLY","QUIET","QUITE","RACE","RADIO","RAILWAY","RAIN","RAISE","RANGE","RAPIDLY","RARE","RATE","RATHER","REACH","REACTION","READ","READER","READING","READY","REAL","REALISE","REALITY","REALIZE","REALLY","REASON","REASONABLE","RECALL","RECEIVE","RECENT","RECENTLY","RECOGNISE","RECOGNITION","RECOGNIZE","RECOMMEND","RECORD","RECOVER","RED","REDUCE","REDUCTION","REFER","REFERENCE","REFLECT","REFORM","REFUSE","REGARD","REGION","REGIONAL","REGULAR","REGULATION","REJECT","RELATE","RELATION","RELATIONSHIP","RELATIVE","RELATIVELY","RELEASE","RELEVANT","RELIEF","RELIGION","RELIGIOUS","RELY","REMAIN","REMEMBER","REMIND","REMOVE","REPEAT","REPLACE","REPLY","REPORT","REPRESENT","REPRESENTATION","REPRESENTATIVE","REQUEST","REQUIRE","REQUIREMENT","RESEARCH","RESOURCE","RESPECT","RESPOND","RESPONSE","RESPONSIBILITY","RESPONSIBLE","REST","RESTAURANT","RESULT","RETAIN","RETURN","REVEAL","REVENUE","REVIEW","REVOLUTION","RICH","RIDE","RIGHT","RING","RISE","RISK","RIVER","ROAD","ROCK","ROLE","ROLL","ROOF","ROOM","ROUND","ROUTE","ROW","ROYAL","RULE","RUN","RURAL","SAFE","SAFETY","SALE","SAME","SAMPLE","SATISFY","SAVE","SAY","SCALE","SCENE","SCHEME","SCHOOL","SCIENCE","SCIENTIFIC","SCIENTIST","SCORE","SCREEN","SEA","SEARCH","SEASON","SEAT","SECOND","SECONDARY","SECRETARY","SECTION","SECTOR","SECURE","SECURITY","SEE","SEEK","SEEM","SELECT","SELECTION","SELL","SEND","SENIOR","SENSE","SENTENCE","SEPARATE","SEQUENCE","SERIES","SERIOUS","SERIOUSLY","SERVANT","SERVE","SERVICE","SESSION","SET","SETTLE","SETTLEMENT","SEVERAL","SEVERE","SEX","SEXUAL","SHAKE","SHALL","SHAPE","SHARE","SHE","SHEET","SHIP","SHOE","SHOOT","SHOP","SHORT","SHOT","SHOULD","SHOULDER","SHOUT","SHOW","SHUT","SIDE","SIGHT","SIGN","SIGNAL","SIGNIFICANCE","SIGNIFICANT","SILENCE","SIMILAR","SIMPLE","SIMPLY","SINCE","SING","SINGLE","SIR","SISTER","SIT","SITE","SITUATION","SIZE","SKILL","SKIN","SKY","SLEEP","SLIGHTLY","SLIP","SLOW","SLOWLY","SMALL","SMILE","SO","SOCIAL","SOCIETY","SOFT","SOFTWARE","SOIL","SOLDIER","SOLICITOR","SOLUTION","SOME","SOMEBODY","SOMEONE","SOMETHING","SOMETIMES","SOMEWHAT","SOMEWHERE","SON","SONG","SOON","SORRY","SORT","SOUND","SOURCE","SOUTH","SOUTHERN","SPACE","SPEAK","SPEAKER","SPECIAL","SPECIES","SPECIFIC","SPEECH","SPEED","SPEND","SPIRIT","SPORT","SPOT","SPREAD","SPRING","STAFF","STAGE","STAND","STANDARD","STAR","START","STATE","STATEMENT","STATION","STATUS","STAY","STEAL","STEP","STICK","STILL","STOCK","STONE","STOP","STORE","STORY","STRAIGHT","STRANGE","STRATEGY","STREET","STRENGTH","STRIKE","STRONG","STRONGLY","STRUCTURE","STUDENT","STUDIO","STUDY","STUFF","STYLE","SUBJECT","SUBSTANTIAL","SUCCEED","SUCCESS","SUCCESSFUL","SUCH","SUDDENLY","SUFFER","SUFFICIENT","SUGGEST","SUGGESTION","SUITABLE","SUM","SUMMER","SUN","SUPPLY","SUPPORT","SUPPOSE","SURE","SURELY","SURFACE","SURPRISE","SURROUND","SURVEY","SURVIVE","SWITCH","SYSTEM","TABLE","TAKE","TALK","TALL","TAPE","TARGET","TASK","TAX","TEA","TEACH","TEACHER","TEACHING","TEAM","TEAR","TECHNICAL","TECHNIQUE","TECHNOLOGY","TELEPHONE","TELEVISION","TELL","TEMPERATURE","TEND","TERM","TERMS","TERRIBLE","TEST","TEXT","THAN","THANK","THANKS","THAT","THE","THEATRE","THEIR","THEM","THEME","THEMSELVES","THEN","THEORY","THERE","THEREFORE","THESE","THEY","THIN","THING","THINK","THIS","THOSE","THOUGH","THOUGHT","THREAT","THREATEN","THROUGH","THROUGHOUT","THROW","THUS","TICKET","TIME","TINY","TITLE","TO","TODAY","TOGETHER","TOMORROW","TONE","TONIGHT","TOO","TOOL","TOOTH","TOP","TOTAL","TOTALLY","TOUCH","TOUR","TOWARDS","TOWN","TRACK","TRADE","TRADITION","TRADITIONAL","TRAFFIC","TRAIN","TRAINING","TRANSFER","TRANSPORT","TRAVEL","TREAT","TREATMENT","TREATY","TREE","TREND","TRIAL","TRIP","TROOP","TROUBLE","TRUE","TRUST","TRUTH","TRY","TURN","TWICE","TYPE","TYPICAL","UNABLE","UNDER","UNDERSTAND","UNDERSTANDING","UNDERTAKE","UNEMPLOYMENT","UNFORTUNATELY","UNION","UNIT","UNITED","UNIVERSITY","UNLESS","UNLIKELY","UNTIL","UP","UPON","UPPER","URBAN","US","USE","USED","USEFUL","USER","USUAL","USUALLY","VALUE","VARIATION","VARIETY","VARIOUS","VARY","VAST","VEHICLE","VERSION","VERY","VIA","VICTIM","VICTORY","VIDEO","VIEW","VILLAGE","VIOLENCE","VISION","VISIT","VISITOR","VITAL","VOICE","VOLUME","VOTE","WAGE","WAIT","WALK","WALL","WANT","WAR","WARM","WARN","WASH","WATCH","WATER","WAVE","WAY","WE","WEAK","WEAPON","WEAR","WEATHER","WEEK","WEEKEND","WEIGHT","WELCOME","WELFARE","WELL","WEST","WESTERN","WHAT","WHATEVER","WHEN","WHERE","WHEREAS","WHETHER","WHICH","WHILE","WHILST","WHITE","WHO","WHOLE","WHOM","WHOSE","WHY","WIDE","WIDELY","WIFE","WILD","WILL","WIN","WIND","WINDOW","WINE","WING","WINNER","WINTER","WISH","WITH","WITHDRAW","WITHIN","WITHOUT","WOMAN","WONDER","WONDERFUL","WOOD","WORD","WORK","WORKER","WORKING","WORKS","WORLD","WORRY","WORTH","WOULD","WRITE","WRITER","WRITING","WRONG","YARD","YEAH","YEAR","YES","YESTERDAY","YET","YOU","YOUNG","YOUR","YOURSELF","YOUTH" diff --git a/project_euler/problem_043/__init__.py b/project_euler/problem_043/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_043/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_043/sol1.py b/project_euler/problem_043/sol1.py new file mode 100644 index 000000000000..cff121b7ce5b --- /dev/null +++ b/project_euler/problem_043/sol1.py @@ -0,0 +1,60 @@ +""" +Problem 43: https://projecteuler.net/problem=43 + +The number, 1406357289, is a 0 to 9 pandigital number because it is made up of +each of the digits 0 to 9 in some order, but it also has a rather interesting +sub-string divisibility property. + +Let d1 be the 1st digit, d2 be the 2nd digit, and so on. In this way, we note +the following: + +d2d3d4=406 is divisible by 2 +d3d4d5=063 is divisible by 3 +d4d5d6=635 is divisible by 5 +d5d6d7=357 is divisible by 7 +d6d7d8=572 is divisible by 11 +d7d8d9=728 is divisible by 13 +d8d9d10=289 is divisible by 17 +Find the sum of all 0 to 9 pandigital numbers with this property. +""" + + +from itertools import permutations + + +def is_substring_divisible(num: tuple) -> bool: + """ + Returns True if the pandigital number passes + all the divisibility tests. + >>> is_substring_divisible((0, 1, 2, 4, 6, 5, 7, 3, 8, 9)) + False + >>> is_substring_divisible((5, 1, 2, 4, 6, 0, 7, 8, 3, 9)) + False + >>> is_substring_divisible((1, 4, 0, 6, 3, 5, 7, 2, 8, 9)) + True + """ + tests = [2, 3, 5, 7, 11, 13, 17] + for i, test in enumerate(tests): + if (num[i + 1] * 100 + num[i + 2] * 10 + num[i + 3]) % test != 0: + return False + return True + + +def solution(n: int = 10) -> int: + """ + Returns the sum of all pandigital numbers which pass the + divisiility tests. + >>> solution(10) + 16695334890 + """ + list_nums = [ + int("".join(map(str, num))) + for num in permutations(range(n)) + if is_substring_divisible(num) + ] + + return sum(list_nums) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_044/__init__.py b/project_euler/problem_044/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_044/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_044/sol1.py b/project_euler/problem_044/sol1.py new file mode 100644 index 000000000000..940a2d041fbf --- /dev/null +++ b/project_euler/problem_044/sol1.py @@ -0,0 +1,47 @@ +""" +Problem 44: https://projecteuler.net/problem=44 + +Pentagonal numbers are generated by the formula, Pn=n(3n−1)/2. The first ten +pentagonal numbers are: +1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ... +It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference, +70 − 22 = 48, is not pentagonal. + +Find the pair of pentagonal numbers, Pj and Pk, for which their sum and difference +are pentagonal and D = |Pk − Pj| is minimised; what is the value of D? +""" + + +def is_pentagonal(n: int) -> bool: + """ + Returns True if n is pentagonal, False otherwise. + >>> is_pentagonal(330) + True + >>> is_pentagonal(7683) + False + >>> is_pentagonal(2380) + True + """ + root = (1 + 24 * n) ** 0.5 + return ((1 + root) / 6) % 1 == 0 + + +def solution(limit: int = 5000) -> int: + """ + Returns the minimum difference of two pentagonal numbers P1 and P2 such that + P1 + P2 is pentagonal and P2 - P1 is pentagonal. + >>> solution(5000) + 5482660 + """ + pentagonal_nums = [(i * (3 * i - 1)) // 2 for i in range(1, limit)] + for i, pentagonal_i in enumerate(pentagonal_nums): + for j in range(i, len(pentagonal_nums)): + pentagonal_j = pentagonal_nums[j] + a = pentagonal_i + pentagonal_j + b = pentagonal_j - pentagonal_i + if is_pentagonal(a) and is_pentagonal(b): + return b + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_045/__init__.py b/project_euler/problem_045/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_045/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_045/sol1.py b/project_euler/problem_045/sol1.py new file mode 100644 index 000000000000..5a0201aa48fa --- /dev/null +++ b/project_euler/problem_045/sol1.py @@ -0,0 +1,59 @@ +""" +Problem 45: https://projecteuler.net/problem=45 + +Triangle, pentagonal, and hexagonal numbers are generated by the following formulae: +Triangle T(n) = (n * (n + 1)) / 2 1, 3, 6, 10, 15, ... +Pentagonal P(n) = (n * (3 * n − 1)) / 2 1, 5, 12, 22, 35, ... +Hexagonal H(n) = n * (2 * n − 1) 1, 6, 15, 28, 45, ... +It can be verified that T(285) = P(165) = H(143) = 40755. + +Find the next triangle number that is also pentagonal and hexagonal. +All trinagle numbers are hexagonal numbers. +T(2n-1) = n * (2 * n - 1) = H(n) +So we shall check only for hexagonal numbers which are also pentagonal. +""" + + +def hexagonal_num(n: int) -> int: + """ + Returns nth hexagonal number + >>> hexagonal_num(143) + 40755 + >>> hexagonal_num(21) + 861 + >>> hexagonal_num(10) + 190 + """ + return n * (2 * n - 1) + + +def is_pentagonal(n: int) -> bool: + """ + Returns True if n is pentagonal, False otherwise. + >>> is_pentagonal(330) + True + >>> is_pentagonal(7683) + False + >>> is_pentagonal(2380) + True + """ + root = (1 + 24 * n) ** 0.5 + return ((1 + root) / 6) % 1 == 0 + + +def solution(start: int = 144) -> int: + """ + Returns the next number which is traingular, pentagonal and hexagonal. + >>> solution(144) + 1533776805 + """ + n = start + num = hexagonal_num(n) + while not is_pentagonal(num): + n += 1 + num = hexagonal_num(n) + return num + + +if __name__ == "__main__": + print(f"{solution()} = ") diff --git a/project_euler/problem_046/__init__.py b/project_euler/problem_046/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_046/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_046/sol1.py b/project_euler/problem_046/sol1.py new file mode 100644 index 000000000000..4b56f44da198 --- /dev/null +++ b/project_euler/problem_046/sol1.py @@ -0,0 +1,95 @@ +""" +Problem 46: https://projecteuler.net/problem=46 + +It was proposed by Christian Goldbach that every odd composite number can be +written as the sum of a prime and twice a square. + +9 = 7 + 2 × 12 +15 = 7 + 2 × 22 +21 = 3 + 2 × 32 +25 = 7 + 2 × 32 +27 = 19 + 2 × 22 +33 = 31 + 2 × 12 + +It turns out that the conjecture was false. + +What is the smallest odd composite that cannot be written as the sum of a +prime and twice a square? +""" + +from __future__ import annotations + +seive = [True] * 100001 +i = 2 +while i * i <= 100000: + if seive[i]: + for j in range(i * i, 100001, i): + seive[j] = False + i += 1 + + +def is_prime(n: int) -> bool: + """ + Returns True if n is prime, + False otherwise, for 2 <= n <= 100000 + >>> is_prime(87) + False + >>> is_prime(23) + True + >>> is_prime(25363) + False + """ + return seive[n] + + +odd_composites = [num for num in range(3, len(seive), 2) if not is_prime(num)] + + +def compute_nums(n: int) -> list[int]: + """ + Returns a list of first n odd composite numbers which do + not follow the conjecture. + >>> compute_nums(1) + [5777] + >>> compute_nums(2) + [5777, 5993] + >>> compute_nums(0) + Traceback (most recent call last): + ... + ValueError: n must be >= 0 + >>> compute_nums("a") + Traceback (most recent call last): + ... + ValueError: n must be an integer + >>> compute_nums(1.1) + Traceback (most recent call last): + ... + ValueError: n must be an integer + + """ + if not isinstance(n, int): + raise ValueError("n must be an integer") + if n <= 0: + raise ValueError("n must be >= 0") + + list_nums = [] + for num in range(len(odd_composites)): + i = 0 + while 2 * i * i <= odd_composites[num]: + rem = odd_composites[num] - 2 * i * i + if is_prime(rem): + break + i += 1 + else: + list_nums.append(odd_composites[num]) + if len(list_nums) == n: + return list_nums + + +def solution() -> int: + """Return the solution to the problem""" + return compute_nums(1)[0] + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_047/__init__.py b/project_euler/problem_047/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_047/sol1.py b/project_euler/problem_047/sol1.py new file mode 100644 index 000000000000..1287e0d9e107 --- /dev/null +++ b/project_euler/problem_047/sol1.py @@ -0,0 +1,112 @@ +""" +Combinatoric selections + +Problem 47 + +The first two consecutive numbers to have two distinct prime factors are: + +14 = 2 × 7 +15 = 3 × 5 + +The first three consecutive numbers to have three distinct prime factors are: + +644 = 2² × 7 × 23 +645 = 3 × 5 × 43 +646 = 2 × 17 × 19. + +Find the first four consecutive integers to have four distinct prime factors each. +What is the first of these numbers? +""" + +from functools import lru_cache + + +def unique_prime_factors(n: int) -> set: + """ + Find unique prime factors of an integer. + Tests include sorting because only the set really matters, + not the order in which it is produced. + >>> sorted(set(unique_prime_factors(14))) + [2, 7] + >>> sorted(set(unique_prime_factors(644))) + [2, 7, 23] + >>> sorted(set(unique_prime_factors(646))) + [2, 17, 19] + """ + i = 2 + factors = set() + while i * i <= n: + if n % i: + i += 1 + else: + n //= i + factors.add(i) + if n > 1: + factors.add(n) + return factors + + +@lru_cache +def upf_len(num: int) -> int: + """ + Memoize upf() length results for a given value. + >>> upf_len(14) + 2 + """ + return len(unique_prime_factors(num)) + + +def equality(iterable: list) -> bool: + """ + Check equality of ALL elements in an interable. + >>> equality([1, 2, 3, 4]) + False + >>> equality([2, 2, 2, 2]) + True + >>> equality([1, 2, 3, 2, 1]) + False + """ + return len(set(iterable)) in (0, 1) + + +def run(n: int) -> list: + """ + Runs core process to find problem solution. + >>> run(3) + [644, 645, 646] + """ + + # Incrementor variable for our group list comprehension. + # This serves as the first number in each list of values + # to test. + base = 2 + + while True: + # Increment each value of a generated range + group = [base + i for i in range(n)] + + # Run elements through out unique_prime_factors function + # Append our target number to the end. + checker = [upf_len(x) for x in group] + checker.append(n) + + # If all numbers in the list are equal, return the group variable. + if equality(checker): + return group + + # Increment our base variable by 1 + base += 1 + + +def solution(n: int = 4) -> int: + """Return the first value of the first four consecutive integers to have four + distinct prime factors each. + >>> solution() + 134043 + """ + results = run(n) + return results[0] if len(results) else None + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_048/__init__.py b/project_euler/problem_048/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_048/sol1.py b/project_euler/problem_048/sol1.py new file mode 100644 index 000000000000..01ff702d9cd5 --- /dev/null +++ b/project_euler/problem_048/sol1.py @@ -0,0 +1,25 @@ +""" +Self Powers +Problem 48 + +The series, 1^1 + 2^2 + 3^3 + ... + 10^10 = 10405071317. + +Find the last ten digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. +""" + + +def solution(): + """ + Returns the last 10 digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. + + >>> solution() + '9110846700' + """ + total = 0 + for i in range(1, 1001): + total += i ** i + return str(total)[-10:] + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_049/__init__.py b/project_euler/problem_049/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_049/sol1.py b/project_euler/problem_049/sol1.py new file mode 100644 index 000000000000..c0d0715be91c --- /dev/null +++ b/project_euler/problem_049/sol1.py @@ -0,0 +1,139 @@ +""" +Prime permutations + +Problem 49 + +The arithmetic sequence, 1487, 4817, 8147, in which each of +the terms increases by 3330, is unusual in two ways: +(i) each of the three terms are prime, +(ii) each of the 4-digit numbers are permutations of one another. + +There are no arithmetic sequences made up of three 1-, 2-, or 3-digit primes, +exhibiting this property, but there is one other 4-digit increasing sequence. + +What 12-digit number do you form by concatenating the three terms in this sequence? + +Solution: + +First, we need to generate all 4 digits prime numbers. Then greedy +all of them and use permutation to form new numbers. Use binary search +to check if the permutated numbers is in our prime list and include +them in a candidate list. + +After that, bruteforce all passed candidates sequences using +3 nested loops since we know the answer will be 12 digits. +The bruteforce of this solution will be about 1 sec. +""" + +from itertools import permutations +from math import floor, sqrt + + +def is_prime(number: int) -> bool: + """ + function to check whether the number is prime or not. + >>> is_prime(2) + True + >>> is_prime(6) + False + >>> is_prime(1) + False + >>> is_prime(-800) + False + >>> is_prime(104729) + True + """ + + if number < 2: + return False + + for i in range(2, floor(sqrt(number)) + 1): + if number % i == 0: + return False + + return True + + +def search(target: int, prime_list: list) -> bool: + """ + function to search a number in a list using Binary Search. + >>> search(3, [1, 2, 3]) + True + >>> search(4, [1, 2, 3]) + False + >>> search(101, list(range(-100, 100))) + False + """ + + left, right = 0, len(prime_list) - 1 + while left <= right: + middle = (left + right) // 2 + if prime_list[middle] == target: + return True + elif prime_list[middle] < target: + left = middle + 1 + else: + right = middle - 1 + + return False + + +def solution(): + """ + Return the solution of the problem. + >>> solution() + 296962999629 + """ + prime_list = [n for n in range(1001, 10000, 2) if is_prime(n)] + candidates = [] + + for number in prime_list: + tmp_numbers = [] + + for prime_member in permutations(list(str(number))): + prime = int("".join(prime_member)) + + if prime % 2 == 0: + continue + + if search(prime, prime_list): + tmp_numbers.append(prime) + + tmp_numbers.sort() + if len(tmp_numbers) >= 3: + candidates.append(tmp_numbers) + + passed = [] + for candidate in candidates: + length = len(candidate) + found = False + + for i in range(length): + for j in range(i + 1, length): + for k in range(j + 1, length): + if ( + abs(candidate[i] - candidate[j]) + == abs(candidate[j] - candidate[k]) + and len({candidate[i], candidate[j], candidate[k]}) == 3 + ): + passed.append( + sorted([candidate[i], candidate[j], candidate[k]]) + ) + found = True + + if found: + break + if found: + break + if found: + break + + answer = set() + for seq in passed: + answer.add("".join([str(i) for i in seq])) + + return max([int(x) for x in answer]) + + +if __name__ == "__main__": + print(solution()) 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() = }") diff --git a/project_euler/problem_051/__init__.py b/project_euler/problem_051/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_051/sol1.py b/project_euler/problem_051/sol1.py new file mode 100644 index 000000000000..b160b5a2dbd4 --- /dev/null +++ b/project_euler/problem_051/sol1.py @@ -0,0 +1,111 @@ +""" +https://projecteuler.net/problem=51 +Prime digit replacements +Problem 51 + +By replacing the 1st digit of the 2-digit number *3, it turns out that six of +the nine possible values: 13, 23, 43, 53, 73, and 83, are all prime. + +By replacing the 3rd and 4th digits of 56**3 with the same digit, this 5-digit +number is the first example having seven primes among the ten generated numbers, +yielding the family: 56003, 56113, 56333, 56443, 56663, 56773, and 56993. +Consequently 56003, being the first member of this family, is the smallest prime +with this property. + +Find the smallest prime which, by replacing part of the number (not necessarily +adjacent digits) with the same digit, is part of an eight prime value family. +""" + +from collections import Counter +from typing import List + + +def prime_sieve(n: int) -> List[int]: + """ + Sieve of Erotosthenes + Function to return all the prime numbers up to a certain number + 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] * n + is_prime[0] = False + is_prime[1] = False + is_prime[2] = True + + for i in range(3, int(n ** 0.5 + 1), 2): + index = i * 2 + while index < n: + is_prime[index] = False + index = index + i + + primes = [2] + + for i in range(3, n, 2): + if is_prime[i]: + primes.append(i) + + return primes + + +def digit_replacements(number: int) -> List[List[int]]: + """ + Returns all the possible families of digit replacements in a number which + contains at least one repeating digit + + >>> digit_replacements(544) + [[500, 511, 522, 533, 544, 555, 566, 577, 588, 599]] + + >>> digit_replacements(3112) + [[3002, 3112, 3222, 3332, 3442, 3552, 3662, 3772, 3882, 3992]] + """ + number = str(number) + replacements = [] + digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + + for duplicate in Counter(number) - Counter(set(number)): + family = [int(number.replace(duplicate, digit)) for digit in digits] + replacements.append(family) + + return replacements + + +def solution(family_length: int = 8) -> int: + """ + Returns the solution of the problem + + >>> solution(2) + 229399 + + >>> solution(3) + 221311 + """ + numbers_checked = set() + + # Filter primes with less than 3 replaceable digits + primes = { + x for x in set(prime_sieve(1_000_000)) if len(str(x)) - len(set(str(x))) >= 3 + } + + for prime in primes: + if prime in numbers_checked: + continue + + replacements = digit_replacements(prime) + + for family in replacements: + numbers_checked.update(family) + primes_in_family = primes.intersection(family) + + if len(primes_in_family) != family_length: + continue + + return min(primes_in_family) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_052/__init__.py b/project_euler/problem_052/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_052/sol1.py b/project_euler/problem_052/sol1.py new file mode 100644 index 000000000000..df5c46ae05d1 --- /dev/null +++ b/project_euler/problem_052/sol1.py @@ -0,0 +1,37 @@ +""" +Permuted multiples +Problem 52 + +It can be seen that the number, 125874, and its double, 251748, contain exactly +the same digits, but in a different order. + +Find the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and 6x, +contain the same digits. +""" + + +def solution(): + """Returns the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and + 6x, contain the same digits. + + >>> solution() + 142857 + """ + i = 1 + + while True: + if ( + sorted(list(str(i))) + == sorted(list(str(2 * i))) + == sorted(list(str(3 * i))) + == sorted(list(str(4 * i))) + == sorted(list(str(5 * i))) + == sorted(list(str(6 * i))) + ): + return i + + i += 1 + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_053/__init__.py b/project_euler/problem_053/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_053/sol1.py b/project_euler/problem_053/sol1.py new file mode 100644 index 000000000000..0692bbe0ebb8 --- /dev/null +++ b/project_euler/problem_053/sol1.py @@ -0,0 +1,43 @@ +""" +Combinatoric selections +Problem 53 + +There are exactly ten ways of selecting three from five, 12345: + + 123, 124, 125, 134, 135, 145, 234, 235, 245, and 345 + +In combinatorics, we use the notation, 5C3 = 10. + +In general, + +nCr = n!/(r!(n−r)!),where r ≤ n, n! = n×(n−1)×...×3×2×1, and 0! = 1. +It is not until n = 23, that a value exceeds one-million: 23C10 = 1144066. + +How many, not necessarily distinct, values of nCr, for 1 ≤ n ≤ 100, are greater +than one-million? +""" +from math import factorial + + +def combinations(n, r): + return factorial(n) / (factorial(r) * factorial(n - r)) + + +def solution(): + """Returns the number of values of nCr, for 1 ≤ n ≤ 100, are greater than + one-million + + >>> solution() + 4075 + """ + total = 0 + + for i in range(1, 101): + for j in range(1, i + 1): + if combinations(i, j) > 1e6: + total += 1 + return total + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_054/__init__.py b/project_euler/problem_054/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_054/poker_hands.txt b/project_euler/problem_054/poker_hands.txt new file mode 100644 index 000000000000..9ab00248c061 --- /dev/null +++ b/project_euler/problem_054/poker_hands.txt @@ -0,0 +1,1000 @@ +8C TS KC 9H 4S 7D 2S 5D 3S AC +5C AD 5D AC 9C 7C 5H 8D TD KS +3H 7H 6S KC JS QH TD JC 2D 8S +TH 8H 5C QS TC 9H 4D JC KS JS +7C 5H KC QH JD AS KH 4C AD 4S +5H KS 9C 7D 9H 8D 3S 5D 5C AH +6H 4H 5C 3H 2H 3S QH 5S 6S AS +TD 8C 4H 7C TC KC 4C 3H 7S KS +7C 9C 6D KD 3H 4C QS QC AC KH +JC 6S 5H 2H 2D KD 9D 7C AS JS +AD QH TH 9D 8H TS 6D 3S AS AC +2H 4S 5C 5S TC KC JD 6C TS 3C +QD AS 6H JS 2C 3D 9H KC 4H 8S +KD 8S 9S 7C 2S 3S 6D 6S 4H KC +3C 8C 2D 7D 4D 9S 4S QH 4H JD +8C KC 7S TC 2D TS 8H QD AC 5C +3D KH QD 6C 6S AD AS 8H 2H QS +6S 8D 4C 8S 6C QH TC 6D 7D 9D +2S 8D 8C 4C TS 9S 9D 9C AC 3D +3C QS 2S 4H JH 3D 2D TD 8S 9H +5H QS 8S 6D 3C 8C JD AS 7H 7D +6H TD 9D AS JH 6C QC 9S KD JC +AH 8S QS 4D TH AC TS 3C 3D 5C +5S 4D JS 3D 8H 6C TS 3S AD 8C +6D 7C 5D 5H 3S 5C JC 2H 5S 3D +5H 6H 2S KS 3D 5D JD 7H JS 8H +KH 4H AS JS QS QC TC 6D 7C KS +3D QS TS 2H JS 4D AS 9S JC KD +QD 5H 4D 5D KH 7H 3D JS KD 4H +2C 9H 6H 5C 9D 6C JC 2D TH 9S +7D 6D AS QD JH 4D JS 7C QS 5C +3H KH QD AD 8C 8H 3S TH 9D 5S +AH 9S 4D 9D 8S 4H JS 3C TC 8D +2C KS 5H QD 3S TS 9H AH AD 8S +5C 7H 5D KD 9H 4D 3D 2D KS AD +KS KC 9S 6D 2C QH 9D 9H TS TC +9C 6H 5D QH 4D AD 6D QC JS KH +9S 3H 9D JD 5C 4D 9H AS TC QH +2C 6D JC 9C 3C AD 9S KH 9D 7D +KC 9C 7C JC JS KD 3H AS 3C 7D +QD KH QS 2C 3S 8S 8H 9H 9C JC +QH 8D 3C KC 4C 4H 6D AD 9H 9D +3S KS QS 7H KH 7D 5H 5D JD AD +2H 2C 6H TH TC 7D 8D 4H 8C AS +4S 2H AC QC 3S 6D TH 4D 4C KH +4D TC KS AS 7C 3C 6D 2D 9H 6C +8C TD 5D QS 2C 7H 4C 9C 3H 9H +5H JH TS 7S TD 6H AD QD 8H 8S +5S AD 9C 8C 7C 8D 5H 9D 8S 2S +4H KH KS 9S 2S KC 5S AD 4S 7D +QS 9C QD 6H JS 5D AC 8D 2S AS +KH AC JC 3S 9D 9S 3C 9C 5S JS +AD 3C 3D KS 3S 5C 9C 8C TS 4S +JH 8D 5D 6H KD QS QD 3D 6C KC +8S JD 6C 3S 8C TC QC 3C QH JS +KC JC 8H 2S 9H 9C JH 8S 8C 9S +8S 2H QH 4D QC 9D KC AS TH 3C +8S 6H TH 7C 2H 6S 3C 3H AS 7S +QH 5S JS 4H 5H TS 8H AH AC JC +9D 8H 2S 4S TC JC 3C 7H 3H 5C +3D AD 3C 3S 4C QC AS 5D TH 8C +6S 9D 4C JS KH AH TS JD 8H AD +4C 6S 9D 7S AC 4D 3D 3S TC JD +AD 7H 6H 4H JH KC TD TS 7D 6S +8H JH TC 3S 8D 8C 9S 2C 5C 4D +2C 9D KC QH TH QS JC 9C 4H TS +QS 3C QD 8H KH 4H 8D TD 8S AC +7C 3C TH 5S 8H 8C 9C JD TC KD +QC TC JD TS 8C 3H 6H KD 7C TD +JH QS KS 9C 6D 6S AS 9H KH 6H +2H 4D AH 2D JH 6H TD 5D 4H JD +KD 8C 9S JH QD JS 2C QS 5C 7C +4S TC 7H 8D 2S 6H 7S 9C 7C KC +8C 5D 7H 4S TD QC 8S JS 4H KS +AD 8S JH 6D TD KD 7C 6C 2D 7D +JC 6H 6S JS 4H QH 9H AH 4C 3C +6H 5H AS 7C 7S 3D KH KC 5D 5C +JC 3D TD AS 4D 6D 6S QH JD KS +8C 7S 8S QH 2S JD 5C 7H AH QD +8S 3C 6H 6C 2C 8D TD 7D 4C 4D +5D QH KH 7C 2S 7H JS 6D QC QD +AD 6C 6S 7D TH 6H 2H 8H KH 4H +KS JS KD 5D 2D KH 7D 9C 8C 3D +9C 6D QD 3C KS 3S 7S AH JD 2D +AH QH AS JC 8S 8H 4C KC TH 7D +JC 5H TD 7C 5D KD 4C AD 8H JS +KC 2H AC AH 7D JH KH 5D 7S 6D +9S 5S 9C 6H 8S TD JD 9H 6C AC +7D 8S 6D TS KD 7H AC 5S 7C 5D +AH QC JC 4C TC 8C 2H TS 2C 7D +KD KC 6S 3D 7D 2S 8S 3H 5S 5C +8S 5D 8H 4C 6H KC 3H 7C 5S KD +JH 8C 3D 3C 6C KC TD 7H 7C 4C +JC KC 6H TS QS TD KS 8H 8C 9S +6C 5S 9C QH 7D AH KS KC 9S 2C +4D 4S 8H TD 9C 3S 7D 9D AS TH +6S 7D 3C 6H 5D KD 2C 5C 9D 9C +2H KC 3D AD 3H QD QS 8D JC 4S +8C 3H 9C 7C AD 5D JC 9D JS AS +5D 9H 5C 7H 6S 6C QC JC QD 9S +JC QS JH 2C 6S 9C QC 3D 4S TC +4H 5S 8D 3D 4D 2S KC 2H JS 2C +TD 3S TH KD 4D 7H JH JS KS AC +7S 8C 9S 2D 8S 7D 5C AD 9D AS +8C 7H 2S 6C TH 3H 4C 3S 8H AC +KD 5H JC 8H JD 2D 4H TD JH 5C +3D AS QH KS 7H JD 8S 5S 6D 5H +9S 6S TC QS JC 5C 5D 9C TH 8C +5H 3S JH 9H 2S 2C 6S 7S AS KS +8C QD JC QS TC QC 4H AC KH 6C +TC 5H 7D JH 4H 2H 8D JC KS 4D +5S 9C KH KD 9H 5C TS 3D 7D 2D +5H AS TC 4D 8C 2C TS 9D 3H 8D +6H 8D 2D 9H JD 6C 4S 5H 5S 6D +AD 9C JC 7D 6H 9S 6D JS 9H 3C +AD JH TC QS 4C 5D 9S 7C 9C AH +KD 6H 2H TH 8S QD KS 9D 9H AS +4H 8H 8D 5H 6C AH 5S AS AD 8S +QS 5D 4S 2H TD KS 5H AC 3H JC +9C 7D QD KD AC 6D 5H QH 6H 5S +KC AH QH 2H 7D QS 3H KS 7S JD +6C 8S 3H 6D KS QD 5D 5C 8H TC +9H 4D 4S 6S 9D KH QC 4H 6C JD +TD 2D QH 4S 6H JH KD 3C QD 8C +4S 6H 7C QD 9D AS AH 6S AD 3C +2C KC TH 6H 8D AH 5C 6D 8S 5D +TD TS 7C AD JC QD 9H 3C KC 7H +5D 4D 5S 8H 4H 7D 3H JD KD 2D +JH TD 6H QS 4S KD 5C 8S 7D 8H +AC 3D AS 8C TD 7H KH 5D 6C JD +9D KS 7C 6D QH TC JD KD AS KC +JH 8S 5S 7S 7D AS 2D 3D AD 2H +2H 5D AS 3C QD KC 6H 9H 9S 2C +9D 5D TH 4C JH 3H 8D TC 8H 9H +6H KD 2C TD 2H 6C 9D 2D JS 8C +KD 7S 3C 7C AS QH TS AD 8C 2S +QS 8H 6C JS 4C 9S QC AD TD TS +2H 7C TS TC 8C 3C 9H 2D 6D JC +TC 2H 8D JH KS 6D 3H TD TH 8H +9D TD 9H QC 5D 6C 8H 8C KC TS +2H 8C 3D AH 4D TH TC 7D 8H KC +TS 5C 2D 8C 6S KH AH 5H 6H KC +5S 5D AH TC 4C JD 8D 6H 8C 6C +KC QD 3D 8H 2D JC 9H 4H AD 2S +TD 6S 7D JS KD 4H QS 2S 3S 8C +4C 9H JH TS 3S 4H QC 5S 9S 9C +2C KD 9H JS 9S 3H JC TS 5D AC +AS 2H 5D AD 5H JC 7S TD JS 4C +2D 4S 8H 3D 7D 2C AD KD 9C TS +7H QD JH 5H JS AC 3D TH 4C 8H +6D KH KC QD 5C AD 7C 2D 4H AC +3D 9D TC 8S QD 2C JC 4H JD AH +6C TD 5S TC 8S AH 2C 5D AS AC +TH 7S 3D AS 6C 4C 7H 7D 4H AH +5C 2H KS 6H 7S 4H 5H 3D 3C 7H +3C 9S AC 7S QH 2H 3D 6S 3S 3H +2D 3H AS 2C 6H TC JS 6S 9C 6C +QH KD QD 6D AC 6H KH 2C TS 8C +8H 7D 3S 9H 5D 3H 4S QC 9S 5H +2D 9D 7H 6H 3C 8S 5H 4D 3S 4S +KD 9S 4S TC 7S QC 3S 8S 2H 7H +TC 3D 8C 3H 6C 2H 6H KS KD 4D +KC 3D 9S 3H JS 4S 8H 2D 6C 8S +6H QS 6C TC QD 9H 7D 7C 5H 4D +TD 9D 8D 6S 6C TC 5D TS JS 8H +4H KC JD 9H TC 2C 6S 5H 8H AS +JS 9C 5C 6S 9D JD 8H KC 4C 6D +4D 8D 8S 6C 7C 6H 7H 8H 5C KC +TC 3D JC 6D KS 9S 6H 7S 9C 2C +6C 3S KD 5H TS 7D 9H 9S 6H KH +3D QD 4C 6H TS AC 3S 5C 2H KD +4C AS JS 9S 7C TS 7H 9H JC KS +4H 8C JD 3H 6H AD 9S 4S 5S KS +4C 2C 7D 3D AS 9C 2S QS KC 6C +8S 5H 3D 2S AC 9D 6S 3S 4D TD +QD TH 7S TS 3D AC 7H 6C 5D QC +TC QD AD 9C QS 5C 8D KD 3D 3C +9D 8H AS 3S 7C 8S JD 2D 8D KC +4C TH AC QH JS 8D 7D 7S 9C KH +9D 8D 4C JH 2C 2S QD KD TS 4H +4D 6D 5D 2D JH 3S 8S 3H TC KH +AD 4D 2C QS 8C KD JH JD AH 5C +5C 6C 5H 2H JH 4H KS 7C TC 3H +3C 4C QC 5D JH 9C QD KH 8D TC +3H 9C JS 7H QH AS 7C 9H 5H JC +2D 5S QD 4S 3C KC 6S 6C 5C 4C +5D KH 2D TS 8S 9C AS 9S 7C 4C +7C AH 8C 8D 5S KD QH QS JH 2C +8C 9D AH 2H AC QC 5S 8H 7H 2C +QD 9H 5S QS QC 9C 5H JC TH 4H +6C 6S 3H 5H 3S 6H KS 8D AC 7S +AC QH 7H 8C 4S KC 6C 3D 3S TC +9D 3D JS TH AC 5H 3H 8S 3S TC +QD KH JS KS 9S QC 8D AH 3C AC +5H 6C KH 3S 9S JH 2D QD AS 8C +6C 4D 7S 7H 5S JC 6S 9H 4H JH +AH 5S 6H 9S AD 3S TH 2H 9D 8C +4C 8D 9H 7C QC AD 4S 9C KC 5S +9D 6H 4D TC 4C JH 2S 5D 3S AS +2H 6C 7C KH 5C AD QS TH JD 8S +3S 4S 7S AH AS KC JS 2S AD TH +JS KC 2S 7D 8C 5C 9C TS 5H 9D +7S 9S 4D TD JH JS KH 6H 5D 2C +JD JS JC TH 2D 3D QD 8C AC 5H +7S KH 5S 9D 5D TD 4S 6H 3C 2D +4S 5D AC 8D 4D 7C AD AS AH 9C +6S TH TS KS 2C QC AH AS 3C 4S +2H 8C 3S JC 5C 7C 3H 3C KH JH +7S 3H JC 5S 6H 4C 2S 4D KC 7H +4D 7C 4H 9S 8S 6S AD TC 6C JC +KH QS 3S TC 4C 8H 8S AC 3C TS +QD QS TH 3C TS 7H 7D AH TD JC +TD JD QC 4D 9S 7S TS AD 7D AC +AH 7H 4S 6D 7C 2H 9D KS JC TD +7C AH JD 4H 6D QS TS 2H 2C 5C +TC KC 8C 9S 4C JS 3C JC 6S AH +AS 7D QC 3D 5S JC JD 9D TD KH +TH 3C 2S 6H AH AC 5H 5C 7S 8H +QC 2D AC QD 2S 3S JD QS 6S 8H +KC 4H 3C 9D JS 6H 3S 8S AS 8C +7H KC 7D JD 2H JC QH 5S 3H QS +9H TD 3S 8H 7S AC 5C 6C AH 7C +8D 9H AH JD TD QS 7D 3S 9C 8S +AH QH 3C JD KC 4S 5S 5D TD KS +9H 7H 6S JH TH 4C 7C AD 5C 2D +7C KD 5S TC 9D 6S 6C 5D 2S TH +KC 9H 8D 5H 7H 4H QC 3D 7C AS +6S 8S QC TD 4S 5C TH QS QD 2S +8S 5H TH QC 9H 6S KC 7D 7C 5C +7H KD AH 4D KH 5C 4S 2D KC QH +6S 2C TD JC AS 4D 6C 8C 4H 5S +JC TC JD 5S 6S 8D AS 9D AD 3S +6D 6H 5D 5S TC 3D 7D QS 9D QD +4S 6C 8S 3S 7S AD KS 2D 7D 7C +KC QH JC AC QD 5D 8D QS 7H 7D +JS AH 8S 5H 3D TD 3H 4S 6C JH +4S QS 7D AS 9H JS KS 6D TC 5C +2D 5C 6H TC 4D QH 3D 9H 8S 6C +6D 7H TC TH 5S JD 5C 9C KS KD +8D TD QH 6S 4S 6C 8S KC 5C TC +5S 3D KS AC 4S 7D QD 4C TH 2S +TS 8H 9S 6S 7S QH 3C AH 7H 8C +4C 8C TS JS QC 3D 7D 5D 7S JH +8S 7S 9D QC AC 7C 6D 2H JH KC +JS KD 3C 6S 4S 7C AH QC KS 5H +KS 6S 4H JD QS TC 8H KC 6H AS +KH 7C TC 6S TD JC 5C 7D AH 3S +3H 4C 4H TC TH 6S 7H 6D 9C QH +7D 5H 4S 8C JS 4D 3D 8S QH KC +3H 6S AD 7H 3S QC 8S 4S 7S JS +3S JD KH TH 6H QS 9C 6C 2D QD +4S QH 4D 5H KC 7D 6D 8D TH 5S +TD AD 6S 7H KD KH 9H 5S KC JC +3H QC AS TS 4S QD KS 9C 7S KC +TS 6S QC 6C TH TC 9D 5C 5D KD +JS 3S 4H KD 4C QD 6D 9S JC 9D +8S JS 6D 4H JH 6H 6S 6C KS KH +AC 7D 5D TC 9S KH 6S QD 6H AS +AS 7H 6D QH 8D TH 2S KH 5C 5H +4C 7C 3D QC TC 4S KH 8C 2D JS +6H 5D 7S 5H 9C 9H JH 8S TH 7H +AS JS 2S QD KH 8H 4S AC 8D 8S +3H 4C TD KD 8C JC 5C QS 2D JD +TS 7D 5D 6C 2C QS 2H 3C AH KS +4S 7C 9C 7D JH 6C 5C 8H 9D QD +2S TD 7S 6D 9C 9S QS KH QH 5C +JC 6S 9C QH JH 8D 7S JS KH 2H +8D 5H TH KC 4D 4S 3S 6S 3D QS +2D JD 4C TD 7C 6D TH 7S JC AH +QS 7S 4C TH 9D TS AD 4D 3H 6H +2D 3H 7D JD 3D AS 2S 9C QC 8S +4H 9H 9C 2C 7S JH KD 5C 5D 6H +TC 9H 8H JC 3C 9S 8D KS AD KC +TS 5H JD QS QH QC 8D 5D KH AH +5D AS 8S 6S 4C AH QC QD TH 7H +3H 4H 7D 6S 4S 9H AS 8H JS 9D +JD 8C 2C 9D 7D 5H 5S 9S JC KD +KD 9C 4S QD AH 7C AD 9D AC TD +6S 4H 4S 9C 8D KS TC 9D JH 7C +5S JC 5H 4S QH AC 2C JS 2S 9S +8C 5H AS QD AD 5C 7D 8S QC TD +JC 4C 8D 5C KH QS 4D 6H 2H 2C +TH 4S 2D KC 3H QD AC 7H AD 9D +KH QD AS 8H TH KC 8D 7S QH 8C +JC 6C 7D 8C KH AD QS 2H 6S 2D +JC KH 2D 7D JS QC 5H 4C 5D AD +TS 3S AD 4S TD 2D TH 6S 9H JH +9H 2D QS 2C 4S 3D KH AS AC 9D +KH 6S 8H 4S KD 7D 9D TS QD QC +JH 5H AH KS AS AD JC QC 5S KH +5D 7D 6D KS KD 3D 7C 4D JD 3S +AC JS 8D 5H 9C 3H 4H 4D TS 2C +6H KS KH 9D 7C 2S 6S 8S 2H 3D +6H AC JS 7S 3S TD 8H 3H 4H TH +9H TC QC KC 5C KS 6H 4H AC 8S +TC 7D QH 4S JC TS 6D 6C AC KH +QH 7D 7C JH QS QD TH 3H 5D KS +3D 5S 8D JS 4C 2C KS 7H 9C 4H +5H 8S 4H TD 2C 3S QD QC 3H KC +QC JS KD 9C AD 5S 9D 7D 7H TS +8C JC KH 7C 7S 6C TS 2C QD TH +5S 9D TH 3C 7S QH 8S 9C 2H 5H +5D 9H 6H 2S JS KH 3H 7C 2H 5S +JD 5D 5S 2C TC 2S 6S 6C 3C 8S +4D KH 8H 4H 2D KS 3H 5C 2S 9H +3S 2D TD 7H 8S 6H JD KC 9C 8D +6S QD JH 7C 9H 5H 8S 8H TH TD +QS 7S TD 7D TS JC KD 7C 3C 2C +3C JD 8S 4H 2D 2S TD AS 4D AC +AH KS 6C 4C 4S 7D 8C 9H 6H AS +5S 3C 9S 2C QS KD 4D 4S AC 5D +2D TS 2C JS KH QH 5D 8C AS KC +KD 3H 6C TH 8S 7S KH 6H 9S AC +6H 7S 6C QS AH 2S 2H 4H 5D 5H +5H JC QD 2C 2S JD AS QC 6S 7D +6C TC AS KD 8H 9D 2C 7D JH 9S +2H 4C 6C AH 8S TD 3H TH 7C TS +KD 4S TS 6C QH 8D 9D 9C AH 7D +6D JS 5C QD QC 9C 5D 8C 2H KD +3C QH JH AD 6S AH KC 8S 6D 6H +3D 7C 4C 7S 5S 3S 6S 5H JC 3C +QH 7C 5H 3C 3S 8C TS 4C KD 9C +QD 3S 7S 5H 7H QH JC 7C 8C KD +3C KD KH 2S 4C TS AC 6S 2C 7C +2C KH 3C 4C 6H 4D 5H 5S 7S QD +4D 7C 8S QD TS 9D KS 6H KD 3C +QS 4D TS 7S 4C 3H QD 8D 9S TC +TS QH AC 6S 3C 9H 9D QS 8S 6H +3S 7S 5D 4S JS 2D 6C QH 6S TH +4C 4H AS JS 5D 3D TS 9C AC 8S +6S 9C 7C 3S 5C QS AD AS 6H 3C +9S 8C 7H 3H 6S 7C AS 9H JD KH +3D 3H 7S 4D 6C 7C AC 2H 9C TH +4H 5S 3H AC TC TH 9C 9H 9S 8D +8D 9H 5H 4D 6C 2H QD 6S 5D 3S +4C 5C JD QS 4D 3H TH AC QH 8C +QC 5S 3C 7H AD 4C KS 4H JD 6D +QS AH 3H KS 9H 2S JS JH 5H 2H +2H 5S TH 6S TS 3S KS 3C 5H JS +2D 9S 7H 3D KC JH 6D 7D JS TD +AC JS 8H 2C 8C JH JC 2D TH 7S +5D 9S 8H 2H 3D TC AH JC KD 9C +9D QD JC 2H 6D KH TS 9S QH TH +2C 8D 4S JD 5H 3H TH TC 9C KC +AS 3D 9H 7D 4D TH KH 2H 7S 3H +4H 7S KS 2S JS TS 8S 2H QD 8D +5S 6H JH KS 8H 2S QC AC 6S 3S +JC AS AD QS 8H 6C KH 4C 4D QD +2S 3D TS TD 9S KS 6S QS 5C 8D +3C 6D 4S QC KC JH QD TH KH AD +9H AH 4D KS 2S 8D JH JC 7C QS +2D 6C TH 3C 8H QD QH 2S 3S KS +6H 5D 9S 4C TS TD JS QD 9D JD +5H 8H KH 8S KS 7C TD AD 4S KD +2C 7C JC 5S AS 6C 7D 8S 5H 9C +6S QD 9S TS KH QS 5S QH 3C KC +7D 3H 3C KD 5C AS JH 7H 6H JD +9D 5C 9H KC 8H KS 4S AD 4D 2S +3S JD QD 8D 2S 7C 5S 6S 5H TS +6D 9S KC TD 3S 6H QD JD 5C 8D +5H 9D TS KD 8D 6H TD QC 4C 7D +6D 4S JD 9D AH 9S AS TD 9H QD +2D 5S 2H 9C 6H 9S TD QC 7D TC +3S 2H KS TS 2C 9C 8S JS 9D 7D +3C KC 6D 5D 6C 6H 8S AS 7S QS +JH 9S 2H 8D 4C 8H 9H AD TH KH +QC AS 2S JS 5C 6H KD 3H 7H 2C +QD 8H 2S 8D 3S 6D AH 2C TC 5C +JD JS TS 8S 3H 5D TD KC JC 6H +6S QS TC 3H 5D AH JC 7C 7D 4H +7C 5D 8H 9C 2H 9H JH KH 5S 2C +9C 7H 6S TH 3S QC QD 4C AC JD +2H 5D 9S 7D KC 3S QS 2D AS KH +2S 4S 2H 7D 5C TD TH QH 9S 4D +6D 3S TS 6H 4H KS 9D 8H 5S 2D +9H KS 4H 3S 5C 5D KH 6H 6S JS +KC AS 8C 4C JC KH QC TH QD AH +6S KH 9S 2C 5H TC 3C 7H JC 4D +JD 4S 6S 5S 8D 7H 7S 4D 4C 2H +7H 9H 5D KH 9C 7C TS TC 7S 5H +4C 8D QC TS 4S 9H 3D AD JS 7C +8C QS 5C 5D 3H JS AH KC 4S 9D +TS JD 8S QS TH JH KH 2D QD JS +JD QC 5D 6S 9H 3S 2C 8H 9S TS +2S 4C AD 7H JC 5C 2D 6D 4H 3D +7S JS 2C 4H 8C AD QD 9C 3S TD +JD TS 4C 6H 9H 7D QD 6D 3C AS +AS 7C 4C 6S 5D 5S 5C JS QC 4S +KD 6S 9S 7C 3C 5S 7D JH QD JS +4S 7S JH 2C 8S 5D 7H 3D QH AD +TD 6H 2H 8D 4H 2D 7C AD KH 5D +TS 3S 5H 2C QD AH 2S 5C KH TD +KC 4D 8C 5D AS 6C 2H 2S 9H 7C +KD JS QC TS QS KH JH 2C 5D AD +3S 5H KC 6C 9H 3H 2H AD 7D 7S +7S JS JH KD 8S 7D 2S 9H 7C 2H +9H 2D 8D QC 6S AD AS 8H 5H 6C +2S 7H 6C 6D 7D 8C 5D 9D JC 3C +7C 9C 7H JD 2H KD 3S KH AD 4S +QH AS 9H 4D JD KS KD TS KH 5H +4C 8H 5S 3S 3D 7D TD AD 7S KC +JS 8S 5S JC 8H TH 9C 4D 5D KC +7C 5S 9C QD 2C QH JS 5H 8D KH +TD 2S KS 3D AD KC 7S TC 3C 5D +4C 2S AD QS 6C 9S QD TH QH 5C +8C AD QS 2D 2S KC JD KS 6C JC +8D 4D JS 2H 5D QD 7S 7D QH TS +6S 7H 3S 8C 8S 9D QS 8H 6C 9S +4S TC 2S 5C QD 4D QS 6D TH 6S +3S 5C 9D 6H 8D 4C 7D TC 7C TD +AH 6S AS 7H 5S KD 3H 5H AC 4C +8D 8S AH KS QS 2C AD 6H 7D 5D +6H 9H 9S 2H QS 8S 9C 5D 2D KD +TS QC 5S JH 7D 7S TH 9S 9H AC +7H 3H 6S KC 4D 6D 5C 4S QD TS +TD 2S 7C QD 3H JH 9D 4H 7S 7H +KS 3D 4H 5H TC 2S AS 2D 6D 7D +8H 3C 7H TD 3H AD KC TH 9C KH +TC 4C 2C 9S 9D 9C 5C 2H JD 3C +3H AC TS 5D AD 8D 6H QC 6S 8C +2S TS 3S JD 7H 8S QH 4C 5S 8D +AC 4S 6C 3C KH 3D 7C 2D 8S 2H +4H 6C 8S TH 2H 4S 8H 9S 3H 7S +7C 4C 9C 2C 5C AS 5D KD 4D QH +9H 4H TS AS 7D 8D 5D 9S 8C 2H +QC KD AC AD 2H 7S AS 3S 2D 9S +2H QC 8H TC 6D QD QS 5D KH 3C +TH JD QS 4C 2S 5S AD 7H 3S AS +7H JS 3D 6C 3S 6D AS 9S AC QS +9C TS AS 8C TC 8S 6H 9D 8D 6C +4D JD 9C KC 7C 6D KS 3S 8C AS +3H 6S TC 8D TS 3S KC 9S 7C AS +8C QC 4H 4S 8S 6C 3S TC AH AC +4D 7D 5C AS 2H 6S TS QC AD TC +QD QC 8S 4S TH 3D AH TS JH 4H +5C 2D 9S 2C 3H 3C 9D QD QH 7D +KC 9H 6C KD 7S 3C 4D AS TC 2D +3D JS 4D 9D KS 7D TH QC 3H 3C +8D 5S 2H 9D 3H 8C 4C 4H 3C TH +JC TH 4S 6S JD 2D 4D 6C 3D 4C +TS 3S 2D 4H AC 2C 6S 2H JH 6H +TD 8S AD TC AH AC JH 9S 6S 7S +6C KC 4S JD 8D 9H 5S 7H QH AH +KD 8D TS JH 5C 5H 3H AD AS JS +2D 4H 3D 6C 8C 7S AD 5D 5C 8S +TD 5D 7S 9C 4S 5H 6C 8C 4C 8S +JS QH 9C AS 5C QS JC 3D QC 7C +JC 9C KH JH QS QC 2C TS 3D AD +5D JH AC 5C 9S TS 4C JD 8C KS +KC AS 2D KH 9H 2C 5S 4D 3D 6H +TH AH 2D 8S JC 3D 8C QH 7S 3S +8H QD 4H JC AS KH KS 3C 9S 6D +9S QH 7D 9C 4S AC 7H KH 4D KD +AH AD TH 6D 9C 9S KD KS QH 4H +QD 6H 9C 7C QS 6D 6S 9D 5S JH +AH 8D 5H QD 2H JC KS 4H KH 5S +5C 2S JS 8D 9C 8C 3D AS KC AH +JD 9S 2H QS 8H 5S 8C TH 5C 4C +QC QS 8C 2S 2C 3S 9C 4C KS KH +2D 5D 8S AH AD TD 2C JS KS 8C +TC 5S 5H 8H QC 9H 6H JD 4H 9S +3C JH 4H 9H AH 4S 2H 4C 8D AC +8S TH 4D 7D 6D QD QS 7S TC 7C +KH 6D 2D JD 5H JS QD JH 4H 4S +9C 7S JH 4S 3S TS QC 8C TC 4H +QH 9D 4D JH QS 3S 2C 7C 6C 2D +4H 9S JD 5C 5H AH 9D TS 2D 4C +KS JH TS 5D 2D AH JS 7H AS 8D +JS AH 8C AD KS 5S 8H 2C 6C TH +2H 5D AD AC KS 3D 8H TS 6H QC +6D 4H TS 9C 5H JS JH 6S JD 4C +JH QH 4H 2C 6D 3C 5D 4C QS KC +6H 4H 6C 7H 6S 2S 8S KH QC 8C +3H 3D 5D KS 4H TD AD 3S 4D TS +5S 7C 8S 7D 2C KS 7S 6C 8C JS +5D 2H 3S 7C 5C QD 5H 6D 9C 9H +JS 2S KD 9S 8D TD TS AC 8C 9D +5H QD 2S AC 8C 9H KS 7C 4S 3C +KH AS 3H 8S 9C JS QS 4S AD 4D +AS 2S TD AD 4D 9H JC 4C 5H QS +5D 7C 4H TC 2D 6C JS 4S KC 3S +4C 2C 5D AC 9H 3D JD 8S QS QH +2C 8S 6H 3C QH 6D TC KD AC AH +QC 6C 3S QS 4S AC 8D 5C AD KH +5S 4C AC KH AS QC 2C 5C 8D 9C +8H JD 3C KH 8D 5C 9C QD QH 9D +7H TS 2C 8C 4S TD JC 9C 5H QH +JS 4S 2C 7C TH 6C AS KS 7S JD +JH 7C 9H 7H TC 5H 3D 6D 5D 4D +2C QD JH 2H 9D 5S 3D TD AD KS +JD QH 3S 4D TH 7D 6S QS KS 4H +TC KS 5S 8D 8H AD 2S 2D 4C JH +5S JH TC 3S 2D QS 9D 4C KD 9S +AC KH 3H AS 9D KC 9H QD 6C 6S +9H 7S 3D 5C 7D KC TD 8H 4H 6S +3C 7H 8H TC QD 4D 7S 6S QH 6C +6D AD 4C QD 6C 5D 7D 9D KS TS +JH 2H JD 9S 7S TS KH 8D 5D 8H +2D 9S 4C 7D 9D 5H QD 6D AC 6S +7S 6D JC QD JH 4C 6S QS 2H 7D +8C TD JH KD 2H 5C QS 2C JS 7S +TC 5H 4H JH QD 3S 5S 5D 8S KH +KS KH 7C 2C 5D JH 6S 9C 6D JC +5H AH JD 9C JS KC 2H 6H 4D 5S +AS 3C TH QC 6H 9C 8S 8C TD 7C +KC 2C QD 9C KH 4D 7S 3C TS 9H +9C QC 2S TS 8C TD 9S QD 3S 3C +4D 9D TH JH AH 6S 2S JD QH JS +QD 9H 6C KD 7D 7H 5D 6S 8H AH +8H 3C 4S 2H 5H QS QH 7S 4H AC +QS 3C 7S 9S 4H 3S AH KS 9D 7C +AD 5S 6S 2H 2D 5H TC 4S 3C 8C +QH TS 6S 4D JS KS JH AS 8S 6D +2C 8S 2S TD 5H AS TC TS 6C KC +KC TS 8H 2H 3H 7C 4C 5S TH TD +KD AD KH 7H 7S 5D 5H 5S 2D 9C +AD 9S 3D 7S 8C QC 7C 9C KD KS +3C QC 9S 8C 4D 5C AS QD 6C 2C +2H KC 8S JD 7S AC 8D 5C 2S 4D +9D QH 3D 2S TC 3S KS 3C 9H TD +KD 6S AC 2C 7H 5H 3S 6C 6H 8C +QH TC 8S 6S KH TH 4H 5D TS 4D +8C JS 4H 6H 2C 2H 7D AC QD 3D +QS KC 6S 2D 5S 4H TD 3H JH 4C +7S 5H 7H 8H KH 6H QS TH KD 7D +5H AD KD 7C KH 5S TD 6D 3C 6C +8C 9C 5H JD 7C KC KH 7H 2H 3S +7S 4H AD 4D 8S QS TH 3D 7H 5S +8D TC KS KD 9S 6D AD JD 5C 2S +7H 8H 6C QD 2H 6H 9D TC 9S 7C +8D 6D 4C 7C 6C 3C TH KH JS JH +5S 3S 8S JS 9H AS AD 8H 7S KD +JH 7C 2C KC 5H AS AD 9C 9S JS +AD AC 2C 6S QD 7C 3H TH KS KD +9D JD 4H 8H 4C KH 7S TS 8C KC +3S 5S 2H 7S 6H 7D KS 5C 6D AD +5S 8C 9H QS 7H 7S 2H 6C 7D TD +QS 5S TD AC 9D KC 3D TC 2D 4D +TD 2H 7D JD QD 4C 7H 5D KC 3D +4C 3H 8S KD QH 5S QC 9H TC 5H +9C QD TH 5H TS 5C 9H AH QH 2C +4D 6S 3C AC 6C 3D 2C 2H TD TH +AC 9C 5D QC 4D AD 8D 6D 8C KC +AD 3C 4H AC 8D 8H 7S 9S TD JC +4H 9H QH JS 2D TH TD TC KD KS +5S 6S 9S 8D TH AS KH 5H 5C 8S +JD 2S 9S 6S 5S 8S 5D 7S 7H 9D +5D 8C 4C 9D AD TS 2C 7D KD TC +8S QS 4D KC 5C 8D 4S KH JD KD +AS 5C AD QH 7D 2H 9S 7H 7C TC +2S 8S JD KH 7S 6C 6D AD 5D QC +9H 6H 3S 8C 8H AH TC 4H JS TD +2C TS 4D 7H 2D QC 9C 5D TH 7C +6C 8H QC 5D TS JH 5C 5H 9H 4S +2D QC 7H AS JS 8S 2H 4C 4H 8D +JS 6S AC KD 3D 3C 4S 7H TH KC +QH KH 6S QS 5S 4H 3C QD 3S 3H +7H AS KH 8C 4H 9C 5S 3D 6S TS +9C 7C 3H 5S QD 2C 3D AD AC 5H +JH TD 2D 4C TS 3H KH AD 3S 7S +AS 4C 5H 4D 6S KD JC 3C 6H 2D +3H 6S 8C 2D TH 4S AH QH AD 5H +7C 2S 9H 7H KC 5C 6D 5S 3H JC +3C TC 9C 4H QD TD JH 6D 9H 5S +7C 6S 5C 5D 6C 4S 7H 9H 6H AH +AD 2H 7D KC 2C 4C 2S 9S 7H 3S +TH 4C 8S 6S 3S AD KS AS JH TD +5C TD 4S 4D AD 6S 5D TC 9C 7D +8H 3S 4D 4S 5S 6H 5C AC 3H 3D +9H 3C AC 4S QS 8S 9D QH 5H 4D +JC 6C 5H TS AC 9C JD 8C 7C QD +8S 8H 9C JD 2D QC QH 6H 3C 8D +KS JS 2H 6H 5H QH QS 3H 7C 6D +TC 3H 4S 7H QC 2H 3S 8C JS KH +AH 8H 5S 4C 9H JD 3H 7S JC AC +3C 2D 4C 5S 6C 4S QS 3S JD 3D +5H 2D TC AH KS 6D 7H AD 8C 6H +6C 7S 3C JD 7C 8H KS KH AH 6D +AH 7D 3H 8H 8S 7H QS 5H 9D 2D +JD AC 4H 7S 8S 9S KS AS 9D QH +7S 2C 8S 5S JH QS JC AH KD 4C +AH 2S 9H 4H 8D TS TD 6H QH JD +4H JC 3H QS 6D 7S 9C 8S 9D 8D +5H TD 4S 9S 4C 8C 8D 7H 3H 3D +QS KH 3S 2C 2S 3C 7S TD 4S QD +7C TD 4D 5S KH AC AS 7H 4C 6C +2S 5H 6D JD 9H QS 8S 2C 2H TD +2S TS 6H 9H 7S 4H JC 4C 5D 5S +2C 5H 7D 4H 3S QH JC JS 6D 8H +4C QH 7C QD 3S AD TH 8S 5S TS +9H TC 2S TD JC 7D 3S 3D TH QH +7D 4C 8S 5C JH 8H 6S 3S KC 3H +JC 3H KH TC QH TH 6H 2C AC 5H +QS 2H 9D 2C AS 6S 6C 2S 8C 8S +9H 7D QC TH 4H KD QS AC 7S 3C +4D JH 6S 5S 8H KS 9S QC 3S AS +JD 2D 6S 7S TC 9H KC 3H 7D KD +2H KH 7C 4D 4S 3H JS QD 7D KC +4C JC AS 9D 3C JS 6C 8H QD 4D +AH JS 3S 6C 4C 3D JH 6D 9C 9H +9H 2D 8C 7H 5S KS 6H 9C 2S TC +6C 8C AD 7H 6H 3D KH AS 5D TH +KS 8C 3S TS 8S 4D 5S 9S 6C 4H +9H 4S 4H 5C 7D KC 2D 2H 9D JH +5C JS TC 9D 9H 5H 7S KH JC 6S +7C 9H 8H 4D JC KH JD 2H TD TC +8H 6C 2H 2C KH 6H 9D QS QH 5H +AC 7D 2S 3D QD JC 2D 8D JD JH +2H JC 2D 7H 2C 3C 8D KD TD 4H +3S 4H 6D 8D TS 3H TD 3D 6H TH +JH JC 3S AC QH 9H 7H 8S QC 2C +7H TD QS 4S 8S 9C 2S 5D 4D 2H +3D TS 3H 2S QC 8H 6H KC JC KS +5D JD 7D TC 8C 6C 9S 3D 8D AC +8H 6H JH 6C 5D 8D 8S 4H AD 2C +9D 4H 2D 2C 3S TS AS TC 3C 5D +4D TH 5H KS QS 6C 4S 2H 3D AD +5C KC 6H 2C 5S 3C 4D 2D 9H 9S +JD 4C 3H TH QH 9H 5S AH 8S AC +7D 9S 6S 2H TD 9C 4H 8H QS 4C +3C 6H 5D 4H 8C 9C KC 6S QD QS +3S 9H KD TC 2D JS 8C 6S 4H 4S +2S 4C 8S QS 6H KH 3H TH 8C 5D +2C KH 5S 3S 7S 7H 6C 9D QD 8D +8H KS AC 2D KH TS 6C JS KC 7H +9C KS 5C TD QC AH 6C 5H 9S 7C +5D 4D 3H 4H 6S 7C 7S AH QD TD +2H 7D QC 6S TC TS AH 7S 9D 3H +TH 5H QD 9S KS 7S 7C 6H 8C TD +TH 2D 4D QC 5C 7D JD AH 9C 4H +4H 3H AH 8D 6H QC QH 9H 2H 2C +2D AD 4C TS 6H 7S TH 4H QS TD +3C KD 2H 3H QS JD TC QC 5D 8H +KS JC QD TH 9S KD 8D 8C 2D 9C +3C QD KD 6D 4D 8D AH AD QC 8S +8H 3S 9D 2S 3H KS 6H 4C 7C KC +TH 9S 5C 3D 7D 6H AC 7S 4D 2C +5C 3D JD 4D 2D 6D 5H 9H 4C KH +AS 7H TD 6C 2H 3D QD KS 4C 4S +JC 3C AC 7C JD JS 8H 9S QC 5D +JD 6S 5S 2H AS 8C 7D 5H JH 3D +8D TC 5S 9S 8S 3H JC 5H 7S AS +5C TD 3D 7D 4H 8D 7H 4D 5D JS +QS 9C KS TD 2S 8S 5C 2H 4H AS +TH 7S 4H 7D 3H JD KD 5D 2S KC +JD 7H 4S 8H 4C JS 6H QH 5S 4H +2C QS 8C 5S 3H QC 2S 6C QD AD +8C 3D JD TC 4H 2H AD 5S AC 2S +5D 2C JS 2D AD 9D 3D 4C 4S JH +8D 5H 5D 6H 7S 4D KS 9D TD JD +3D 6D 9C 2S AS 7D 5S 5C 8H JD +7C 8S 3S 6S 5H JD TC AD 7H 7S +2S 9D TS 4D AC 8D 6C QD JD 3H +9S KH 2C 3C AC 3D 5H 6H 8D 5D +KS 3D 2D 6S AS 4C 2S 7C 7H KH +AC 2H 3S JC 5C QH 4D 2D 5H 7S +TS AS JD 8C 6H JC 8S 5S 2C 5D +7S QH 7H 6C QC 8H 2D 7C JD 2S +2C QD 2S 2H JC 9C 5D 2D JD JH +7C 5C 9C 8S 7D 6D 8D 6C 9S JH +2C AD 6S 5H 3S KS 7S 9D KH 4C +7H 6C 2C 5C TH 9D 8D 3S QC AH +5S KC 6H TC 5H 8S TH 6D 3C AH +9C KD 4H AD TD 9S 4S 7D 6H 5D +7H 5C 5H 6D AS 4C KD KH 4H 9D +3C 2S 5C 6C JD QS 2H 9D 7D 3H +AC 2S 6S 7S JS QD 5C QS 6H AD +5H TH QC 7H TC 3S 7C 6D KC 3D +4H 3D QC 9S 8H 2C 3S JC KS 5C +4S 6S 2C 6H 8S 3S 3D 9H 3H JS +4S 8C 4D 2D 8H 9H 7D 9D AH TS +9S 2C 9H 4C 8D AS 7D 3D 6D 5S +6S 4C 7H 8C 3H 5H JC AH 9D 9C +2S 7C 5S JD 8C 3S 3D 4D 7D 6S +3C KC 4S 5D 7D 3D JD 7H 3H 4H +9C 9H 4H 4D TH 6D QD 8S 9S 7S +2H AC 8S 4S AD 8C 2C AH 7D TC +TS 9H 3C AD KS TC 3D 8C 8H JD +QC 8D 2C 3C 7D 7C JD 9H 9C 6C +AH 6S JS JH 5D AS QC 2C JD TD +9H KD 2H 5D 2D 3S 7D TC AH TS +TD 8H AS 5D AH QC AC 6S TC 5H +KS 4S 7H 4D 8D 9C TC 2H 6H 3H +3H KD 4S QD QH 3D 8H 8C TD 7S +8S JD TC AH JS QS 2D KH KS 4D +3C AD JC KD JS KH 4S TH 9H 2C +QC 5S JS 9S KS AS 7C QD 2S JD +KC 5S QS 3S 2D AC 5D 9H 8H KS +6H 9C TC AD 2C 6D 5S JD 6C 7C +QS KH TD QD 2C 3H 8S 2S QC AH +9D 9H JH TC QH 3C 2S JS 5C 7H +6C 3S 3D 2S 4S QD 2D TH 5D 2C +2D 6H 6D 2S JC QH AS 7H 4H KH +5H 6S KS AD TC TS 7C AC 4S 4H +AD 3C 4H QS 8C 9D KS 2H 2D 4D +4S 9D 6C 6D 9C AC 8D 3H 7H KD +JC AH 6C TS JD 6D AD 3S 5D QD +JC JH JD 3S 7S 8S JS QC 3H 4S +JD TH 5C 2C AD JS 7H 9S 2H 7S +8D 3S JH 4D QC AS JD 2C KC 6H +2C AC 5H KD 5S 7H QD JH AH 2D +JC QH 8D 8S TC 5H 5C AH 8C 6C +3H JS 8S QD JH 3C 4H 6D 5C 3S +6D 4S 4C AH 5H 5S 3H JD 7C 8D +8H AH 2H 3H JS 3C 7D QC 4H KD +6S 2H KD 5H 8H 2D 3C 8S 7S QD +2S 7S KC QC AH TC QS 6D 4C 8D +5S 9H 2C 3S QD 7S 6C 2H 7C 9D +3C 6C 5C 5S JD JC KS 3S 5D TS +7C KS 6S 5S 2S 2D TC 2H 5H QS +AS 7H 6S TS 5H 9S 9D 3C KD 2H +4S JS QS 3S 4H 7C 2S AC 6S 9D +8C JH 2H 5H 7C 5D QH QS KH QC +3S TD 3H 7C KC 8D 5H 8S KH 8C +4H KH JD TS 3C 7H AS QC JS 5S +AH 9D 2C 8D 4D 2D 6H 6C KC 6S +2S 6H 9D 3S 7H 4D KH 8H KD 3D +9C TC AC JH KH 4D JD 5H TD 3S +7S 4H 9D AS 4C 7D QS 9S 2S KH +3S 8D 8S KS 8C JC 5C KH 2H 5D +8S QH 2C 4D KC JS QC 9D AC 6H +8S 8C 7C JS JD 6S 4C 9C AC 4S +QH 5D 2C 7D JC 8S 2D JS JH 4C +JS 4C 7S TS JH KC KH 5H QD 4S +QD 8C 8D 2D 6S TD 9D AC QH 5S +QH QC JS 3D 3C 5C 4H KH 8S 7H +7C 2C 5S JC 8S 3H QC 5D 2H KC +5S 8D KD 6H 4H QD QH 6D AH 3D +7S KS 6C 2S 4D AC QS 5H TS JD +7C 2D TC 5D QS AC JS QC 6C KC +2C KS 4D 3H TS 8S AD 4H 7S 9S +QD 9H QH 5H 4H 4D KH 3S JC AD +4D AC KC 8D 6D 4C 2D KH 2C JD +2C 9H 2D AH 3H 6D 9C 7D TC KS +8C 3H KD 7C 5C 2S 4S 5H AS AH +TH JD 4H KD 3H TC 5C 3S AC KH +6D 7H AH 7S QC 6H 2D TD JD AS +JH 5D 7H TC 9S 7D JC AS 5S KH +2H 8C AD TH 6H QD KD 9H 6S 6C +QH KC 9D 4D 3S JS JH 4H 2C 9H +TC 7H KH 4H JC 7D 9S 3H QS 7S +AD 7D JH 6C 7H 4H 3S 3H 4D QH +JD 2H 5C AS 6C QC 4D 3C TC JH +AC JD 3H 6H 4C JC AD 7D 7H 9H +4H TC TS 2C 8C 6S KS 2H JD 9S +4C 3H QS QC 9S 9H 6D KC 9D 9C +5C AD 8C 2C QH TH QD JC 8D 8H +QC 2C 2S QD 9C 4D 3S 8D JH QS +9D 3S 2C 7S 7C JC TD 3C TC 9H +3C TS 8H 5C 4C 2C 6S 8D 7C 4H +KS 7H 2H TC 4H 2C 3S AS AH QS +8C 2D 2H 2C 4S 4C 6S 7D 5S 3S +TH QC 5D TD 3C QS KD KC KS AS +4D AH KD 9H KS 5C 4C 6H JC 7S +KC 4H 5C QS TC 2H JC 9S AH QH +4S 9H 3H 5H 3C QD 2H QC JH 8H +5D AS 7H 2C 3D JH 6H 4C 6S 7D +9C JD 9H AH JS 8S QH 3H KS 8H +3S AC QC TS 4D AD 3D AH 8S 9H +7H 3H QS 9C 9S 5H JH JS AH AC +8D 3C JD 2H AC 9C 7H 5S 4D 8H +7C JH 9H 6C JS 9S 7H 8C 9D 4H +2D AS 9S 6H 4D JS JH 9H AD QD +6H 7S JH KH AH 7H TD 5S 6S 2C +8H JH 6S 5H 5S 9D TC 4C QC 9S +7D 2C KD 3H 5H AS QD 7H JS 4D +TS QH 6C 8H TH 5H 3C 3H 9C 9D +AD KH JS 5D 3H AS AC 9S 5C KC +2C KH 8C JC QS 6D AH 2D KC TC +9D 3H 2S 7C 4D 6D KH KS 8D 7D +9H 2S TC JH AC QC 3H 5S 3S 8H +3S AS KD 8H 4C 3H 7C JH QH TS +7S 6D 7H 9D JH 4C 3D 3S 6C AS +4S 2H 2C 4C 8S 5H KC 8C QC QD +3H 3S 6C QS QC 2D 6S 5D 2C 9D +2H 8D JH 2S 3H 2D 6C 5C 7S AD +9H JS 5D QH 8S TS 2H 7S 6S AD +6D QC 9S 7H 5H 5C 7D KC JD 4H +QC 5S 9H 9C 4D 6S KS 2S 4C 7C +9H 7C 4H 8D 3S 6H 5C 8H JS 7S +2D 6H JS TD 4H 4D JC TH 5H KC +AC 7C 8D TH 3H 9S 2D 4C KC 4D +KD QS 9C 7S 3D KS AD TS 4C 4H +QH 9C 8H 2S 7D KS 7H 5D KD 4C +9C 2S 2H JC 6S 6C TC QC JH 5C +7S AC 8H KC 8S 6H QS JC 3D 6S +JS 2D JH 8C 4S 6H 8H 6D 5D AD +6H 7D 2S 4H 9H 7C AS AC 8H 5S +3C JS 4S 6D 5H 2S QH 6S 9C 2C +3D 5S 6S 9S 4C QS 8D QD 8S TC +9C 3D AH 9H 5S 2C 7D AD JC 3S +7H TC AS 3C 6S 6D 7S KH KC 9H +3S TC 8H 6S 5H JH 8C 7D AC 2S +QD 9D 9C 3S JC 8C KS 8H 5D 4D +JS AH JD 6D 9D 8C 9H 9S 8H 3H +2D 6S 4C 4D 8S AD 4S TC AH 9H +TS AC QC TH KC 6D 4H 7S 8C 2H +3C QD JS 9D 5S JC AH 2H TS 9H +3H 4D QH 5D 9C 5H 7D 4S JC 3S +8S TH 3H 7C 2H JD JS TS AC 8D +9C 2H TD KC JD 2S 8C 5S AD 2C +3D KD 7C 5H 4D QH QD TC 6H 7D +7H 2C KC 5S KD 6H AH QC 7S QH +6H 5C AC 5H 2C 9C 2D 7C TD 2S +4D 9D AH 3D 7C JD 4H 8C 4C KS +TH 3C JS QH 8H 4C AS 3D QS QC +4D 7S 5H JH 6D 7D 6H JS KH 3C +QD 8S 7D 2H 2C 7C JC 2S 5H 8C +QH 8S 9D TC 2H AD 7C 8D QD 6S +3S 7C AD 9H 2H 9S JD TS 4C 2D +3S AS 4H QC 2C 8H 8S 7S TD TC +JH TH TD 3S 4D 4H 5S 5D QS 2C +8C QD QH TC 6D 4S 9S 9D 4H QC +8C JS 9D 6H JD 3H AD 6S TD QC +KC 8S 3D 7C TD 7D 8D 9H 4S 3S +6C 4S 3D 9D KD TC KC KS AC 5S +7C 6S QH 3D JS KD 6H 6D 2D 8C +JD 2S 5S 4H 8S AC 2D 6S TS 5C +5H 8C 5S 3C 4S 3D 7C 8D AS 3H +AS TS 7C 3H AD 7D JC QS 6C 6H +3S 9S 4C AC QH 5H 5D 9H TS 4H +6C 5C 7H 7S TD AD JD 5S 2H 2S +7D 6C KC 3S JD 8D 8S TS QS KH +8S QS 8D 6C TH AC AH 2C 8H 9S +7H TD KH QH 8S 3D 4D AH JD AS +TS 3D 2H JC 2S JH KH 6C QC JS +KC TH 2D 6H 7S 2S TC 8C 9D QS +3C 9D 6S KH 8H 6D 5D TH 2C 2H +6H TC 7D AD 4D 8S TS 9H TD 7S +JS 6D JD JC 2H AC 6C 3D KH 8D +KH JD 9S 5D 4H 4C 3H 7S QS 5C +4H JD 5D 3S 3C 4D KH QH QS 7S +JD TS 8S QD AH 4C 6H 3S 5S 2C +QS 3D JD AS 8D TH 7C 6S QC KS +7S 2H 8C QC 7H AC 6D 2D TH KH +5S 6C 7H KH 7D AH 8C 5C 7S 3D +3C KD AD 7D 6C 4D KS 2D 8C 4S +7C 8D 5S 2D 2S AH AD 2C 9D TD +3C AD 4S KS JH 7C 5C 8C 9C TH +AS TD 4D 7C JD 8C QH 3C 5H 9S +3H 9C 8S 9S 6S QD KS AH 5H JH +QC 9C 5S 4H 2H TD 7D AS 8C 9D +8C 2C 9D KD TC 7S 3D KH QC 3C +4D AS 4C QS 5S 9D 6S JD QH KS +6D AH 6C 4C 5H TS 9H 7D 3D 5S +QS JD 7C 8D 9C AC 3S 6S 6C KH +8H JH 5D 9S 6D AS 6S 3S QC 7H +QD AD 5C JH 2H AH 4H AS KC 2C +JH 9C 2C 6H 2D JS 5D 9H KC 6D +7D 9D KD TH 3H AS 6S QC 6H AD +JD 4H 7D KC 3H JS 3C TH 3D QS +4C 3H 8C QD 5H 6H AS 8H AD JD +TH 8S KD 5D QC 7D JS 5S 5H TS +7D KC 9D QS 3H 3C 6D TS 7S AH +7C 4H 7H AH QC AC 4D 5D 6D TH +3C 4H 2S KD 8H 5H JH TC 6C JD +4S 8C 3D 4H JS TD 7S JH QS KD +7C QC KD 4D 7H 6S AD TD TC KH +5H 9H KC 3H 4D 3D AD 6S QD 6H +TH 7C 6H TS QH 5S 2C KC TD 6S +7C 4D 5S JD JH 7D AC KD KH 4H +7D 6C 8D 8H 5C JH 8S QD TH JD +8D 7D 6C 7C 9D KD AS 5C QH JH +9S 2C 8C 3C 4C KS JH 2D 8D 4H +7S 6C JH KH 8H 3H 9D 2D AH 6D +4D TC 9C 8D 7H TD KS TH KD 3C +JD 9H 8D QD AS KD 9D 2C 2S 9C +8D 3H 5C 7H KS 5H QH 2D 8C 9H +2D TH 6D QD 6C KC 3H 3S AD 4C +4H 3H JS 9D 3C TC 5H QH QC JC +3D 5C 6H 3S 3C JC 5S 7S 2S QH +AC 5C 8C 4D 5D 4H 2S QD 3C 3H +2C TD AH 9C KD JS 6S QD 4C QC +QS 8C 3S 4H TC JS 3H 7C JC AD +5H 4D 9C KS JC TD 9S TS 8S 9H +QD TS 7D AS AC 2C TD 6H 8H AH +6S AD 8C 4S 9H 8D 9D KH 8S 3C +QS 4D 2D 7S KH JS JC AD 4C 3C +QS 9S 7H KC TD TH 5H JS AC JH +6D AC 2S QS 7C AS KS 6S KH 5S +6D 8H KH 3C QS 2H 5C 9C 9D 6C +JS 2C 4C 6H 7D JC AC QD TD 3H +4H QC 8H JD 4C KD KS 5C KC 7S +6D 2D 3H 2S QD 5S 7H AS TH 6S +AS 6D 8D 2C 8S TD 8H QD JC AH +9C 9H 2D TD QH 2H 5C TC 3D 8H +KC 8S 3D KH 2S TS TC 6S 4D JH +9H 9D QS AC KC 6H 5D 4D 8D AH +9S 5C QS 4H 7C 7D 2H 8S AD JS +3D AC 9S AS 2C 2D 2H 3H JC KH +7H QH KH JD TC KS 5S 8H 4C 8D +2H 7H 3S 2S 5H QS 3C AS 9H KD +AD 3D JD 6H 5S 9C 6D AC 9S 3S +3D 5D 9C 2D AC 4S 2S AD 6C 6S +QC 4C 2D 3H 6S KC QH QD 2H JH +QC 3C 8S 4D 9S 2H 5C 8H QS QD +6D KD 6S 7H 3S KH 2H 5C JC 6C +3S 9S TC 6S 8H 2D AD 7S 8S TS +3C 6H 9C 3H 5C JC 8H QH TD QD +3C JS QD 5D TD 2C KH 9H TH AS +9S TC JD 3D 5C 5H AD QH 9H KC +TC 7H 4H 8H 3H TD 6S AC 7C 2S +QS 9D 5D 3C JC KS 4D 6C JH 2S +9S 6S 3C 7H TS 4C KD 6D 3D 9C +2D 9H AH AC 7H 2S JH 3S 7C QC +QD 9H 3C 2H AC AS 8S KD 8C KH +2D 7S TD TH 6D JD 8D 4D 2H 5S +8S QH KD JD QS JH 4D KC 5H 3S +3C KH QC 6D 8H 3S AH 7D TD 2D +5S 9H QH 4S 6S 6C 6D TS TH 7S +6C 4C 6D QS JS 9C TS 3H 8D 8S +JS 5C 7S AS 2C AH 2H AD 5S TC +KD 6C 9C 9D TS 2S JC 4H 2C QD +QS 9H TC 3H KC KS 4H 3C AD TH +KH 9C 2H KD 9D TC 7S KC JH 2D +7C 3S KC AS 8C 5D 9C 9S QH 3H +2D 8C TD 4C 2H QC 5D TC 2C 7D +KS 4D 6C QH TD KH 5D 7C AD 8D +2S 9S 8S 4C 8C 3D 6H QD 7C 7H +6C 8S QH 5H TS 5C 3C 4S 2S 2H +8S 6S 2H JC 3S 3H 9D 8C 2S 7H +QC 2C 8H 9C AC JD 4C 4H 6S 3S +3H 3S 7D 4C 9S 5H 8H JC 3D TC +QH 2S 2D 9S KD QD 9H AD 6D 9C +8D 2D KS 9S JC 4C JD KC 4S TH +KH TS 6D 4D 5C KD 5H AS 9H AD +QD JS 7C 6D 5D 5C TH 5H QH QS +9D QH KH 5H JH 4C 4D TC TH 6C +KH AS TS 9D KD 9C 7S 4D 8H 5S +KH AS 2S 7D 9D 4C TS TH AH 7C +KS 4D AC 8S 9S 8D TH QH 9D 5C +5D 5C 8C QS TC 4C 3D 3S 2C 8D +9D KS 2D 3C KC 4S 8C KH 6C JC +8H AH 6H 7D 7S QD 3C 4C 6C KC +3H 2C QH 8H AS 7D 4C 8C 4H KC +QD 5S 4H 2C TD AH JH QH 4C 8S +3H QS 5S JS 8H 2S 9H 9C 3S 2C +6H TS 7S JC QD AC TD KC 5S 3H +QH AS QS 7D JC KC 2C 4C 5C 5S +QH 3D AS JS 4H 8D 7H JC 2S 9C +5D 4D 2S 4S 9D 9C 2D QS 8H 7H +6D 7H 3H JS TS AC 2D JH 7C 8S +JH 5H KC 3C TC 5S 9H 4C 8H 9D +8S KC 5H 9H AD KS 9D KH 8D AH +JC 2H 9H KS 6S 3H QC 5H AH 9C +5C KH 5S AD 6C JC 9H QC 9C TD +5S 5D JC QH 2D KS 8H QS 2H TS +JH 5H 5S AH 7H 3C 8S AS TD KH +6H 3D JD 2C 4C KC 7S AH 6C JH +4C KS 9D AD 7S KC 7D 8H 3S 9C +7H 5C 5H 3C 8H QC 3D KH 6D JC +2D 4H 5D 7D QC AD AH 9H QH 8H +KD 8C JS 9D 3S 3C 2H 5D 6D 2S +8S 6S TS 3C 6H 8D 5S 3H TD 6C +KS 3D JH 9C 7C 9S QS 5S 4H 6H +7S 6S TH 4S KC KD 3S JC JH KS +7C 3C 2S 6D QH 2C 7S 5H 8H AH +KC 8D QD 6D KH 5C 7H 9D 3D 9C +6H 2D 8S JS 9S 2S 6D KC 7C TC +KD 9C JH 7H KC 8S 2S 7S 3D 6H +4H 9H 2D 4C 8H 7H 5S 8S 2H 8D +AD 7C 3C 7S 5S 4D 9H 3D JC KH +5D AS 7D 6D 9C JC 4C QH QS KH +KD JD 7D 3D QS QC 8S 6D JS QD +6S 8C 5S QH TH 9H AS AC 2C JD +QC KS QH 7S 3C 4C 5C KC 5D AH +6C 4H 9D AH 2C 3H KD 3D TS 5C +TD 8S QS AS JS 3H KD AC 4H KS +7D 5D TS 9H 4H 4C 9C 2H 8C QC +2C 7D 9H 4D KS 4C QH AD KD JS +QD AD AH KH 9D JS 9H JC KD JD +8S 3C 4S TS 7S 4D 5C 2S 6H 7C +JS 7S 5C KD 6D QH 8S TD 2H 6S +QH 6C TC 6H TD 4C 9D 2H QC 8H +3D TS 4D 2H 6H 6S 2C 7H 8S 6C +9H 9D JD JH 3S AH 2C 6S 3H 8S +2C QS 8C 5S 3H 2S 7D 3C AD 4S +5C QC QH AS TS 4S 6S 4C 5H JS +JH 5C TD 4C 6H JS KD KH QS 4H +TC KH JC 4D 9H 9D 8D KC 3C 8H +2H TC 8S AD 9S 4H TS 7H 2C 5C +4H 2S 6C 5S KS AH 9C 7C 8H KD +TS QH TD QS 3C JH AH 2C 8D 7D +5D KC 3H 5S AC 4S 7H QS 4C 2H +3D 7D QC KH JH 6D 6C TD TH KD +5S 8D TH 6C 9D 7D KH 8C 9S 6D +JD QS 7S QC 2S QH JC 4S KS 8D +7S 5S 9S JD KD 9C JC AD 2D 7C +4S 5H AH JH 9C 5D TD 7C 2D 6S +KC 6C 7H 6S 9C QD 5S 4H KS TD +6S 8D KS 2D TH TD 9H JD TS 3S +KH JS 4H 5D 9D TC TD QC JD TS +QS QD AC AD 4C 6S 2D AS 3H KC +4C 7C 3C TD QS 9C KC AS 8D AD +KC 7H QC 6D 8H 6S 5S AH 7S 8C +3S AD 9H JC 6D JD AS KH 6S JH +AD 3D TS KS 7H JH 2D JS QD AC +9C JD 7C 6D TC 6H 6C JC 3D 3S +QC KC 3S JC KD 2C 8D AH QS TS +AS KD 3D JD 8H 7C 8C 5C QD 6C diff --git a/project_euler/problem_054/sol1.py b/project_euler/problem_054/sol1.py new file mode 100644 index 000000000000..d2fd810d1b69 --- /dev/null +++ b/project_euler/problem_054/sol1.py @@ -0,0 +1,381 @@ +""" +Problem: https://projecteuler.net/problem=54 + +In the card game poker, a hand consists of five cards and are ranked, +from lowest to highest, in the following way: + +High Card: Highest value card. +One Pair: Two cards of the same value. +Two Pairs: Two different pairs. +Three of a Kind: Three cards of the same value. +Straight: All cards are consecutive values. +Flush: All cards of the same suit. +Full House: Three of a kind and a pair. +Four of a Kind: Four cards of the same value. +Straight Flush: All cards are consecutive values of same suit. +Royal Flush: Ten, Jack, Queen, King, Ace, in same suit. + +The cards are valued in the order: +2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace. + +If two players have the same ranked hands then the rank made up of the highest +value wins; for example, a pair of eights beats a pair of fives. +But if two ranks tie, for example, both players have a pair of queens, then highest +cards in each hand are compared; if the highest cards tie then the next highest +cards are compared, and so on. + +The file, poker.txt, contains one-thousand random hands dealt to two players. +Each line of the file contains ten cards (separated by a single space): the +first five are Player 1's cards and the last five are Player 2's cards. +You can assume that all hands are valid (no invalid characters or repeated cards), +each player's hand is in no specific order, and in each hand there is a clear winner. + +How many hands does Player 1 win? + +Resources used: +https://en.wikipedia.org/wiki/Texas_hold_%27em +https://en.wikipedia.org/wiki/List_of_poker_hands + +Similar problem on codewars: +https://www.codewars.com/kata/ranking-poker-hands +https://www.codewars.com/kata/sortable-poker-hands +""" +from __future__ import annotations + +import os + + +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. + + Attributes: (read-only) + hand: string representing the hand consisting of five cards + + Methods: + compare_with(opponent): takes in player's hand (self) and + opponent's hand (opponent) and compares both hands according to + the rules of Texas Hold'em. + Returns one of 3 strings (Win, Loss, Tie) based on whether + player's hand is better than opponent's hand. + + hand_name(): Returns a string made up of two parts: hand name + and high card. + + Supported operators: + Rich comparison operators: <, >, <=, >=, ==, != + + Supported builtin methods and functions: + list.sort(), sorted() + """ + + _HAND_NAME = [ + "High card", + "One pair", + "Two pairs", + "Three of a kind", + "Straight", + "Flush", + "Full house", + "Four of a kind", + "Straight flush", + "Royal flush", + ] + + _CARD_NAME = [ + "", # placeholder as lists are zero indexed + "One", + "Two", + "Three", + "Four", + "Five", + "Six", + "Seven", + "Eight", + "Nine", + "Ten", + "Jack", + "Queen", + "King", + "Ace", + ] + + def __init__(self, hand: str) -> None: + """ + Initialize hand. + Hand should of type str and should contain only five cards each + separated by a space. + + The cards should be of the following format: + [card value][card suit] + + The first character is the value of the card: + 2, 3, 4, 5, 6, 7, 8, 9, T(en), J(ack), Q(ueen), K(ing), A(ce) + + The second character represents the suit: + S(pades), H(earts), D(iamonds), C(lubs) + + For example: "6S 4C KC AS TH" + """ + if not isinstance(hand, str): + raise TypeError(f"Hand should be of type 'str': {hand!r}") + # split removes duplicate whitespaces so no need of strip + if len(hand.split(" ")) != 5: + raise ValueError(f"Hand should contain only 5 cards: {hand!r}") + self._hand = hand + self._first_pair = 0 + self._second_pair = 0 + self._card_values, self._card_suit = self._internal_state() + self._hand_type = self._get_hand_type() + self._high_card = self._card_values[0] + + @property + def hand(self): + """Returns the self hand""" + return self._hand + + def compare_with(self, other: "PokerHand") -> str: + """ + Determines the outcome of comparing self hand with other hand. + Returns the output as 'Win', 'Loss', 'Tie' according to the rules of + Texas Hold'em. + + Here are some examples: + >>> player = PokerHand("2H 3H 4H 5H 6H") # Stright flush + >>> opponent = PokerHand("KS AS TS QS JS") # Royal flush + >>> player.compare_with(opponent) + 'Loss' + + >>> player = PokerHand("2S AH 2H AS AC") # Full house + >>> opponent = PokerHand("2H 3H 5H 6H 7H") # Flush + >>> player.compare_with(opponent) + 'Win' + + >>> player = PokerHand("2S AH 4H 5S 6C") # High card + >>> opponent = PokerHand("AD 4C 5H 6H 2C") # High card + >>> player.compare_with(opponent) + 'Tie' + """ + # Breaking the tie works on the following order of precedence: + # 1. First pair (default 0) + # 2. Second pair (default 0) + # 3. Compare all cards in reverse order because they are sorted. + + # First pair and second pair will only be a non-zero value if the card + # type is either from the following: + # 21: Four of a kind + # 20: Full house + # 17: Three of a kind + # 16: Two pairs + # 15: One pair + if self._hand_type > other._hand_type: + return "Win" + elif self._hand_type < other._hand_type: + return "Loss" + elif self._first_pair == other._first_pair: + if self._second_pair == other._second_pair: + return self._compare_cards(other) + else: + return "Win" if self._second_pair > other._second_pair else "Loss" + return "Win" if self._first_pair > other._first_pair else "Loss" + + # This function is not part of the problem, I did it just for fun + def hand_name(self) -> str: + """ + Return the name of the hand in the following format: + 'hand name, high card' + + Here are some examples: + >>> PokerHand("KS AS TS QS JS").hand_name() + 'Royal flush' + + >>> PokerHand("2D 6D 3D 4D 5D").hand_name() + 'Straight flush, Six-high' + + >>> PokerHand("JC 6H JS JD JH").hand_name() + 'Four of a kind, Jacks' + + >>> PokerHand("3D 2H 3H 2C 2D").hand_name() + 'Full house, Twos over Threes' + + >>> PokerHand("2H 4D 3C AS 5S").hand_name() # Low ace + 'Straight, Five-high' + + Source: https://en.wikipedia.org/wiki/List_of_poker_hands + """ + name = PokerHand._HAND_NAME[self._hand_type - 14] + high = PokerHand._CARD_NAME[self._high_card] + pair1 = PokerHand._CARD_NAME[self._first_pair] + pair2 = PokerHand._CARD_NAME[self._second_pair] + if self._hand_type in [22, 19, 18]: + return name + f", {high}-high" + elif self._hand_type in [21, 17, 15]: + return name + f", {pair1}s" + elif self._hand_type in [20, 16]: + join = "over" if self._hand_type == 20 else "and" + return name + f", {pair1}s {join} {pair2}s" + elif self._hand_type == 23: + return name + else: + return name + f", {high}" + + def _compare_cards(self, other: "PokerHand") -> str: + # Enumerate gives us the index as well as the element of a list + for index, card_value in enumerate(self._card_values): + if card_value != other._card_values[index]: + return "Win" if card_value > other._card_values[index] else "Loss" + return "Tie" + + def _get_hand_type(self) -> int: + # Number representing the type of hand internally: + # 23: Royal flush + # 22: Straight flush + # 21: Four of a kind + # 20: Full house + # 19: Flush + # 18: Straight + # 17: Three of a kind + # 16: Two pairs + # 15: One pair + # 14: High card + if self._is_flush(): + if self._is_five_high_straight() or self._is_straight(): + return 23 if sum(self._card_values) == 60 else 22 + return 19 + elif self._is_five_high_straight() or self._is_straight(): + return 18 + return 14 + self._is_same_kind() + + def _is_flush(self) -> bool: + return len(self._card_suit) == 1 + + def _is_five_high_straight(self) -> bool: + # If a card is a five high straight (low ace) change the location of + # ace from the start of the list to the end. Check whether the first + # element is ace or not. (Don't want to change again) + # Five high straight (low ace): AH 2H 3S 4C 5D + # Why use sorted here? One call to this function will mutate the list to + # [5, 4, 3, 2, 14] and so for subsequent calls (which will be rare) we + # need to compare the sorted version. + # Refer test_multiple_calls_five_high_straight in test_poker_hand.py + if sorted(self._card_values) == [2, 3, 4, 5, 14]: + if self._card_values[0] == 14: + # Remember, our list is sorted in reverse order + ace_card = self._card_values.pop(0) + self._card_values.append(ace_card) + return True + return False + + def _is_straight(self) -> bool: + for i in range(4): + if self._card_values[i] - self._card_values[i + 1] != 1: + return False + return True + + def _is_same_kind(self) -> int: + # Kind Values for internal use: + # 7: Four of a kind + # 6: Full house + # 3: Three of a kind + # 2: Two pairs + # 1: One pair + # 0: False + kind = val1 = val2 = 0 + for i in range(4): + # Compare two cards at a time, if they are same increase 'kind', + # add the value of the card to val1, if it is repeating again we + # will add 2 to 'kind' as there are now 3 cards with same value. + # If we get card of different value than val1, we will do the same + # thing with val2 + if self._card_values[i] == self._card_values[i + 1]: + if not val1: + val1 = self._card_values[i] + kind += 1 + elif val1 == self._card_values[i]: + kind += 2 + elif not val2: + val2 = self._card_values[i] + kind += 1 + elif val2 == self._card_values[i]: + kind += 2 + # For consistency in hand type (look at note in _get_hand_type function) + kind = kind + 2 if kind in [4, 5] else kind + # first meaning first pair to compare in 'compare_with' + first = max(val1, val2) + second = min(val1, val2) + # If it's full house (three count pair + two count pair), make sure + # first pair is three count and if not then switch them both. + if kind == 6 and self._card_values.count(first) != 3: + first, second = second, first + self._first_pair = first + self._second_pair = second + return kind + + def _internal_state(self) -> tuple[list[int], set[str]]: + # Internal representation of hand as a list of card values and + # a set of card suit + trans: dict = {"T": "10", "J": "11", "Q": "12", "K": "13", "A": "14"} + new_hand = self._hand.translate(str.maketrans(trans)).split() + card_values = [int(card[:-1]) for card in new_hand] + card_suit = {card[-1] for card in new_hand} + return sorted(card_values, reverse=True), card_suit + + def __repr__(self): + return f'{self.__class__}("{self._hand}")' + + def __str__(self): + return self._hand + + # Rich comparison operators (used in list.sort() and sorted() builtin functions) + # Note that this is not part of the problem but another extra feature where + # if you have a list of PokerHand objects, you can sort them just through + # the builtin functions. + def __eq__(self, other): + if isinstance(other, PokerHand): + return self.compare_with(other) == "Tie" + return NotImplemented + + def __lt__(self, other): + if isinstance(other, PokerHand): + return self.compare_with(other) == "Loss" + return NotImplemented + + def __le__(self, other): + if isinstance(other, PokerHand): + return self < other or self == other + return NotImplemented + + def __gt__(self, other): + if isinstance(other, PokerHand): + return not self < other and self != other + return NotImplemented + + def __ge__(self, other): + if isinstance(other, PokerHand): + return not self < other + return NotImplemented + + def __hash__(self): + return object.__hash__(self) + + +def solution() -> int: + # Solution for problem number 54 from Project Euler + # Input from poker_hands.txt file + 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) as file_hand: + for line in file_hand: + player_hand = line[:14].strip() + opponent_hand = line[15:].strip() + player, opponent = PokerHand(player_hand), PokerHand(opponent_hand) + output = player.compare_with(opponent) + if output == "Win": + answer += 1 + return answer + + +if __name__ == "__main__": + solution() diff --git a/project_euler/problem_054/test_poker_hand.py b/project_euler/problem_054/test_poker_hand.py new file mode 100644 index 000000000000..96317fc7df33 --- /dev/null +++ b/project_euler/problem_054/test_poker_hand.py @@ -0,0 +1,228 @@ +import os +from itertools import chain +from random import randrange, shuffle + +import pytest + +from .sol1 import PokerHand + +SORTED_HANDS = ( + "4S 3H 2C 7S 5H", + "9D 8H 2C 6S 7H", + "2D 6D 9D TH 7D", + "TC 8C 2S JH 6C", + "JH 8S TH AH QH", + "TS KS 5S 9S AC", + "KD 6S 9D TH AD", + "KS 8D 4D 9S 4S", # pair + "8C 4S KH JS 4D", # pair + "QH 8H KD JH 8S", # pair + "KC 4H KS 2H 8D", # pair + "KD 4S KC 3H 8S", # pair + "AH 8S AS KC JH", # pair + "3H 4C 4H 3S 2H", # 2 pairs + "5S 5D 2C KH KH", # 2 pairs + "3C KH 5D 5S KH", # 2 pairs + "AS 3C KH AD KH", # 2 pairs + "7C 7S 3S 7H 5S", # 3 of a kind + "7C 7S KH 2H 7H", # 3 of a kind + "AC KH QH AH AS", # 3 of a kind + "2H 4D 3C AS 5S", # straight (low ace) + "3C 5C 4C 2C 6H", # straight + "6S 8S 7S 5H 9H", # straight + "JS QS 9H TS KH", # straight + "QC KH TS JS AH", # straight (high ace) + "8C 9C 5C 3C TC", # flush + "3S 8S 9S 5S KS", # flush + "4C 5C 9C 8C KC", # flush + "JH 8H AH KH QH", # flush + "3D 2H 3H 2C 2D", # full house + "2H 2C 3S 3H 3D", # full house + "KH KC 3S 3H 3D", # full house + "JC 6H JS JD JH", # 4 of a kind + "JC 7H JS JD JH", # 4 of a kind + "JC KH JS JD JH", # 4 of a kind + "2S AS 4S 5S 3S", # straight flush (low ace) + "2D 6D 3D 4D 5D", # straight flush + "5C 6C 3C 7C 4C", # straight flush + "JH 9H TH KH QH", # straight flush + "JH AH TH KH QH", # royal flush (high ace straight flush) +) + +TEST_COMPARE = ( + ("2H 3H 4H 5H 6H", "KS AS TS QS JS", "Loss"), + ("2H 3H 4H 5H 6H", "AS AD AC AH JD", "Win"), + ("AS AH 2H AD AC", "JS JD JC JH 3D", "Win"), + ("2S AH 2H AS AC", "JS JD JC JH AD", "Loss"), + ("2S AH 2H AS AC", "2H 3H 5H 6H 7H", "Win"), + ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", "Win"), + ("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C", "Win"), + ("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S", "Tie"), + ("2S 3H 4H 5S 6C", "AH AC 5H 6H AS", "Win"), + ("2S 2H 4H 5S 4C", "AH AC 5H 6H AS", "Loss"), + ("2S 2H 4H 5S 4C", "AH AC 5H 6H 7S", "Win"), + ("6S AD 7H 4S AS", "AH AC 5H 6H 7S", "Loss"), + ("2S AH 4H 5S KC", "AH AC 5H 6H 7S", "Loss"), + ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", "Loss"), + ("4S 5H 6H TS AC", "3S 5H 6H TS AC", "Win"), + ("2S AH 4H 5S 6C", "AD 4C 5H 6H 2C", "Tie"), + ("AS AH 3H AD AC", "AS AH 2H AD AC", "Win"), + ("AH AC 5H 5C QS", "AH AC 5H 5C KS", "Loss"), + ("AH AC 5H 5C QS", "KH KC 5H 5C QS", "Win"), + ("7C 7S KH 2H 7H", "3C 3S AH 2H 3H", "Win"), + ("3C 3S AH 2H 3H", "7C 7S KH 2H 7H", "Loss"), + ("6H 5H 4H 3H 2H", "5H 4H 3H 2H AH", "Win"), + ("5H 4H 3H 2H AH", "5H 4H 3H 2H AH", "Tie"), + ("5H 4H 3H 2H AH", "6H 5H 4H 3H 2H", "Loss"), + ("AH AD KS KC AC", "AH KD KH AC KC", "Win"), + ("2H 4D 3C AS 5S", "2H 4D 3C 6S 5S", "Loss"), + ("2H 3S 3C 3H 2S", "3S 3C 2S 2H 2D", "Win"), + ("4D 6D 5D 2D JH", "3S 8S 3H TC KH", "Loss"), + ("4S 6C 8S 3S 7S", "AD KS 2D 7D 7C", "Loss"), + ("6S 4C 7H 8C 3H", "5H JC AH 9D 9C", "Loss"), + ("9D 9H JH TC QH", "3C 2S JS 5C 7H", "Win"), + ("2H TC 8S AD 9S", "4H TS 7H 2C 5C", "Win"), + ("9D 3S 2C 7S 7C", "JC TD 3C TC 9H", "Loss"), +) + +TEST_FLUSH = ( + ("2H 3H 4H 5H 6H", True), + ("AS AH 2H AD AC", False), + ("2H 3H 5H 6H 7H", True), + ("KS AS TS QS JS", True), + ("8H 9H QS JS TH", False), + ("AS 3S 4S 8S 2S", True), +) + +TEST_STRAIGHT = ( + ("2H 3H 4H 5H 6H", True), + ("AS AH 2H AD AC", False), + ("2H 3H 5H 6H 7H", False), + ("KS AS TS QS JS", True), + ("8H 9H QS JS TH", True), +) + +TEST_FIVE_HIGH_STRAIGHT = ( + ("2H 4D 3C AS 5S", True, [5, 4, 3, 2, 14]), + ("2H 5D 3C AS 5S", False, [14, 5, 5, 3, 2]), + ("JH QD KC AS TS", False, [14, 13, 12, 11, 10]), + ("9D 3S 2C 7S 7C", False, [9, 7, 7, 3, 2]), +) + +TEST_KIND = ( + ("JH AH TH KH QH", 0), + ("JH 9H TH KH QH", 0), + ("JC KH JS JD JH", 7), + ("KH KC 3S 3H 3D", 6), + ("8C 9C 5C 3C TC", 0), + ("JS QS 9H TS KH", 0), + ("7C 7S KH 2H 7H", 3), + ("3C KH 5D 5S KH", 2), + ("QH 8H KD JH 8S", 1), + ("2D 6D 9D TH 7D", 0), +) + +TEST_TYPES = ( + ("JH AH TH KH QH", 23), + ("JH 9H TH KH QH", 22), + ("JC KH JS JD JH", 21), + ("KH KC 3S 3H 3D", 20), + ("8C 9C 5C 3C TC", 19), + ("JS QS 9H TS KH", 18), + ("7C 7S KH 2H 7H", 17), + ("3C KH 5D 5S KH", 16), + ("QH 8H KD JH 8S", 15), + ("2D 6D 9D TH 7D", 14), +) + + +def generate_random_hand(): + play, oppo = randrange(len(SORTED_HANDS)), randrange(len(SORTED_HANDS)) + expected = ["Loss", "Tie", "Win"][(play >= oppo) + (play > oppo)] + hand, other = SORTED_HANDS[play], SORTED_HANDS[oppo] + return hand, other, expected + + +def generate_random_hands(number_of_hands: int = 100): + return (generate_random_hand() for _ in range(number_of_hands)) + + +@pytest.mark.parametrize("hand, expected", TEST_FLUSH) +def test_hand_is_flush(hand, expected): + assert PokerHand(hand)._is_flush() == expected + + +@pytest.mark.parametrize("hand, expected", TEST_STRAIGHT) +def test_hand_is_straight(hand, expected): + assert PokerHand(hand)._is_straight() == expected + + +@pytest.mark.parametrize("hand, expected, card_values", TEST_FIVE_HIGH_STRAIGHT) +def test_hand_is_five_high_straight(hand, expected, card_values): + player = PokerHand(hand) + assert player._is_five_high_straight() == expected + assert player._card_values == card_values + + +@pytest.mark.parametrize("hand, expected", TEST_KIND) +def test_hand_is_same_kind(hand, expected): + assert PokerHand(hand)._is_same_kind() == expected + + +@pytest.mark.parametrize("hand, expected", TEST_TYPES) +def test_hand_values(hand, expected): + assert PokerHand(hand)._hand_type == expected + + +@pytest.mark.parametrize("hand, other, expected", TEST_COMPARE) +def test_compare_simple(hand, other, expected): + assert PokerHand(hand).compare_with(PokerHand(other)) == expected + + +@pytest.mark.parametrize("hand, other, expected", generate_random_hands()) +def test_compare_random(hand, other, expected): + assert PokerHand(hand).compare_with(PokerHand(other)) == expected + + +def test_hand_sorted(): + POKER_HANDS = [PokerHand(hand) for hand in SORTED_HANDS] + list_copy = POKER_HANDS.copy() + shuffle(list_copy) + user_sorted = chain(sorted(list_copy)) + for index, hand in enumerate(user_sorted): + assert hand == POKER_HANDS[index] + + +def test_custom_sort_five_high_straight(): + # Test that five high straights are compared correctly. + pokerhands = [PokerHand("2D AC 3H 4H 5S"), PokerHand("2S 3H 4H 5S 6C")] + pokerhands.sort(reverse=True) + assert pokerhands[0].__str__() == "2S 3H 4H 5S 6C" + + +def test_multiple_calls_five_high_straight(): + # Multiple calls to five_high_straight function should still return True + # and shouldn't mutate the list in every call other than the first. + pokerhand = PokerHand("2C 4S AS 3D 5C") + expected = True + expected_card_values = [5, 4, 3, 2, 14] + for _ in range(10): + assert pokerhand._is_five_high_straight() == expected + assert pokerhand._card_values == expected_card_values + + +def test_euler_project(): + # Problem number 54 from Project Euler + # Testing from poker_hands.txt file + 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) as file_hand: + for line in file_hand: + player_hand = line[:14].strip() + opponent_hand = line[15:].strip() + player, opponent = PokerHand(player_hand), PokerHand(opponent_hand) + output = player.compare_with(opponent) + if output == "Win": + answer += 1 + assert answer == 376 diff --git a/project_euler/problem_055/__init__.py b/project_euler/problem_055/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_055/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_055/sol1.py b/project_euler/problem_055/sol1.py new file mode 100644 index 000000000000..ff98de5e3539 --- /dev/null +++ b/project_euler/problem_055/sol1.py @@ -0,0 +1,81 @@ +""" +Lychrel numbers +Problem 55: https://projecteuler.net/problem=55 + +If we take 47, reverse and add, 47 + 74 = 121, which is palindromic. + +Not all numbers produce palindromes so quickly. For example, +349 + 943 = 1292, +1292 + 2921 = 4213 +4213 + 3124 = 7337 +That is, 349 took three iterations to arrive at a palindrome. + +Although no one has proved it yet, it is thought that some numbers, like 196, +never produce a palindrome. A number that never forms a palindrome through the +reverse and add process is called a Lychrel number. Due to the theoretical nature +of these numbers, and for the purpose of this problem, we shall assume that a number +is Lychrel until proven otherwise. In addition you are given that for every number +below ten-thousand, it will either (i) become a palindrome in less than fifty +iterations, or, (ii) no one, with all the computing power that exists, has managed +so far to map it to a palindrome. In fact, 10677 is the first number to be shown +to require over fifty iterations before producing a palindrome: +4668731596684224866951378664 (53 iterations, 28-digits). + +Surprisingly, there are palindromic numbers that are themselves Lychrel numbers; +the first example is 4994. +How many Lychrel numbers are there below ten-thousand? +""" + + +def is_palindrome(n: int) -> bool: + """ + Returns True if a number is palindrome. + >>> is_palindrome(12567321) + False + >>> is_palindrome(1221) + True + >>> is_palindrome(9876789) + True + """ + return str(n) == str(n)[::-1] + + +def sum_reverse(n: int) -> int: + """ + Returns the sum of n and reverse of n. + >>> sum_reverse(123) + 444 + >>> sum_reverse(3478) + 12221 + >>> sum_reverse(12) + 33 + """ + return int(n) + int(str(n)[::-1]) + + +def solution(limit: int = 10000) -> int: + """ + Returns the count of all lychrel numbers below limit. + >>> solution(10000) + 249 + >>> solution(5000) + 76 + >>> solution(1000) + 13 + """ + lychrel_nums = [] + for num in range(1, limit): + iterations = 0 + a = num + while iterations < 50: + num = sum_reverse(num) + iterations += 1 + if is_palindrome(num): + break + else: + lychrel_nums.append(a) + return len(lychrel_nums) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_056/__init__.py b/project_euler/problem_056/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_056/sol1.py b/project_euler/problem_056/sol1.py new file mode 100644 index 000000000000..8eaa6e553342 --- /dev/null +++ b/project_euler/problem_056/sol1.py @@ -0,0 +1,45 @@ +""" +Project Euler Problem 56: https://projecteuler.net/problem=56 + +A googol (10^100) is a massive number: one followed by one-hundred zeros; +100^100 is almost unimaginably large: one followed by two-hundred zeros. +Despite their size, the sum of the digits in each number is only 1. + +Considering natural numbers of the form, ab, where a, b < 100, +what is the maximum digital sum? +""" + + +def solution(a: int = 100, b: int = 100) -> int: + """ + Considering natural numbers of the form, a**b, where a, b < 100, + what is the maximum digital sum? + :param a: + :param b: + :return: + >>> solution(10,10) + 45 + + >>> solution(100,100) + 972 + + >>> solution(100,200) + 1872 + """ + + # RETURN the MAXIMUM from the list of SUMs of the list of INT converted from STR of + # BASE raised to the POWER + return max( + [ + sum([int(x) for x in str(base ** power)]) + for base in range(a) + for power in range(b) + ] + ) + + +# Tests +if __name__ == "__main__": + import doctest + + doctest.testmod() 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() = }") 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() diff --git a/project_euler/problem_062/__init__.py b/project_euler/problem_062/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_062/sol1.py b/project_euler/problem_062/sol1.py new file mode 100644 index 000000000000..83286c801301 --- /dev/null +++ b/project_euler/problem_062/sol1.py @@ -0,0 +1,62 @@ +""" +Project Euler 62 +https://projecteuler.net/problem=62 + +The cube, 41063625 (345^3), can be permuted to produce two other cubes: +56623104 (384^3) and 66430125 (405^3). In fact, 41063625 is the smallest cube +which has exactly three permutations of its digits which are also cube. + +Find the smallest cube for which exactly five permutations of its digits are +cube. +""" + +from collections import defaultdict + + +def solution(max_base: int = 5) -> int: + """ + Iterate through every possible cube and sort the cube's digits in + ascending order. Sorting maintains an ordering of the digits that allows + you to compare permutations. Store each sorted sequence of digits in a + dictionary, whose key is the sequence of digits and value is a list of + numbers that are the base of the cube. + + Once you find 5 numbers that produce the same sequence of digits, return + the smallest one, which is at index 0 since we insert each base number in + ascending order. + + >>> solution(2) + 125 + >>> solution(3) + 41063625 + """ + freqs = defaultdict(list) + num = 0 + + while True: + digits = get_digits(num) + freqs[digits].append(num) + + if len(freqs[digits]) == max_base: + base = freqs[digits][0] ** 3 + return base + + num += 1 + + +def get_digits(num: int) -> str: + """ + Computes the sorted sequence of digits of the cube of num. + + >>> get_digits(3) + '27' + >>> get_digits(99) + '027999' + >>> get_digits(123) + '0166788' + """ + return "".join(sorted(list(str(num ** 3)))) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_063/__init__.py b/project_euler/problem_063/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_063/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_063/sol1.py b/project_euler/problem_063/sol1.py new file mode 100644 index 000000000000..bfc642be28dc --- /dev/null +++ b/project_euler/problem_063/sol1.py @@ -0,0 +1,34 @@ +""" +The 5-digit number, 16807=75, is also a fifth power. Similarly, the 9-digit number, +134217728=89, is a ninth power. +How many n-digit positive integers exist which are also an nth power? +""" + +""" +The maximum base can be 9 because all n-digit numbers < 10^n. +Now 9**23 has 22 digits so the maximum power can be 22. +Using these conclusions, we will calculate the result. +""" + + +def solution(max_base: int = 10, max_power: int = 22) -> int: + """ + Returns the count of all n-digit numbers which are nth power + >>> solution(10, 22) + 49 + >>> solution(0, 0) + 0 + >>> solution(1, 1) + 0 + >>> solution(-1, -1) + 0 + """ + 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 + ) + + +if __name__ == "__main__": + print(f"{solution(10, 22) = }") 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()))}") 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() = }") diff --git a/project_euler/problem_067/__init__.py b/project_euler/problem_067/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_067/sol1.py b/project_euler/problem_067/sol1.py new file mode 100644 index 000000000000..ebfa865a9479 --- /dev/null +++ b/project_euler/problem_067/sol1.py @@ -0,0 +1,49 @@ +""" +Problem Statement: +By starting at the top of the triangle below and moving to adjacent numbers on +the row below, the maximum total from top to bottom is 23. +3 +7 4 +2 4 6 +8 5 9 3 +That is, 3 + 7 + 4 + 9 = 23. +Find the maximum total from top to bottom in triangle.txt (right click and +'Save Link/Target As...'), a 15K text file containing a triangle with +one-hundred rows. +""" +import os + + +def solution(): + """ + Finds the maximum total in a triangle as described by the problem statement + above. + + >>> solution() + 7273 + """ + script_dir = os.path.dirname(os.path.realpath(__file__)) + triangle = os.path.join(script_dir, "triangle.txt") + + with open(triangle) as f: + triangle = f.readlines() + + a = map(lambda x: x.rstrip("\r\n").split(" "), triangle) + a = list(map(lambda x: list(map(lambda y: int(y), x)), a)) + + for i in range(1, len(a)): + for j in range(len(a[i])): + if j != len(a[i - 1]): + number1 = a[i - 1][j] + else: + number1 = 0 + if j > 0: + number2 = a[i - 1][j - 1] + else: + number2 = 0 + a[i][j] += max(number1, number2) + return max(a[-1]) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_067/triangle.txt b/project_euler/problem_067/triangle.txt new file mode 100644 index 000000000000..00aa2bc6382d --- /dev/null +++ b/project_euler/problem_067/triangle.txt @@ -0,0 +1,100 @@ +59 +73 41 +52 40 09 +26 53 06 34 +10 51 87 86 81 +61 95 66 57 25 68 +90 81 80 38 92 67 73 +30 28 51 76 81 18 75 44 +84 14 95 87 62 81 17 78 58 +21 46 71 58 02 79 62 39 31 09 +56 34 35 53 78 31 81 18 90 93 15 +78 53 04 21 84 93 32 13 97 11 37 51 +45 03 81 79 05 18 78 86 13 30 63 99 95 +39 87 96 28 03 38 42 17 82 87 58 07 22 57 +06 17 51 17 07 93 09 07 75 97 95 78 87 08 53 +67 66 59 60 88 99 94 65 55 77 55 34 27 53 78 28 +76 40 41 04 87 16 09 42 75 69 23 97 30 60 10 79 87 +12 10 44 26 21 36 32 84 98 60 13 12 36 16 63 31 91 35 +70 39 06 05 55 27 38 48 28 22 34 35 62 62 15 14 94 89 86 +66 56 68 84 96 21 34 34 34 81 62 40 65 54 62 05 98 03 02 60 +38 89 46 37 99 54 34 53 36 14 70 26 02 90 45 13 31 61 83 73 47 +36 10 63 96 60 49 41 05 37 42 14 58 84 93 96 17 09 43 05 43 06 59 +66 57 87 57 61 28 37 51 84 73 79 15 39 95 88 87 43 39 11 86 77 74 18 +54 42 05 79 30 49 99 73 46 37 50 02 45 09 54 52 27 95 27 65 19 45 26 45 +71 39 17 78 76 29 52 90 18 99 78 19 35 62 71 19 23 65 93 85 49 33 75 09 02 +33 24 47 61 60 55 32 88 57 55 91 54 46 57 07 77 98 52 80 99 24 25 46 78 79 05 +92 09 13 55 10 67 26 78 76 82 63 49 51 31 24 68 05 57 07 54 69 21 67 43 17 63 12 +24 59 06 08 98 74 66 26 61 60 13 03 09 09 24 30 71 08 88 70 72 70 29 90 11 82 41 34 +66 82 67 04 36 60 92 77 91 85 62 49 59 61 30 90 29 94 26 41 89 04 53 22 83 41 09 74 90 +48 28 26 37 28 52 77 26 51 32 18 98 79 36 62 13 17 08 19 54 89 29 73 68 42 14 08 16 70 37 +37 60 69 70 72 71 09 59 13 60 38 13 57 36 09 30 43 89 30 39 15 02 44 73 05 73 26 63 56 86 12 +55 55 85 50 62 99 84 77 28 85 03 21 27 22 19 26 82 69 54 04 13 07 85 14 01 15 70 59 89 95 10 19 +04 09 31 92 91 38 92 86 98 75 21 05 64 42 62 84 36 20 73 42 21 23 22 51 51 79 25 45 85 53 03 43 22 +75 63 02 49 14 12 89 14 60 78 92 16 44 82 38 30 72 11 46 52 90 27 08 65 78 03 85 41 57 79 39 52 33 48 +78 27 56 56 39 13 19 43 86 72 58 95 39 07 04 34 21 98 39 15 39 84 89 69 84 46 37 57 59 35 59 50 26 15 93 +42 89 36 27 78 91 24 11 17 41 05 94 07 69 51 96 03 96 47 90 90 45 91 20 50 56 10 32 36 49 04 53 85 92 25 65 +52 09 61 30 61 97 66 21 96 92 98 90 06 34 96 60 32 69 68 33 75 84 18 31 71 50 84 63 03 03 19 11 28 42 75 45 45 +61 31 61 68 96 34 49 39 05 71 76 59 62 67 06 47 96 99 34 21 32 47 52 07 71 60 42 72 94 56 82 83 84 40 94 87 82 46 +01 20 60 14 17 38 26 78 66 81 45 95 18 51 98 81 48 16 53 88 37 52 69 95 72 93 22 34 98 20 54 27 73 61 56 63 60 34 63 +93 42 94 83 47 61 27 51 79 79 45 01 44 73 31 70 83 42 88 25 53 51 30 15 65 94 80 44 61 84 12 77 02 62 02 65 94 42 14 94 +32 73 09 67 68 29 74 98 10 19 85 48 38 31 85 67 53 93 93 77 47 67 39 72 94 53 18 43 77 40 78 32 29 59 24 06 02 83 50 60 66 +32 01 44 30 16 51 15 81 98 15 10 62 86 79 50 62 45 60 70 38 31 85 65 61 64 06 69 84 14 22 56 43 09 48 66 69 83 91 60 40 36 61 +92 48 22 99 15 95 64 43 01 16 94 02 99 19 17 69 11 58 97 56 89 31 77 45 67 96 12 73 08 20 36 47 81 44 50 64 68 85 40 81 85 52 09 +91 35 92 45 32 84 62 15 19 64 21 66 06 01 52 80 62 59 12 25 88 28 91 50 40 16 22 99 92 79 87 51 21 77 74 77 07 42 38 42 74 83 02 05 +46 19 77 66 24 18 05 32 02 84 31 99 92 58 96 72 91 36 62 99 55 29 53 42 12 37 26 58 89 50 66 19 82 75 12 48 24 87 91 85 02 07 03 76 86 +99 98 84 93 07 17 33 61 92 20 66 60 24 66 40 30 67 05 37 29 24 96 03 27 70 62 13 04 45 47 59 88 43 20 66 15 46 92 30 04 71 66 78 70 53 99 +67 60 38 06 88 04 17 72 10 99 71 07 42 25 54 05 26 64 91 50 45 71 06 30 67 48 69 82 08 56 80 67 18 46 66 63 01 20 08 80 47 07 91 16 03 79 87 +18 54 78 49 80 48 77 40 68 23 60 88 58 80 33 57 11 69 55 53 64 02 94 49 60 92 16 35 81 21 82 96 25 24 96 18 02 05 49 03 50 77 06 32 84 27 18 38 +68 01 50 04 03 21 42 94 53 24 89 05 92 26 52 36 68 11 85 01 04 42 02 45 15 06 50 04 53 73 25 74 81 88 98 21 67 84 79 97 99 20 95 04 40 46 02 58 87 +94 10 02 78 88 52 21 03 88 60 06 53 49 71 20 91 12 65 07 49 21 22 11 41 58 99 36 16 09 48 17 24 52 36 23 15 72 16 84 56 02 99 43 76 81 71 29 39 49 17 +64 39 59 84 86 16 17 66 03 09 43 06 64 18 63 29 68 06 23 07 87 14 26 35 17 12 98 41 53 64 78 18 98 27 28 84 80 67 75 62 10 11 76 90 54 10 05 54 41 39 66 +43 83 18 37 32 31 52 29 95 47 08 76 35 11 04 53 35 43 34 10 52 57 12 36 20 39 40 55 78 44 07 31 38 26 08 15 56 88 86 01 52 62 10 24 32 05 60 65 53 28 57 99 +03 50 03 52 07 73 49 92 66 80 01 46 08 67 25 36 73 93 07 42 25 53 13 96 76 83 87 90 54 89 78 22 78 91 73 51 69 09 79 94 83 53 09 40 69 62 10 79 49 47 03 81 30 +71 54 73 33 51 76 59 54 79 37 56 45 84 17 62 21 98 69 41 95 65 24 39 37 62 03 24 48 54 64 46 82 71 78 33 67 09 16 96 68 52 74 79 68 32 21 13 78 96 60 09 69 20 36 +73 26 21 44 46 38 17 83 65 98 07 23 52 46 61 97 33 13 60 31 70 15 36 77 31 58 56 93 75 68 21 36 69 53 90 75 25 82 39 50 65 94 29 30 11 33 11 13 96 02 56 47 07 49 02 +76 46 73 30 10 20 60 70 14 56 34 26 37 39 48 24 55 76 84 91 39 86 95 61 50 14 53 93 64 67 37 31 10 84 42 70 48 20 10 72 60 61 84 79 69 65 99 73 89 25 85 48 92 56 97 16 +03 14 80 27 22 30 44 27 67 75 79 32 51 54 81 29 65 14 19 04 13 82 04 91 43 40 12 52 29 99 07 76 60 25 01 07 61 71 37 92 40 47 99 66 57 01 43 44 22 40 53 53 09 69 26 81 07 +49 80 56 90 93 87 47 13 75 28 87 23 72 79 32 18 27 20 28 10 37 59 21 18 70 04 79 96 03 31 45 71 81 06 14 18 17 05 31 50 92 79 23 47 09 39 47 91 43 54 69 47 42 95 62 46 32 85 +37 18 62 85 87 28 64 05 77 51 47 26 30 65 05 70 65 75 59 80 42 52 25 20 44 10 92 17 71 95 52 14 77 13 24 55 11 65 26 91 01 30 63 15 49 48 41 17 67 47 03 68 20 90 98 32 04 40 68 +90 51 58 60 06 55 23 68 05 19 76 94 82 36 96 43 38 90 87 28 33 83 05 17 70 83 96 93 06 04 78 47 80 06 23 84 75 23 87 72 99 14 50 98 92 38 90 64 61 58 76 94 36 66 87 80 51 35 61 38 +57 95 64 06 53 36 82 51 40 33 47 14 07 98 78 65 39 58 53 06 50 53 04 69 40 68 36 69 75 78 75 60 03 32 39 24 74 47 26 90 13 40 44 71 90 76 51 24 36 50 25 45 70 80 61 80 61 43 90 64 11 +18 29 86 56 68 42 79 10 42 44 30 12 96 18 23 18 52 59 02 99 67 46 60 86 43 38 55 17 44 93 42 21 55 14 47 34 55 16 49 24 23 29 96 51 55 10 46 53 27 92 27 46 63 57 30 65 43 27 21 20 24 83 +81 72 93 19 69 52 48 01 13 83 92 69 20 48 69 59 20 62 05 42 28 89 90 99 32 72 84 17 08 87 36 03 60 31 36 36 81 26 97 36 48 54 56 56 27 16 91 08 23 11 87 99 33 47 02 14 44 73 70 99 43 35 33 +90 56 61 86 56 12 70 59 63 32 01 15 81 47 71 76 95 32 65 80 54 70 34 51 40 45 33 04 64 55 78 68 88 47 31 47 68 87 03 84 23 44 89 72 35 08 31 76 63 26 90 85 96 67 65 91 19 14 17 86 04 71 32 95 +37 13 04 22 64 37 37 28 56 62 86 33 07 37 10 44 52 82 52 06 19 52 57 75 90 26 91 24 06 21 14 67 76 30 46 14 35 89 89 41 03 64 56 97 87 63 22 34 03 79 17 45 11 53 25 56 96 61 23 18 63 31 37 37 47 +77 23 26 70 72 76 77 04 28 64 71 69 14 85 96 54 95 48 06 62 99 83 86 77 97 75 71 66 30 19 57 90 33 01 60 61 14 12 90 99 32 77 56 41 18 14 87 49 10 14 90 64 18 50 21 74 14 16 88 05 45 73 82 47 74 44 +22 97 41 13 34 31 54 61 56 94 03 24 59 27 98 77 04 09 37 40 12 26 87 09 71 70 07 18 64 57 80 21 12 71 83 94 60 39 73 79 73 19 97 32 64 29 41 07 48 84 85 67 12 74 95 20 24 52 41 67 56 61 29 93 35 72 69 +72 23 63 66 01 11 07 30 52 56 95 16 65 26 83 90 50 74 60 18 16 48 43 77 37 11 99 98 30 94 91 26 62 73 45 12 87 73 47 27 01 88 66 99 21 41 95 80 02 53 23 32 61 48 32 43 43 83 14 66 95 91 19 81 80 67 25 88 +08 62 32 18 92 14 83 71 37 96 11 83 39 99 05 16 23 27 10 67 02 25 44 11 55 31 46 64 41 56 44 74 26 81 51 31 45 85 87 09 81 95 22 28 76 69 46 48 64 87 67 76 27 89 31 11 74 16 62 03 60 94 42 47 09 34 94 93 72 +56 18 90 18 42 17 42 32 14 86 06 53 33 95 99 35 29 15 44 20 49 59 25 54 34 59 84 21 23 54 35 90 78 16 93 13 37 88 54 19 86 67 68 55 66 84 65 42 98 37 87 56 33 28 58 38 28 38 66 27 52 21 81 15 08 22 97 32 85 27 +91 53 40 28 13 34 91 25 01 63 50 37 22 49 71 58 32 28 30 18 68 94 23 83 63 62 94 76 80 41 90 22 82 52 29 12 18 56 10 08 35 14 37 57 23 65 67 40 72 39 93 39 70 89 40 34 07 46 94 22 20 05 53 64 56 30 05 56 61 88 27 +23 95 11 12 37 69 68 24 66 10 87 70 43 50 75 07 62 41 83 58 95 93 89 79 45 39 02 22 05 22 95 43 62 11 68 29 17 40 26 44 25 71 87 16 70 85 19 25 59 94 90 41 41 80 61 70 55 60 84 33 95 76 42 63 15 09 03 40 38 12 03 32 +09 84 56 80 61 55 85 97 16 94 82 94 98 57 84 30 84 48 93 90 71 05 95 90 73 17 30 98 40 64 65 89 07 79 09 19 56 36 42 30 23 69 73 72 07 05 27 61 24 31 43 48 71 84 21 28 26 65 65 59 65 74 77 20 10 81 61 84 95 08 52 23 70 +47 81 28 09 98 51 67 64 35 51 59 36 92 82 77 65 80 24 72 53 22 07 27 10 21 28 30 22 48 82 80 48 56 20 14 43 18 25 50 95 90 31 77 08 09 48 44 80 90 22 93 45 82 17 13 96 25 26 08 73 34 99 06 49 24 06 83 51 40 14 15 10 25 01 +54 25 10 81 30 64 24 74 75 80 36 75 82 60 22 69 72 91 45 67 03 62 79 54 89 74 44 83 64 96 66 73 44 30 74 50 37 05 09 97 70 01 60 46 37 91 39 75 75 18 58 52 72 78 51 81 86 52 08 97 01 46 43 66 98 62 81 18 70 93 73 08 32 46 34 +96 80 82 07 59 71 92 53 19 20 88 66 03 26 26 10 24 27 50 82 94 73 63 08 51 33 22 45 19 13 58 33 90 15 22 50 36 13 55 06 35 47 82 52 33 61 36 27 28 46 98 14 73 20 73 32 16 26 80 53 47 66 76 38 94 45 02 01 22 52 47 96 64 58 52 39 +88 46 23 39 74 63 81 64 20 90 33 33 76 55 58 26 10 46 42 26 74 74 12 83 32 43 09 02 73 55 86 54 85 34 28 23 29 79 91 62 47 41 82 87 99 22 48 90 20 05 96 75 95 04 43 28 81 39 81 01 28 42 78 25 39 77 90 57 58 98 17 36 73 22 63 74 51 +29 39 74 94 95 78 64 24 38 86 63 87 93 06 70 92 22 16 80 64 29 52 20 27 23 50 14 13 87 15 72 96 81 22 08 49 72 30 70 24 79 31 16 64 59 21 89 34 96 91 48 76 43 53 88 01 57 80 23 81 90 79 58 01 80 87 17 99 86 90 72 63 32 69 14 28 88 69 +37 17 71 95 56 93 71 35 43 45 04 98 92 94 84 96 11 30 31 27 31 60 92 03 48 05 98 91 86 94 35 90 90 08 48 19 33 28 68 37 59 26 65 96 50 68 22 07 09 49 34 31 77 49 43 06 75 17 81 87 61 79 52 26 27 72 29 50 07 98 86 01 17 10 46 64 24 18 56 +51 30 25 94 88 85 79 91 40 33 63 84 49 67 98 92 15 26 75 19 82 05 18 78 65 93 61 48 91 43 59 41 70 51 22 15 92 81 67 91 46 98 11 11 65 31 66 10 98 65 83 21 05 56 05 98 73 67 46 74 69 34 08 30 05 52 07 98 32 95 30 94 65 50 24 63 28 81 99 57 +19 23 61 36 09 89 71 98 65 17 30 29 89 26 79 74 94 11 44 48 97 54 81 55 39 66 69 45 28 47 13 86 15 76 74 70 84 32 36 33 79 20 78 14 41 47 89 28 81 05 99 66 81 86 38 26 06 25 13 60 54 55 23 53 27 05 89 25 23 11 13 54 59 54 56 34 16 24 53 44 06 +13 40 57 72 21 15 60 08 04 19 11 98 34 45 09 97 86 71 03 15 56 19 15 44 97 31 90 04 87 87 76 08 12 30 24 62 84 28 12 85 82 53 99 52 13 94 06 65 97 86 09 50 94 68 69 74 30 67 87 94 63 07 78 27 80 36 69 41 06 92 32 78 37 82 30 05 18 87 99 72 19 99 +44 20 55 77 69 91 27 31 28 81 80 27 02 07 97 23 95 98 12 25 75 29 47 71 07 47 78 39 41 59 27 76 13 15 66 61 68 35 69 86 16 53 67 63 99 85 41 56 08 28 33 40 94 76 90 85 31 70 24 65 84 65 99 82 19 25 54 37 21 46 33 02 52 99 51 33 26 04 87 02 08 18 96 +54 42 61 45 91 06 64 79 80 82 32 16 83 63 42 49 19 78 65 97 40 42 14 61 49 34 04 18 25 98 59 30 82 72 26 88 54 36 21 75 03 88 99 53 46 51 55 78 22 94 34 40 68 87 84 25 30 76 25 08 92 84 42 61 40 38 09 99 40 23 29 39 46 55 10 90 35 84 56 70 63 23 91 39 +52 92 03 71 89 07 09 37 68 66 58 20 44 92 51 56 13 71 79 99 26 37 02 06 16 67 36 52 58 16 79 73 56 60 59 27 44 77 94 82 20 50 98 33 09 87 94 37 40 83 64 83 58 85 17 76 53 02 83 52 22 27 39 20 48 92 45 21 09 42 24 23 12 37 52 28 50 78 79 20 86 62 73 20 59 +54 96 80 15 91 90 99 70 10 09 58 90 93 50 81 99 54 38 36 10 30 11 35 84 16 45 82 18 11 97 36 43 96 79 97 65 40 48 23 19 17 31 64 52 65 65 37 32 65 76 99 79 34 65 79 27 55 33 03 01 33 27 61 28 66 08 04 70 49 46 48 83 01 45 19 96 13 81 14 21 31 79 93 85 50 05 +92 92 48 84 59 98 31 53 23 27 15 22 79 95 24 76 05 79 16 93 97 89 38 89 42 83 02 88 94 95 82 21 01 97 48 39 31 78 09 65 50 56 97 61 01 07 65 27 21 23 14 15 80 97 44 78 49 35 33 45 81 74 34 05 31 57 09 38 94 07 69 54 69 32 65 68 46 68 78 90 24 28 49 51 45 86 35 +41 63 89 76 87 31 86 09 46 14 87 82 22 29 47 16 13 10 70 72 82 95 48 64 58 43 13 75 42 69 21 12 67 13 64 85 58 23 98 09 37 76 05 22 31 12 66 50 29 99 86 72 45 25 10 28 19 06 90 43 29 31 67 79 46 25 74 14 97 35 76 37 65 46 23 82 06 22 30 76 93 66 94 17 96 13 20 72 +63 40 78 08 52 09 90 41 70 28 36 14 46 44 85 96 24 52 58 15 87 37 05 98 99 39 13 61 76 38 44 99 83 74 90 22 53 80 56 98 30 51 63 39 44 30 91 91 04 22 27 73 17 35 53 18 35 45 54 56 27 78 48 13 69 36 44 38 71 25 30 56 15 22 73 43 32 69 59 25 93 83 45 11 34 94 44 39 92 +12 36 56 88 13 96 16 12 55 54 11 47 19 78 17 17 68 81 77 51 42 55 99 85 66 27 81 79 93 42 65 61 69 74 14 01 18 56 12 01 58 37 91 22 42 66 83 25 19 04 96 41 25 45 18 69 96 88 36 93 10 12 98 32 44 83 83 04 72 91 04 27 73 07 34 37 71 60 59 31 01 54 54 44 96 93 83 36 04 45 +30 18 22 20 42 96 65 79 17 41 55 69 94 81 29 80 91 31 85 25 47 26 43 49 02 99 34 67 99 76 16 14 15 93 08 32 99 44 61 77 67 50 43 55 87 55 53 72 17 46 62 25 50 99 73 05 93 48 17 31 70 80 59 09 44 59 45 13 74 66 58 94 87 73 16 14 85 38 74 99 64 23 79 28 71 42 20 37 82 31 23 +51 96 39 65 46 71 56 13 29 68 53 86 45 33 51 49 12 91 21 21 76 85 02 17 98 15 46 12 60 21 88 30 92 83 44 59 42 50 27 88 46 86 94 73 45 54 23 24 14 10 94 21 20 34 23 51 04 83 99 75 90 63 60 16 22 33 83 70 11 32 10 50 29 30 83 46 11 05 31 17 86 42 49 01 44 63 28 60 07 78 95 40 +44 61 89 59 04 49 51 27 69 71 46 76 44 04 09 34 56 39 15 06 94 91 75 90 65 27 56 23 74 06 23 33 36 69 14 39 05 34 35 57 33 22 76 46 56 10 61 65 98 09 16 69 04 62 65 18 99 76 49 18 72 66 73 83 82 40 76 31 89 91 27 88 17 35 41 35 32 51 32 67 52 68 74 85 80 57 07 11 62 66 47 22 67 +65 37 19 97 26 17 16 24 24 17 50 37 64 82 24 36 32 11 68 34 69 31 32 89 79 93 96 68 49 90 14 23 04 04 67 99 81 74 70 74 36 96 68 09 64 39 88 35 54 89 96 58 66 27 88 97 32 14 06 35 78 20 71 06 85 66 57 02 58 91 72 05 29 56 73 48 86 52 09 93 22 57 79 42 12 01 31 68 17 59 63 76 07 77 +73 81 14 13 17 20 11 09 01 83 08 85 91 70 84 63 62 77 37 07 47 01 59 95 39 69 39 21 99 09 87 02 97 16 92 36 74 71 90 66 33 73 73 75 52 91 11 12 26 53 05 26 26 48 61 50 90 65 01 87 42 47 74 35 22 73 24 26 56 70 52 05 48 41 31 18 83 27 21 39 80 85 26 08 44 02 71 07 63 22 05 52 19 08 20 +17 25 21 11 72 93 33 49 64 23 53 82 03 13 91 65 85 02 40 05 42 31 77 42 05 36 06 54 04 58 07 76 87 83 25 57 66 12 74 33 85 37 74 32 20 69 03 97 91 68 82 44 19 14 89 28 85 85 80 53 34 87 58 98 88 78 48 65 98 40 11 57 10 67 70 81 60 79 74 72 97 59 79 47 30 20 54 80 89 91 14 05 33 36 79 39 +60 85 59 39 60 07 57 76 77 92 06 35 15 72 23 41 45 52 95 18 64 79 86 53 56 31 69 11 91 31 84 50 44 82 22 81 41 40 30 42 30 91 48 94 74 76 64 58 74 25 96 57 14 19 03 99 28 83 15 75 99 01 89 85 79 50 03 95 32 67 44 08 07 41 62 64 29 20 14 76 26 55 48 71 69 66 19 72 44 25 14 01 48 74 12 98 07 +64 66 84 24 18 16 27 48 20 14 47 69 30 86 48 40 23 16 61 21 51 50 26 47 35 33 91 28 78 64 43 68 04 79 51 08 19 60 52 95 06 68 46 86 35 97 27 58 04 65 30 58 99 12 12 75 91 39 50 31 42 64 70 04 46 07 98 73 98 93 37 89 77 91 64 71 64 65 66 21 78 62 81 74 42 20 83 70 73 95 78 45 92 27 34 53 71 15 +30 11 85 31 34 71 13 48 05 14 44 03 19 67 23 73 19 57 06 90 94 72 57 69 81 62 59 68 88 57 55 69 49 13 07 87 97 80 89 05 71 05 05 26 38 40 16 62 45 99 18 38 98 24 21 26 62 74 69 04 85 57 77 35 58 67 91 79 79 57 86 28 66 34 72 51 76 78 36 95 63 90 08 78 47 63 45 31 22 70 52 48 79 94 15 77 61 67 68 +23 33 44 81 80 92 93 75 94 88 23 61 39 76 22 03 28 94 32 06 49 65 41 34 18 23 08 47 62 60 03 63 33 13 80 52 31 54 73 43 70 26 16 69 57 87 83 31 03 93 70 81 47 95 77 44 29 68 39 51 56 59 63 07 25 70 07 77 43 53 64 03 94 42 95 39 18 01 66 21 16 97 20 50 90 16 70 10 95 69 29 06 25 61 41 26 15 59 63 35 diff --git a/project_euler/problem_069/__init__.py b/project_euler/problem_069/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_069/sol1.py b/project_euler/problem_069/sol1.py new file mode 100644 index 000000000000..d148dd79a777 --- /dev/null +++ b/project_euler/problem_069/sol1.py @@ -0,0 +1,66 @@ +""" +Totient maximum +Problem 69: https://projecteuler.net/problem=69 + +Euler's Totient function, φ(n) [sometimes called the phi function], +is used to determine the number of numbers less than 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. + +n Relatively Prime φ(n) n/φ(n) +2 1 1 2 +3 1,2 2 1.5 +4 1,3 2 2 +5 1,2,3,4 4 1.25 +6 1,5 2 3 +7 1,2,3,4,5,6 6 1.1666... +8 1,3,5,7 4 2 +9 1,2,4,5,7,8 6 1.5 +10 1,3,7,9 4 2.5 + +It can be seen that n=6 produces a maximum n/φ(n) for n ≤ 10. + +Find the value of n ≤ 1,000,000 for which n/φ(n) is a maximum. +""" + + +def solution(n: int = 10 ** 6) -> int: + """ + Returns solution to problem. + Algorithm: + 1. Precompute φ(k) for all natural k, k <= n using product formula (wikilink below) + https://en.wikipedia.org/wiki/Euler%27s_totient_function#Euler's_product_formula + + 2. Find k/φ(k) for all k ≤ n and return the k that attains maximum + + >>> solution(10) + 6 + + >>> solution(100) + 30 + + >>> solution(9973) + 2310 + + """ + + if n <= 0: + raise ValueError("Please enter an integer greater than 0") + + phi = list(range(n + 1)) + for number in range(2, n + 1): + if phi[number] == number: + phi[number] -= 1 + for multiple in range(number * 2, n + 1, number): + phi[multiple] = (phi[multiple] // number) * (number - 1) + + answer = 1 + for number in range(1, n + 1): + if (answer / phi[answer]) < (number / phi[number]): + answer = number + + return answer + + +if __name__ == "__main__": + print(solution()) 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() = }") diff --git a/project_euler/problem_071/__init__.py b/project_euler/problem_071/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_071/sol1.py b/project_euler/problem_071/sol1.py new file mode 100644 index 000000000000..415b127e5374 --- /dev/null +++ b/project_euler/problem_071/sol1.py @@ -0,0 +1,48 @@ +""" +Ordered fractions +Problem 71 +https://projecteuler.net/problem=71 + +Consider the fraction n/d, where n and d are positive +integers. If n int: + """ + Returns the closest numerator of the fraction immediately to the + left of given fraction (numerator/denominator) from a list of reduced + proper fractions. + >>> solution() + 428570 + >>> solution(3, 7, 8) + 2 + >>> solution(6, 7, 60) + 47 + """ + max_numerator = 0 + max_denominator = 1 + + for current_denominator in range(1, limit + 1): + current_numerator = current_denominator * numerator // denominator + if current_denominator % denominator == 0: + current_numerator -= 1 + if current_numerator * max_denominator > current_denominator * max_numerator: + max_numerator = current_numerator + max_denominator = current_denominator + return max_numerator + + +if __name__ == "__main__": + print(solution(numerator=3, denominator=7, limit=1000000)) diff --git a/project_euler/problem_072/__init__.py b/project_euler/problem_072/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_072/sol1.py b/project_euler/problem_072/sol1.py new file mode 100644 index 000000000000..846396ab0f9c --- /dev/null +++ b/project_euler/problem_072/sol1.py @@ -0,0 +1,46 @@ +""" +Problem 72 Counting fractions: https://projecteuler.net/problem=72 + +Description: + +Consider the fraction, n/d, where n and d are positive integers. If n int: + """ + Returns an integer, the solution to the problem + >>> solution(10) + 31 + >>> solution(100) + 3043 + >>> solution(1_000) + 304191 + """ + + phi = [i - 1 for i in range(limit + 1)] + + for i in range(2, limit + 1): + for j in range(2 * i, limit + 1, i): + phi[j] -= phi[i] + + return sum(phi[2 : limit + 1]) + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_072/sol2.py b/project_euler/problem_072/sol2.py new file mode 100644 index 000000000000..08e92c18bb3a --- /dev/null +++ b/project_euler/problem_072/sol2.py @@ -0,0 +1,45 @@ +""" +Project Euler Problem 72: https://projecteuler.net/problem=72 + +Consider the fraction, n/d, where n and d are positive integers. If n int: + """ + Return the number of reduced proper fractions with denominator less than limit. + >>> solution(8) + 21 + >>> solution(1000) + 304191 + """ + primes = set(range(3, limit, 2)) + primes.add(2) + for p in range(3, limit, 2): + if p not in primes: + continue + primes.difference_update(set(range(p * p, limit, p))) + + phi = [float(n) for n in range(limit + 1)] + + for p in primes: + for n in range(p, limit + 1, p): + phi[n] *= 1 - 1 / p + + return int(sum(phi[2:])) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_074/__init__.py b/project_euler/problem_074/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_074/sol1.py b/project_euler/problem_074/sol1.py new file mode 100644 index 000000000000..5e6aff6f52f2 --- /dev/null +++ b/project_euler/problem_074/sol1.py @@ -0,0 +1,111 @@ +""" +Project Euler Problem 74: https://projecteuler.net/problem=74 + +The number 145 is well known for the property that the sum of the factorial of its +digits is equal to 145: + +1! + 4! + 5! = 1 + 24 + 120 = 145 + +Perhaps less well known is 169, in that it produces the longest chain of numbers that +link back to 169; it turns out that there are only three such loops that exist: + +169 → 363601 → 1454 → 169 +871 → 45361 → 871 +872 → 45362 → 872 + +It is not difficult to prove that EVERY starting number will eventually get stuck in +a loop. For example, + +69 → 363600 → 1454 → 169 → 363601 (→ 1454) +78 → 45360 → 871 → 45361 (→ 871) +540 → 145 (→ 145) + +Starting with 69 produces a chain of five non-repeating terms, but the longest +non-repeating chain with a starting number below one million is sixty terms. + +How many chains, with a starting number below one million, contain exactly sixty +non-repeating terms? +""" + + +DIGIT_FACTORIALS = { + "0": 1, + "1": 1, + "2": 2, + "3": 6, + "4": 24, + "5": 120, + "6": 720, + "7": 5040, + "8": 40320, + "9": 362880, +} + +CACHE_SUM_DIGIT_FACTORIALS = {145: 145} + +CHAIN_LENGTH_CACHE = { + 145: 0, + 169: 3, + 36301: 3, + 1454: 3, + 871: 2, + 45361: 2, + 872: 2, + 45361: 2, +} + + +def sum_digit_factorials(n: int) -> int: + """ + Return the sum of the factorial of the digits of n. + >>> sum_digit_factorials(145) + 145 + >>> sum_digit_factorials(45361) + 871 + >>> sum_digit_factorials(540) + 145 + """ + if n in CACHE_SUM_DIGIT_FACTORIALS: + return CACHE_SUM_DIGIT_FACTORIALS[n] + ret = sum([DIGIT_FACTORIALS[let] for let in str(n)]) + CACHE_SUM_DIGIT_FACTORIALS[n] = ret + return ret + + +def chain_length(n: int, previous: set = None) -> int: + """ + Calculate the length of the chain of non-repeating terms starting with n. + Previous is a set containing the previous member of the chain. + >>> chain_length(10101) + 11 + >>> chain_length(555) + 20 + >>> chain_length(178924) + 39 + """ + previous = previous or set() + if n in CHAIN_LENGTH_CACHE: + return CHAIN_LENGTH_CACHE[n] + next_number = sum_digit_factorials(n) + if next_number in previous: + CHAIN_LENGTH_CACHE[n] = 0 + return 0 + else: + previous.add(n) + ret = 1 + chain_length(next_number, previous) + CHAIN_LENGTH_CACHE[n] = ret + return ret + + +def solution(num_terms: int = 60, max_start: int = 1000000) -> int: + """ + Return the number of chains with a starting number below one million which + contain exactly n non-repeating terms. + >>> solution(10,1000) + 28 + """ + return sum(1 for i in range(1, max_start) if chain_length(i) == num_terms) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_074/sol2.py b/project_euler/problem_074/sol2.py new file mode 100644 index 000000000000..689593277a81 --- /dev/null +++ b/project_euler/problem_074/sol2.py @@ -0,0 +1,126 @@ +""" + 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. +""" + +factorial_cache = {} +factorial_sum_cache = {} + + +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) + + if a in factorial_cache: + return factorial_cache[a] + + # The case of 0! is handled separately + if a == 0: + factorial_cache[a] = 1 + else: + # use a temporary support variable to store the computation + temporary_number = a + temporary_computation = 1 + + while temporary_number > 0: + temporary_computation *= temporary_number + temporary_number -= 1 + + factorial_cache[a] = temporary_computation + return factorial_cache[a] + + +def factorial_sum(a: int) -> int: + """Function to perform the sum of the factorial + of all the digits in a + + >>> 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 + + """ 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)) + 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(10, 1000) + 26 + """ + + # 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_set = {i} + len_chain_set = 1 + last_chain_element = i + + # The new element of the chain + 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 + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + print(f"{solution()}") diff --git a/project_euler/problem_075/__init__.py b/project_euler/problem_075/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_075/sol1.py b/project_euler/problem_075/sol1.py new file mode 100644 index 000000000000..b57604d76a86 --- /dev/null +++ b/project_euler/problem_075/sol1.py @@ -0,0 +1,60 @@ +""" +Project Euler Problem 75: https://projecteuler.net/problem=75 + +It turns out that 12 cm is the smallest length of wire that can be bent to form an +integer sided right angle triangle in exactly one way, but there are many more examples. + +12 cm: (3,4,5) +24 cm: (6,8,10) +30 cm: (5,12,13) +36 cm: (9,12,15) +40 cm: (8,15,17) +48 cm: (12,16,20) + +In contrast, some lengths of wire, like 20 cm, cannot be bent to form an integer sided +right angle triangle, and other lengths allow more than one solution to be found; for +example, using 120 cm it is possible to form exactly three different integer sided +right angle triangles. + +120 cm: (30,40,50), (20,48,52), (24,45,51) + +Given that L is the length of the wire, for how many values of L ≤ 1,500,000 can +exactly one integer sided right angle triangle be formed? + +Solution: we generate all pythagorean triples using Euclid's formula and +keep track of the frequencies of the perimeters. + +Reference: https://en.wikipedia.org/wiki/Pythagorean_triple#Generating_a_triple +""" + +from collections import defaultdict +from math import gcd +from typing import DefaultDict + + +def solution(limit: int = 1500000) -> int: + """ + Return the number of values of L <= limit such that a wire of length L can be + formmed into an integer sided right angle triangle in exactly one way. + >>> solution(50) + 6 + >>> solution(1000) + 112 + >>> solution(50000) + 5502 + """ + frequencies: DefaultDict = defaultdict(int) + euclid_m = 2 + while 2 * euclid_m * (euclid_m + 1) <= limit: + for euclid_n in range((euclid_m % 2) + 1, euclid_m, 2): + if gcd(euclid_m, euclid_n) > 1: + continue + primitive_perimeter = 2 * euclid_m * (euclid_m + euclid_n) + for perimeter in range(primitive_perimeter, limit + 1, primitive_perimeter): + frequencies[perimeter] += 1 + euclid_m += 1 + return sum(1 for frequency in frequencies.values() if frequency == 1) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_076/__init__.py b/project_euler/problem_076/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_076/sol1.py b/project_euler/problem_076/sol1.py new file mode 100644 index 000000000000..60bb87089e7e --- /dev/null +++ b/project_euler/problem_076/sol1.py @@ -0,0 +1,55 @@ +""" +Counting Summations +Problem 76: https://projecteuler.net/problem=76 + +It is possible to write five as a sum in exactly six different ways: + +4 + 1 +3 + 2 +3 + 1 + 1 +2 + 2 + 1 +2 + 1 + 1 + 1 +1 + 1 + 1 + 1 + 1 + +How many different ways can one hundred be written as a sum of at least two +positive integers? +""" + + +def solution(m: int = 100) -> int: + """ + Returns the number of different ways the number m can be written as a + sum of at least two positive integers. + + >>> solution(100) + 190569291 + >>> solution(50) + 204225 + >>> solution(30) + 5603 + >>> solution(10) + 41 + >>> solution(5) + 6 + >>> solution(3) + 2 + >>> solution(2) + 1 + >>> solution(1) + 0 + """ + memo = [[0 for _ in range(m)] for _ in range(m + 1)] + for i in range(m + 1): + memo[i][0] = 1 + + for n in range(m + 1): + for k in range(1, m): + memo[n][k] += memo[n][k - 1] + if n > k: + memo[n][k] += memo[n - k - 1][k] + + return memo[m][m - 1] - 1 + + +if __name__ == "__main__": + print(solution(int(input("Enter a number: ").strip()))) diff --git a/project_euler/problem_080/__init__.py b/project_euler/problem_080/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_080/sol1.py b/project_euler/problem_080/sol1.py new file mode 100644 index 000000000000..db69d7e8451c --- /dev/null +++ b/project_euler/problem_080/sol1.py @@ -0,0 +1,37 @@ +""" +Project Euler Problem 80: https://projecteuler.net/problem=80 +Author: Sandeep Gupta +Problem statement: For the first one hundred natural numbers, find the total of +the digital sums of the first one hundred decimal digits for all the irrational +square roots. +Time: 5 October 2020, 18:30 +""" +import decimal + + +def solution() -> int: + """ + To evaluate the sum, Used decimal python module to calculate the decimal + places up to 100, the most important thing would be take calculate + a few extra places for decimal otherwise there will be rounding + error. + + >>> solution() + 40886 + """ + answer = 0 + decimal_context = decimal.Context(prec=105) + for i in range(2, 100): + number = decimal.Decimal(i) + sqrt_number = number.sqrt(decimal_context) + if len(str(sqrt_number)) > 1: + answer += int(str(sqrt_number)[0]) + sqrt_number = str(sqrt_number)[2:101] + answer += sum([int(x) for x in sqrt_number]) + return answer + + +if __name__ == "__main__": + import doctest + + doctest.testmod() 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() = }") 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() = }") 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() = }") diff --git a/project_euler/problem_091/__init__.py b/project_euler/problem_091/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_091/sol1.py b/project_euler/problem_091/sol1.py new file mode 100644 index 000000000000..6c9aa3fa6c70 --- /dev/null +++ b/project_euler/problem_091/sol1.py @@ -0,0 +1,59 @@ +""" +Project Euler Problem 91: https://projecteuler.net/problem=91 + +The points P (x1, y1) and Q (x2, y2) are plotted at integer coordinates and +are joined to the origin, O(0,0), to form ΔOPQ. + +There are exactly fourteen triangles containing a right angle that can be formed +when each coordinate lies between 0 and 2 inclusive; that is, +0 ≤ x1, y1, x2, y2 ≤ 2. + +Given that 0 ≤ x1, y1, x2, y2 ≤ 50, how many right triangles can be formed? +""" + + +from itertools import combinations, product + + +def is_right(x1: int, y1: int, x2: int, y2: int) -> bool: + """ + Check if the triangle described by P(x1,y1), Q(x2,y2) and O(0,0) is right-angled. + Note: this doesn't check if P and Q are equal, but that's handled by the use of + itertools.combinations in the solution function. + + >>> is_right(0, 1, 2, 0) + True + >>> is_right(1, 0, 2, 2) + False + """ + if x1 == y1 == 0 or x2 == y2 == 0: + return False + a_square = x1 * x1 + y1 * y1 + b_square = x2 * x2 + y2 * y2 + c_square = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + return ( + a_square + b_square == c_square + or a_square + c_square == b_square + or b_square + c_square == a_square + ) + + +def solution(limit: int = 50) -> int: + """ + Return the number of right triangles OPQ that can be formed by two points P, Q + which have both x- and y- coordinates between 0 and limit inclusive. + + >>> solution(2) + 14 + >>> solution(10) + 448 + """ + return sum( + 1 + for pt1, pt2 in combinations(product(range(limit + 1), repeat=2), 2) + if is_right(*pt1, *pt2) + ) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_097/__init__.py b/project_euler/problem_097/__init__.py new file mode 100644 index 000000000000..792d6005489e --- /dev/null +++ b/project_euler/problem_097/__init__.py @@ -0,0 +1 @@ +# diff --git a/project_euler/problem_097/sol1.py b/project_euler/problem_097/sol1.py new file mode 100644 index 000000000000..2007ef58d35a --- /dev/null +++ b/project_euler/problem_097/sol1.py @@ -0,0 +1,46 @@ +""" +The first known prime found to exceed one million digits was discovered in 1999, +and is a Mersenne prime of the form 2**6972593 − 1; it contains exactly 2,098,960 +digits. Subsequently other Mersenne primes, of the form 2**p − 1, have been found +which contain more digits. +However, in 2004 there was found a massive non-Mersenne prime which contains +2,357,207 digits: (28433 * (2 ** 7830457 + 1)). + +Find the last ten digits of this prime number. +""" + + +def solution(n: int = 10) -> str: + """ + Returns the last n digits of NUMBER. + >>> solution() + '8739992577' + >>> solution(8) + '39992577' + >>> solution(1) + '7' + >>> solution(-1) + Traceback (most recent call last): + ... + ValueError: Invalid input + >>> solution(8.3) + Traceback (most recent call last): + ... + ValueError: Invalid input + >>> solution("a") + Traceback (most recent call last): + ... + ValueError: Invalid input + """ + if not isinstance(n, int) or n < 0: + raise ValueError("Invalid input") + MODULUS = 10 ** n + NUMBER = 28433 * (pow(2, 7830457, MODULUS)) + 1 + return str(NUMBER % MODULUS) + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"{solution(10) = }") diff --git a/project_euler/problem_099/__init__.py b/project_euler/problem_099/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_099/base_exp.txt b/project_euler/problem_099/base_exp.txt new file mode 100644 index 000000000000..e4b2d01522ca --- /dev/null +++ b/project_euler/problem_099/base_exp.txt @@ -0,0 +1,1000 @@ +519432,525806 +632382,518061 +78864,613712 +466580,530130 +780495,510032 +525895,525320 +15991,714883 +960290,502358 +760018,511029 +166800,575487 +210884,564478 +555151,523163 +681146,515199 +563395,522587 +738250,512126 +923525,503780 +595148,520429 +177108,572629 +750923,511482 +440902,532446 +881418,505504 +422489,534197 +979858,501616 +685893,514935 +747477,511661 +167214,575367 +234140,559696 +940238,503122 +728969,512609 +232083,560102 +900971,504694 +688801,514772 +189664,569402 +891022,505104 +445689,531996 +119570,591871 +821453,508118 +371084,539600 +911745,504251 +623655,518600 +144361,582486 +352442,541775 +420726,534367 +295298,549387 +6530,787777 +468397,529976 +672336,515696 +431861,533289 +84228,610150 +805376,508857 +444409,532117 +33833,663511 +381850,538396 +402931,536157 +92901,604930 +304825,548004 +731917,512452 +753734,511344 +51894,637373 +151578,580103 +295075,549421 +303590,548183 +333594,544123 +683952,515042 +60090,628880 +951420,502692 +28335,674991 +714940,513349 +343858,542826 +549279,523586 +804571,508887 +260653,554881 +291399,549966 +402342,536213 +408889,535550 +40328,652524 +375856,539061 +768907,510590 +165993,575715 +976327,501755 +898500,504795 +360404,540830 +478714,529095 +694144,514472 +488726,528258 +841380,507226 +328012,544839 +22389,690868 +604053,519852 +329514,544641 +772965,510390 +492798,527927 +30125,670983 +895603,504906 +450785,531539 +840237,507276 +380711,538522 +63577,625673 +76801,615157 +502694,527123 +597706,520257 +310484,547206 +944468,502959 +121283,591152 +451131,531507 +566499,522367 +425373,533918 +40240,652665 +39130,654392 +714926,513355 +469219,529903 +806929,508783 +287970,550487 +92189,605332 +103841,599094 +671839,515725 +452048,531421 +987837,501323 +935192,503321 +88585,607450 +613883,519216 +144551,582413 +647359,517155 +213902,563816 +184120,570789 +258126,555322 +502546,527130 +407655,535678 +401528,536306 +477490,529193 +841085,507237 +732831,512408 +833000,507595 +904694,504542 +581435,521348 +455545,531110 +873558,505829 +94916,603796 +720176,513068 +545034,523891 +246348,557409 +556452,523079 +832015,507634 +173663,573564 +502634,527125 +250732,556611 +569786,522139 +216919,563178 +521815,525623 +92304,605270 +164446,576167 +753413,511364 +11410,740712 +448845,531712 +925072,503725 +564888,522477 +7062,780812 +641155,517535 +738878,512100 +636204,517828 +372540,539436 +443162,532237 +571192,522042 +655350,516680 +299741,548735 +581914,521307 +965471,502156 +513441,526277 +808682,508700 +237589,559034 +543300,524025 +804712,508889 +247511,557192 +543486,524008 +504383,526992 +326529,545039 +792493,509458 +86033,609017 +126554,589005 +579379,521481 +948026,502823 +404777,535969 +265767,554022 +266876,553840 +46631,643714 +492397,527958 +856106,506581 +795757,509305 +748946,511584 +294694,549480 +409781,535463 +775887,510253 +543747,523991 +210592,564536 +517119,525990 +520253,525751 +247926,557124 +592141,520626 +346580,542492 +544969,523902 +506501,526817 +244520,557738 +144745,582349 +69274,620858 +292620,549784 +926027,503687 +736320,512225 +515528,526113 +407549,535688 +848089,506927 +24141,685711 +9224,757964 +980684,501586 +175259,573121 +489160,528216 +878970,505604 +969546,502002 +525207,525365 +690461,514675 +156510,578551 +659778,516426 +468739,529945 +765252,510770 +76703,615230 +165151,575959 +29779,671736 +928865,503569 +577538,521605 +927555,503618 +185377,570477 +974756,501809 +800130,509093 +217016,563153 +365709,540216 +774508,510320 +588716,520851 +631673,518104 +954076,502590 +777828,510161 +990659,501222 +597799,520254 +786905,509727 +512547,526348 +756449,511212 +869787,505988 +653747,516779 +84623,609900 +839698,507295 +30159,670909 +797275,509234 +678136,515373 +897144,504851 +989554,501263 +413292,535106 +55297,633667 +788650,509637 +486748,528417 +150724,580377 +56434,632490 +77207,614869 +588631,520859 +611619,519367 +100006,601055 +528924,525093 +190225,569257 +851155,506789 +682593,515114 +613043,519275 +514673,526183 +877634,505655 +878905,505602 +1926,914951 +613245,519259 +152481,579816 +841774,507203 +71060,619442 +865335,506175 +90244,606469 +302156,548388 +399059,536557 +478465,529113 +558601,522925 +69132,620966 +267663,553700 +988276,501310 +378354,538787 +529909,525014 +161733,576968 +758541,511109 +823425,508024 +149821,580667 +269258,553438 +481152,528891 +120871,591322 +972322,501901 +981350,501567 +676129,515483 +950860,502717 +119000,592114 +392252,537272 +191618,568919 +946699,502874 +289555,550247 +799322,509139 +703886,513942 +194812,568143 +261823,554685 +203052,566221 +217330,563093 +734748,512313 +391759,537328 +807052,508777 +564467,522510 +59186,629748 +113447,594545 +518063,525916 +905944,504492 +613922,519213 +439093,532607 +445946,531981 +230530,560399 +297887,549007 +459029,530797 +403692,536075 +855118,506616 +963127,502245 +841711,507208 +407411,535699 +924729,503735 +914823,504132 +333725,544101 +176345,572832 +912507,504225 +411273,535308 +259774,555036 +632853,518038 +119723,591801 +163902,576321 +22691,689944 +402427,536212 +175769,572988 +837260,507402 +603432,519893 +313679,546767 +538165,524394 +549026,523608 +61083,627945 +898345,504798 +992556,501153 +369999,539727 +32847,665404 +891292,505088 +152715,579732 +824104,507997 +234057,559711 +730507,512532 +960529,502340 +388395,537687 +958170,502437 +57105,631806 +186025,570311 +993043,501133 +576770,521664 +215319,563513 +927342,503628 +521353,525666 +39563,653705 +752516,511408 +110755,595770 +309749,547305 +374379,539224 +919184,503952 +990652,501226 +647780,517135 +187177,570017 +168938,574877 +649558,517023 +278126,552016 +162039,576868 +658512,516499 +498115,527486 +896583,504868 +561170,522740 +747772,511647 +775093,510294 +652081,516882 +724905,512824 +499707,527365 +47388,642755 +646668,517204 +571700,522007 +180430,571747 +710015,513617 +435522,532941 +98137,602041 +759176,511070 +486124,528467 +526942,525236 +878921,505604 +408313,535602 +926980,503640 +882353,505459 +566887,522345 +3326,853312 +911981,504248 +416309,534800 +392991,537199 +622829,518651 +148647,581055 +496483,527624 +666314,516044 +48562,641293 +672618,515684 +443676,532187 +274065,552661 +265386,554079 +347668,542358 +31816,667448 +181575,571446 +961289,502320 +365689,540214 +987950,501317 +932299,503440 +27388,677243 +746701,511701 +492258,527969 +147823,581323 +57918,630985 +838849,507333 +678038,515375 +27852,676130 +850241,506828 +818403,508253 +131717,587014 +850216,506834 +904848,504529 +189758,569380 +392845,537217 +470876,529761 +925353,503711 +285431,550877 +454098,531234 +823910,508003 +318493,546112 +766067,510730 +261277,554775 +421530,534289 +694130,514478 +120439,591498 +213308,563949 +854063,506662 +365255,540263 +165437,575872 +662240,516281 +289970,550181 +847977,506933 +546083,523816 +413252,535113 +975829,501767 +361540,540701 +235522,559435 +224643,561577 +736350,512229 +328303,544808 +35022,661330 +307838,547578 +474366,529458 +873755,505819 +73978,617220 +827387,507845 +670830,515791 +326511,545034 +309909,547285 +400970,536363 +884827,505352 +718307,513175 +28462,674699 +599384,520150 +253565,556111 +284009,551093 +343403,542876 +446557,531921 +992372,501160 +961601,502308 +696629,514342 +919537,503945 +894709,504944 +892201,505051 +358160,541097 +448503,531745 +832156,507636 +920045,503924 +926137,503675 +416754,534757 +254422,555966 +92498,605151 +826833,507873 +660716,516371 +689335,514746 +160045,577467 +814642,508425 +969939,501993 +242856,558047 +76302,615517 +472083,529653 +587101,520964 +99066,601543 +498005,527503 +709800,513624 +708000,513716 +20171,698134 +285020,550936 +266564,553891 +981563,501557 +846502,506991 +334,1190800 +209268,564829 +9844,752610 +996519,501007 +410059,535426 +432931,533188 +848012,506929 +966803,502110 +983434,501486 +160700,577267 +504374,526989 +832061,507640 +392825,537214 +443842,532165 +440352,532492 +745125,511776 +13718,726392 +661753,516312 +70500,619875 +436952,532814 +424724,533973 +21954,692224 +262490,554567 +716622,513264 +907584,504425 +60086,628882 +837123,507412 +971345,501940 +947162,502855 +139920,584021 +68330,621624 +666452,516038 +731446,512481 +953350,502619 +183157,571042 +845400,507045 +651548,516910 +20399,697344 +861779,506331 +629771,518229 +801706,509026 +189207,569512 +737501,512168 +719272,513115 +479285,529045 +136046,585401 +896746,504860 +891735,505067 +684771,514999 +865309,506184 +379066,538702 +503117,527090 +621780,518717 +209518,564775 +677135,515423 +987500,501340 +197049,567613 +329315,544673 +236756,559196 +357092,541226 +520440,525733 +213471,563911 +956852,502490 +702223,514032 +404943,535955 +178880,572152 +689477,514734 +691351,514630 +866669,506128 +370561,539656 +739805,512051 +71060,619441 +624861,518534 +261660,554714 +366137,540160 +166054,575698 +601878,519990 +153445,579501 +279899,551729 +379166,538691 +423209,534125 +675310,515526 +145641,582050 +691353,514627 +917468,504026 +284778,550976 +81040,612235 +161699,576978 +616394,519057 +767490,510661 +156896,578431 +427408,533714 +254849,555884 +737217,512182 +897133,504851 +203815,566051 +270822,553189 +135854,585475 +778805,510111 +784373,509847 +305426,547921 +733418,512375 +732087,512448 +540668,524215 +702898,513996 +628057,518328 +640280,517587 +422405,534204 +10604,746569 +746038,511733 +839808,507293 +457417,530938 +479030,529064 +341758,543090 +620223,518824 +251661,556451 +561790,522696 +497733,527521 +724201,512863 +489217,528217 +415623,534867 +624610,518548 +847541,506953 +432295,533249 +400391,536421 +961158,502319 +139173,584284 +421225,534315 +579083,521501 +74274,617000 +701142,514087 +374465,539219 +217814,562985 +358972,540995 +88629,607424 +288597,550389 +285819,550812 +538400,524385 +809930,508645 +738326,512126 +955461,502535 +163829,576343 +826475,507891 +376488,538987 +102234,599905 +114650,594002 +52815,636341 +434037,533082 +804744,508880 +98385,601905 +856620,506559 +220057,562517 +844734,507078 +150677,580387 +558697,522917 +621751,518719 +207067,565321 +135297,585677 +932968,503404 +604456,519822 +579728,521462 +244138,557813 +706487,513800 +711627,513523 +853833,506674 +497220,527562 +59428,629511 +564845,522486 +623621,518603 +242689,558077 +125091,589591 +363819,540432 +686453,514901 +656813,516594 +489901,528155 +386380,537905 +542819,524052 +243987,557841 +693412,514514 +488484,528271 +896331,504881 +336730,543721 +728298,512647 +604215,519840 +153729,579413 +595687,520398 +540360,524240 +245779,557511 +924873,503730 +509628,526577 +528523,525122 +3509,847707 +522756,525555 +895447,504922 +44840,646067 +45860,644715 +463487,530404 +398164,536654 +894483,504959 +619415,518874 +966306,502129 +990922,501212 +835756,507474 +548881,523618 +453578,531282 +474993,529410 +80085,612879 +737091,512193 +50789,638638 +979768,501620 +792018,509483 +665001,516122 +86552,608694 +462772,530469 +589233,520821 +891694,505072 +592605,520594 +209645,564741 +42531,649269 +554376,523226 +803814,508929 +334157,544042 +175836,572970 +868379,506051 +658166,516520 +278203,551995 +966198,502126 +627162,518387 +296774,549165 +311803,547027 +843797,507118 +702304,514032 +563875,522553 +33103,664910 +191932,568841 +543514,524006 +506835,526794 +868368,506052 +847025,506971 +678623,515342 +876139,505726 +571997,521984 +598632,520198 +213590,563892 +625404,518497 +726508,512738 +689426,514738 +332495,544264 +411366,535302 +242546,558110 +315209,546555 +797544,509219 +93889,604371 +858879,506454 +124906,589666 +449072,531693 +235960,559345 +642403,517454 +720567,513047 +705534,513858 +603692,519870 +488137,528302 +157370,578285 +63515,625730 +666326,516041 +619226,518883 +443613,532186 +597717,520257 +96225,603069 +86940,608450 +40725,651929 +460976,530625 +268875,553508 +270671,553214 +363254,540500 +384248,538137 +762889,510892 +377941,538833 +278878,551890 +176615,572755 +860008,506412 +944392,502967 +608395,519571 +225283,561450 +45095,645728 +333798,544090 +625733,518476 +995584,501037 +506135,526853 +238050,558952 +557943,522972 +530978,524938 +634244,517949 +177168,572616 +85200,609541 +953043,502630 +523661,525484 +999295,500902 +840803,507246 +961490,502312 +471747,529685 +380705,538523 +911180,504275 +334149,544046 +478992,529065 +325789,545133 +335884,543826 +426976,533760 +749007,511582 +667067,516000 +607586,519623 +674054,515599 +188534,569675 +565185,522464 +172090,573988 +87592,608052 +907432,504424 +8912,760841 +928318,503590 +757917,511138 +718693,513153 +315141,546566 +728326,512645 +353492,541647 +638429,517695 +628892,518280 +877286,505672 +620895,518778 +385878,537959 +423311,534113 +633501,517997 +884833,505360 +883402,505416 +999665,500894 +708395,513697 +548142,523667 +756491,511205 +987352,501340 +766520,510705 +591775,520647 +833758,507563 +843890,507108 +925551,503698 +74816,616598 +646942,517187 +354923,541481 +256291,555638 +634470,517942 +930904,503494 +134221,586071 +282663,551304 +986070,501394 +123636,590176 +123678,590164 +481717,528841 +423076,534137 +866246,506145 +93313,604697 +783632,509880 +317066,546304 +502977,527103 +141272,583545 +71708,618938 +617748,518975 +581190,521362 +193824,568382 +682368,515131 +352956,541712 +351375,541905 +505362,526909 +905165,504518 +128645,588188 +267143,553787 +158409,577965 +482776,528754 +628896,518282 +485233,528547 +563606,522574 +111001,595655 +115920,593445 +365510,540237 +959724,502374 +938763,503184 +930044,503520 +970959,501956 +913658,504176 +68117,621790 +989729,501253 +567697,522288 +820427,508163 +54236,634794 +291557,549938 +124961,589646 +403177,536130 +405421,535899 +410233,535417 +815111,508403 +213176,563974 +83099,610879 +998588,500934 +513640,526263 +129817,587733 +1820,921851 +287584,550539 +299160,548820 +860621,506386 +529258,525059 +586297,521017 +953406,502616 +441234,532410 +986217,501386 +781938,509957 +461247,530595 +735424,512277 +146623,581722 +839838,507288 +510667,526494 +935085,503327 +737523,512167 +303455,548204 +992779,501145 +60240,628739 +939095,503174 +794368,509370 +501825,527189 +459028,530798 +884641,505363 +512287,526364 +835165,507499 +307723,547590 +160587,577304 +735043,512300 +493289,527887 +110717,595785 +306480,547772 +318593,546089 +179810,571911 +200531,566799 +314999,546580 +197020,567622 +301465,548487 +237808,559000 +131944,586923 +882527,505449 +468117,530003 +711319,513541 +156240,578628 +965452,502162 +992756,501148 +437959,532715 +739938,512046 +614249,519196 +391496,537356 +62746,626418 +688215,514806 +75501,616091 +883573,505412 +558824,522910 +759371,511061 +173913,573489 +891351,505089 +727464,512693 +164833,576051 +812317,508529 +540320,524243 +698061,514257 +69149,620952 +471673,529694 +159092,577753 +428134,533653 +89997,606608 +711061,513557 +779403,510081 +203327,566155 +798176,509187 +667688,515963 +636120,517833 +137410,584913 +217615,563034 +556887,523038 +667229,515991 +672276,515708 +325361,545187 +172115,573985 +13846,725685 diff --git a/project_euler/problem_099/sol1.py b/project_euler/problem_099/sol1.py new file mode 100644 index 000000000000..88912e1f0f9e --- /dev/null +++ b/project_euler/problem_099/sol1.py @@ -0,0 +1,34 @@ +""" +Problem: + +Comparing two numbers written in index form like 2'11 and 3'7 is not difficult, as any +calculator would confirm that 2^11 = 2048 < 3^7 = 2187. + +However, confirming that 632382^518061 > 519432^525806 would be much more difficult, as +both numbers contain over three million digits. + +Using base_exp.txt, a 22K text file containing one thousand lines with a base/exponent +pair on each line, determine which line number has the greatest numerical value. + +NOTE: The first two lines in the file represent the numbers in the example given above. +""" + +import os +from math import log10 + + +def solution(data_file: str = "base_exp.txt") -> int: + """ + >>> solution() + 709 + """ + largest = [0, 0] + for i, line in enumerate(open(os.path.join(os.path.dirname(__file__), data_file))): + a, x = list(map(int, line.split(","))) + if x * log10(a) > largest[0]: + largest = [x * log10(a), i + 1] + return largest[1] + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_112/__init__.py b/project_euler/problem_112/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_112/sol1.py b/project_euler/problem_112/sol1.py new file mode 100644 index 000000000000..450dc51662d6 --- /dev/null +++ b/project_euler/problem_112/sol1.py @@ -0,0 +1,89 @@ +""" +Problem 112: https://projecteuler.net/problem=112 + +Working from left-to-right if no digit is exceeded by the digit to its left it is +called an increasing number; for example, 134468. +Similarly if no digit is exceeded by the digit to its right it is called a decreasing +number; for example, 66420. +We shall call a positive integer that is neither increasing nor decreasing a "bouncy" +number, for example, 155349. +Clearly there cannot be any bouncy numbers below one-hundred, but just over half of +the numbers below one-thousand (525) are bouncy. In fact, the least number for which +the proportion of bouncy numbers first reaches 50% is 538. +Surprisingly, bouncy numbers become more and more common and by the time we reach +21780 the proportion of bouncy numbers is equal to 90%. + +Find the least number for which the proportion of bouncy numbers is exactly 99%. +""" + + +def check_bouncy(n: int) -> bool: + """ + Returns True if number is bouncy, False otherwise + >>> check_bouncy(6789) + False + >>> check_bouncy(-12345) + False + >>> check_bouncy(0) + False + >>> check_bouncy(6.74) + Traceback (most recent call last): + ... + ValueError: check_bouncy() accepts only integer arguments + >>> check_bouncy(132475) + True + >>> check_bouncy(34) + False + >>> check_bouncy(341) + True + >>> check_bouncy(47) + False + >>> check_bouncy(-12.54) + Traceback (most recent call last): + ... + ValueError: check_bouncy() accepts only integer arguments + >>> check_bouncy(-6548) + True + """ + if not isinstance(n, int): + raise ValueError("check_bouncy() accepts only integer arguments") + return "".join(sorted(str(n))) != str(n) and "".join(sorted(str(n)))[::-1] != str(n) + + +def solution(percent: float = 99) -> int: + """ + Returns the least number for which the proportion of bouncy numbers is + exactly 'percent' + >>> solution(50) + 538 + >>> solution(90) + 21780 + >>> solution(80) + 4770 + >>> solution(105) + Traceback (most recent call last): + ... + ValueError: solution() only accepts values from 0 to 100 + >>> solution(100.011) + Traceback (most recent call last): + ... + ValueError: solution() only accepts values from 0 to 100 + """ + if not 0 < percent < 100: + raise ValueError("solution() only accepts values from 0 to 100") + bouncy_num = 0 + num = 1 + + while True: + if check_bouncy(num): + bouncy_num += 1 + if (bouncy_num / num) * 100 >= percent: + return num + num += 1 + + +if __name__ == "__main__": + from doctest import testmod + + testmod() + print(f"{solution(99)}") diff --git a/project_euler/problem_113/__init__.py b/project_euler/problem_113/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_113/sol1.py b/project_euler/problem_113/sol1.py new file mode 100644 index 000000000000..951d9b49c104 --- /dev/null +++ b/project_euler/problem_113/sol1.py @@ -0,0 +1,75 @@ +""" +Project Euler Problem 113: https://projecteuler.net/problem=113 + +Working from left-to-right if no digit is exceeded by the digit to its left it is +called an increasing number; for example, 134468. + +Similarly if no digit is exceeded by the digit to its right it is called a decreasing +number; for example, 66420. + +We shall call a positive integer that is neither increasing nor decreasing a +"bouncy" number; for example, 155349. + +As n increases, the proportion of bouncy numbers below n increases such that there +are only 12951 numbers below one-million that are not bouncy and only 277032 +non-bouncy numbers below 10^10. + +How many numbers below a googol (10^100) are not bouncy? +""" + + +def choose(n: int, r: int) -> int: + """ + Calculate the binomial coefficient c(n,r) using the multiplicative formula. + >>> choose(4,2) + 6 + >>> choose(5,3) + 10 + >>> choose(20,6) + 38760 + """ + ret = 1.0 + for i in range(1, r + 1): + ret *= (n + 1 - i) / i + return round(ret) + + +def non_bouncy_exact(n: int) -> int: + """ + Calculate the number of non-bouncy numbers with at most n digits. + >>> non_bouncy_exact(1) + 9 + >>> non_bouncy_exact(6) + 7998 + >>> non_bouncy_exact(10) + 136126 + """ + return choose(8 + n, n) + choose(9 + n, n) - 10 + + +def non_bouncy_upto(n: int) -> int: + """ + Calculate the number of non-bouncy numbers with at most n digits. + >>> non_bouncy_upto(1) + 9 + >>> non_bouncy_upto(6) + 12951 + >>> non_bouncy_upto(10) + 277032 + """ + return sum(non_bouncy_exact(i) for i in range(1, n + 1)) + + +def solution(num_digits: int = 100) -> int: + """ + Caclulate the number of non-bouncy numbers less than a googol. + >>> solution(6) + 12951 + >>> solution(10) + 277032 + """ + return non_bouncy_upto(num_digits) + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_119/__init__.py b/project_euler/problem_119/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_119/sol1.py b/project_euler/problem_119/sol1.py new file mode 100644 index 000000000000..7f343ac242e9 --- /dev/null +++ b/project_euler/problem_119/sol1.py @@ -0,0 +1,51 @@ +""" +Problem 119: https://projecteuler.net/problem=119 + +Name: Digit power sum + +The number 512 is interesting because it is equal to the sum of its digits +raised to some power: 5 + 1 + 2 = 8, and 8^3 = 512. Another example of a number +with this property is 614656 = 28^4. We shall define an to be the nth term of +this sequence and insist that a number must contain at least two digits to have a sum. +You are given that a2 = 512 and a10 = 614656. Find a30 +""" + +import math + + +def digit_sum(n: int) -> int: + """ + Returns the sum of the digits of the number. + >>> digit_sum(123) + 6 + >>> digit_sum(456) + 15 + >>> digit_sum(78910) + 25 + """ + return sum([int(digit) for digit in str(n)]) + + +def solution(n: int = 30) -> int: + """ + Returns the value of 30th digit power sum. + >>> solution(2) + 512 + >>> solution(5) + 5832 + >>> solution(10) + 614656 + """ + digit_to_powers = [] + for digit in range(2, 100): + for power in range(2, 100): + number = int(math.pow(digit, power)) + if digit == digit_sum(number): + digit_to_powers.append(number) + + digit_to_powers.sort() + return digit_to_powers[n - 1] + + +if __name__ == "__main__": + print(solution()) diff --git a/project_euler/problem_120/__init__.py b/project_euler/problem_120/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_120/sol1.py b/project_euler/problem_120/sol1.py new file mode 100644 index 000000000000..0e6821214560 --- /dev/null +++ b/project_euler/problem_120/sol1.py @@ -0,0 +1,32 @@ +""" +Problem 120 Square remainders: https://projecteuler.net/problem=120 + +Description: + +Let r be the remainder when (a−1)^n + (a+1)^n is divided by a^2. +For example, if a = 7 and n = 3, then r = 42: 6^3 + 8^3 = 728 ≡ 42 mod 49. +And as n varies, so too will r, but for a = 7 it turns out that r_max = 42. +For 3 ≤ a ≤ 1000, find ∑ r_max. + +Solution: + +On expanding the terms, we get 2 if n is even and 2an if n is odd. +For maximizing the value, 2an < a*a => n <= (a - 1)/2 (integer division) +""" + + +def solution(n: int = 1000) -> int: + """ + Returns ∑ r_max for 3 <= a <= n as explained above + >>> solution(10) + 300 + >>> solution(100) + 330750 + >>> solution(1000) + 333082500 + """ + return sum(2 * a * ((a - 1) // 2) for a in range(3, n + 1)) + + +if __name__ == "__main__": + print(solution()) 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()) diff --git a/project_euler/problem_125/__init__.py b/project_euler/problem_125/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_125/sol1.py b/project_euler/problem_125/sol1.py new file mode 100644 index 000000000000..afc1f2890cef --- /dev/null +++ b/project_euler/problem_125/sol1.py @@ -0,0 +1,56 @@ +""" +Problem 125: https://projecteuler.net/problem=125 + +The palindromic number 595 is interesting because it can be written as the sum +of consecutive squares: 6^2 + 7^2 + 8^2 + 9^2 + 10^2 + 11^2 + 12^2. + +There are exactly eleven palindromes below one-thousand that can be written as +consecutive square sums, and the sum of these palindromes is 4164. Note that +1 = 0^2 + 1^2 has not been included as this problem is concerned with the +squares of positive integers. + +Find the sum of all the numbers less than 10^8 that are both palindromic and can +be written as the sum of consecutive squares. +""" + + +def is_palindrome(n: int) -> bool: + """ + Check if an integer is palindromic. + >>> is_palindrome(12521) + True + >>> is_palindrome(12522) + False + >>> is_palindrome(12210) + False + """ + if n % 10 == 0: + return False + s = str(n) + return s == s[::-1] + + +def solution() -> int: + """ + Returns the sum of all numbers less than 1e8 that are both palindromic and + can be written as the sum of consecutive squares. + """ + LIMIT = 10 ** 8 + answer = set() + first_square = 1 + sum_squares = 5 + while sum_squares < LIMIT: + last_square = first_square + 1 + while sum_squares < LIMIT: + if is_palindrome(sum_squares): + answer.add(sum_squares) + last_square += 1 + sum_squares += last_square ** 2 + first_square += 1 + sum_squares = first_square ** 2 + (first_square + 1) ** 2 + + return sum(answer) + + +if __name__ == "__main__": + print(solution()) 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() = }") diff --git a/project_euler/problem_173/__init__.py b/project_euler/problem_173/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_173/sol1.py b/project_euler/problem_173/sol1.py new file mode 100644 index 000000000000..d539b1437ef1 --- /dev/null +++ b/project_euler/problem_173/sol1.py @@ -0,0 +1,41 @@ +""" +Project Euler Problem 173: https://projecteuler.net/problem=173 + +We shall define a square lamina to be a square outline with a square "hole" so that +the shape possesses vertical and horizontal symmetry. For example, using exactly +thirty-two square tiles we can form two different square laminae: + +With one-hundred tiles, and not necessarily using all of the tiles at one time, it is +possible to form forty-one different square laminae. + +Using up to one million tiles how many different square laminae can be formed? +""" + + +from math import ceil, sqrt + + +def solution(limit: int = 1000000) -> int: + """ + Return the number of different square laminae that can be formed using up to + one million tiles. + >>> solution(100) + 41 + """ + answer = 0 + + for outer_width in range(3, (limit // 4) + 2): + if outer_width ** 2 > limit: + hole_width_lower_bound = max(ceil(sqrt(outer_width ** 2 - limit)), 1) + else: + hole_width_lower_bound = 1 + if (outer_width - hole_width_lower_bound) % 2: + hole_width_lower_bound += 1 + + answer += (outer_width - hole_width_lower_bound - 2) // 2 + 1 + + return answer + + +if __name__ == "__main__": + print(f"{solution() = }") diff --git a/project_euler/problem_174/__init__.py b/project_euler/problem_174/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_174/sol1.py b/project_euler/problem_174/sol1.py new file mode 100644 index 000000000000..cbc0df5a9d65 --- /dev/null +++ b/project_euler/problem_174/sol1.py @@ -0,0 +1,52 @@ +""" +Project Euler Problem 174: https://projecteuler.net/problem=174 + +We shall define a square lamina to be a square outline with a square "hole" so that +the shape possesses vertical and horizontal symmetry. + +Given eight tiles it is possible to form a lamina in only one way: 3x3 square with a +1x1 hole in the middle. However, using thirty-two tiles it is possible to form two +distinct laminae. + +If t represents the number of tiles used, we shall say that t = 8 is type L(1) and +t = 32 is type L(2). + +Let N(n) be the number of t ≤ 1000000 such that t is type L(n); for example, +N(15) = 832. + +What is ∑ N(n) for 1 ≤ n ≤ 10? +""" + +from collections import defaultdict +from math import ceil, sqrt + + +def solution(t_limit: int = 1000000, n_limit: int = 10) -> int: + """ + Return the sum of N(n) for 1 <= n <= n_limit. + + >>> solution(1000,5) + 249 + >>> solution(10000,10) + 2383 + """ + count: defaultdict = defaultdict(int) + + for outer_width in range(3, (t_limit // 4) + 2): + if outer_width * outer_width > t_limit: + hole_width_lower_bound = max( + ceil(sqrt(outer_width * outer_width - t_limit)), 1 + ) + else: + hole_width_lower_bound = 1 + + hole_width_lower_bound += (outer_width - hole_width_lower_bound) % 2 + + for hole_width in range(hole_width_lower_bound, outer_width - 1, 2): + count[outer_width * outer_width - hole_width * hole_width] += 1 + + return sum(1 for n in count.values() if 1 <= n <= 10) + + +if __name__ == "__main__": + print(f"{solution() = }") 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() = }") diff --git a/project_euler/problem_191/__init__.py b/project_euler/problem_191/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_191/sol1.py b/project_euler/problem_191/sol1.py new file mode 100644 index 000000000000..38325b363b89 --- /dev/null +++ b/project_euler/problem_191/sol1.py @@ -0,0 +1,105 @@ +""" +Prize Strings +Problem 191 + +A particular school offers cash rewards to children with good attendance and +punctuality. If they are absent for three consecutive days or late on more +than one occasion then they forfeit their prize. + +During an n-day period a trinary string is formed for each child consisting +of L's (late), O's (on time), and A's (absent). + +Although there are eighty-one trinary strings for a 4-day period that can be +formed, exactly forty-three strings would lead to a prize: + +OOOO OOOA OOOL OOAO OOAA OOAL OOLO OOLA OAOO OAOA +OAOL OAAO OAAL OALO OALA OLOO OLOA OLAO OLAA AOOO +AOOA AOOL AOAO AOAA AOAL AOLO AOLA AAOO AAOA AAOL +AALO AALA ALOO ALOA ALAO ALAA LOOO LOOA LOAO LOAA +LAOO LAOA LAAO + +How many "prize" strings exist over a 30-day period? + +References: + - The original Project Euler project page: + https://projecteuler.net/problem=191 +""" + + +cache = {} + + +def _calculate(days: int, absent: int, late: int) -> int: + """ + A small helper function for the recursion, mainly to have + a clean interface for the solution() function below. + + It should get called with the number of days (corresponding + to the desired length of the 'prize strings'), and the + initial values for the number of consecutive absent days and + number of total late days. + + >>> _calculate(days=4, absent=0, late=0) + 43 + >>> _calculate(days=30, absent=2, late=0) + 0 + >>> _calculate(days=30, absent=1, late=0) + 98950096 + """ + + # if we are absent twice, or late 3 consecutive days, + # no further prize strings are possible + if late == 3 or absent == 2: + return 0 + + # if we have no days left, and have not failed any other rules, + # we have a prize string + if days == 0: + return 1 + + # No easy solution, so now we need to do the recursive calculation + + # First, check if the combination is already in the cache, and + # if yes, return the stored value from there since we already + # know the number of possible prize strings from this point on + key = (days, absent, late) + if key in cache: + return cache[key] + + # now we calculate the three possible ways that can unfold from + # this point on, depending on our attendance today + + # 1) if we are late (but not absent), the "absent" counter stays as + # it is, but the "late" counter increases by one + state_late = _calculate(days - 1, absent, late + 1) + + # 2) if we are absent, the "absent" counter increases by 1, and the + # "late" counter resets to 0 + state_absent = _calculate(days - 1, absent + 1, 0) + + # 3) if we are on time, this resets the "late" counter and keeps the + # absent counter + state_ontime = _calculate(days - 1, absent, 0) + + prizestrings = state_late + state_absent + state_ontime + + cache[key] = prizestrings + return prizestrings + + +def solution(days: int = 30) -> int: + """ + Returns the number of possible prize strings for a particular number + of days, using a simple recursive function with caching to speed it up. + + >>> solution() + 1918080160 + >>> solution(4) + 43 + """ + + return _calculate(days, absent=0, late=0) + + +if __name__ == "__main__": + print(solution()) 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() = }") 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() = }") 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() = }") 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() = }") diff --git a/quantum/__init__.py b/quantum/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/quantum/deutsch_jozsa.py b/quantum/deutsch_jozsa.py new file mode 100755 index 000000000000..304eea196e03 --- /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)}") diff --git a/quantum/half_adder.py b/quantum/half_adder.py new file mode 100755 index 000000000000..4af704e640be --- /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}") diff --git a/quantum/not_gate.py b/quantum/not_gate.py new file mode 100644 index 000000000000..e68a780091c7 --- /dev/null +++ b/quantum/not_gate.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +Build a simple bare-minimum quantum circuit that starts with a single +qubit (by default, in state 0) and inverts it. Run the experiment 1000 +times and print the total count of the states finally observed. +Qiskit Docs: https://qiskit.org/documentation/getting_started.html +""" + +import qiskit as q + + +def single_qubit_measure(qubits: int, classical_bits: int) -> q.result.counts.Counts: + """ + >>> single_qubit_measure(2, 2) + {'11': 1000} + >>> single_qubit_measure(4, 4) + {'0011': 1000} + """ + # Use Aer's qasm_simulator + simulator = q.Aer.get_backend("qasm_simulator") + + # Create a Quantum Circuit acting on the q register + circuit = q.QuantumCircuit(qubits, classical_bits) + + # Apply X (NOT) Gate to Qubits 0 & 1 + circuit.x(0) + circuit.x(1) + + # Map the quantum measurement to the classical bits + circuit.measure([0, 1], [0, 1]) + + # Execute the circuit on the qasm simulator + job = q.execute(circuit, simulator, shots=1000) + + # Return the histogram data of the results of the experiment. + return job.result().get_counts(circuit) + + +if __name__ == "__main__": + counts = single_qubit_measure(2, 2) + print(f"Total count for various states are: {counts}") diff --git a/quantum/quantum_entanglement.py b/quantum/quantum_entanglement.py new file mode 100644 index 000000000000..3d8e2771361c --- /dev/null +++ b/quantum/quantum_entanglement.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +""" +Build a quantum circuit with pair or group of qubits to perform +quantum entanglement. +Quantum entanglement is a phenomenon observed at the quantum scale +where entangled particles stay connected (in some sense) so that +the actions performed on one of the particles affects the other, +no matter the distance between two particles. +""" + +import qiskit + + +def quantum_entanglement(qubits: int = 2) -> qiskit.result.counts.Counts: + """ + # >>> quantum_entanglement(2) + # {'00': 500, '11': 500} + # ┌───┐ ┌─┐ + # q_0: ┤ H ├──■──┤M├─── + # └───┘┌─┴─┐└╥┘┌─┐ + # q_1: ─────┤ X ├─╫─┤M├ + # └───┘ ║ └╥┘ + # c: 2/═══════════╩══╩═ + # 0 1 + Args: + qubits (int): number of quibits to use. Defaults to 2 + Returns: + qiskit.result.counts.Counts: mapping of states to its counts + """ + classical_bits = qubits + + # Using Aer's qasm_simulator + simulator = qiskit.Aer.get_backend("qasm_simulator") + + # Creating a Quantum Circuit acting on the q register + circuit = qiskit.QuantumCircuit(qubits, classical_bits) + + # Adding a H gate on qubit 0 (now q0 in superposition) + circuit.h(0) + + for i in range(1, qubits): + # Adding CX (CNOT) gate + circuit.cx(i - 1, i) + + # Mapping the quantum measurement to the classical bits + circuit.measure(list(range(qubits)), list(range(classical_bits))) + + # Now measuring any one qubit would affect other qubits to collapse + # their super position and have same state as the measured one. + + # Executing the circuit on the qasm simulator + job = qiskit.execute(circuit, simulator, shots=1000) + + return job.result().get_counts(circuit) + + +if __name__ == "__main__": + print(f"Total count for various states are: {quantum_entanglement(3)}") 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() diff --git a/quantum/single_qubit_measure.py b/quantum/single_qubit_measure.py new file mode 100755 index 000000000000..7f058c2179a9 --- /dev/null +++ b/quantum/single_qubit_measure.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +Build a simple bare-minimum quantum circuit that starts with a single +qubit (by default, in state 0), runs the experiment 1000 times, and +finally prints the total count of the states finally observed. +Qiskit Docs: https://qiskit.org/documentation/getting_started.html +""" + +import qiskit as q + + +def single_qubit_measure(qubits: int, classical_bits: int) -> q.result.counts.Counts: + """ + >>> single_qubit_measure(1, 1) + {'0': 1000} + """ + # Use Aer's qasm_simulator + simulator = q.Aer.get_backend("qasm_simulator") + + # Create a Quantum Circuit acting on the q register + circuit = q.QuantumCircuit(qubits, classical_bits) + + # Map the quantum measurement to the classical bits + circuit.measure([0], [0]) + + # Execute the circuit on the qasm simulator + job = q.execute(circuit, simulator, shots=1000) + + # Return the histogram data of the results of the experiment. + return job.result().get_counts(circuit) + + +if __name__ == "__main__": + print(f"Total count for various states are: {single_qubit_measure(1, 1)}") diff --git a/scheduling/__init__.py b/scheduling/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scripts/project_euler_answers.json b/scripts/project_euler_answers.json new file mode 100644 index 000000000000..05c144d1e06a --- /dev/null +++ b/scripts/project_euler_answers.json @@ -0,0 +1,727 @@ +{ + "001": "233168", + "002": "4613732", + "003": "6857", + "004": "906609", + "005": "232792560", + "006": "25164150", + "007": "104743", + "008": "23514624000", + "009": "31875000", + "010": "142913828922", + "011": "70600674", + "012": "76576500", + "013": "5537376230", + "014": "837799", + "015": "137846528820", + "016": "1366", + "017": "21124", + "018": "1074", + "019": "171", + "020": "648", + "021": "31626", + "022": "871198282", + "023": "4179871", + "024": "2783915460", + "025": "4782", + "026": "983", + "027": "-59231", + "028": "669171001", + "029": "9183", + "030": "443839", + "031": "73682", + "032": "45228", + "033": "100", + "034": "40730", + "035": "55", + "036": "872187", + "037": "748317", + "038": "932718654", + "039": "840", + "040": "210", + "041": "7652413", + "042": "162", + "043": "16695334890", + "044": "5482660", + "045": "1533776805", + "046": "5777", + "047": "134043", + "048": "9110846700", + "049": "296962999629", + "050": "997651", + "051": "121313", + "052": "142857", + "053": "4075", + "054": "376", + "055": "249", + "056": "972", + "057": "153", + "058": "26241", + "059": "129448", + "060": "26033", + "061": "28684", + "062": "127035954683", + "063": "49", + "064": "1322", + "065": "272", + "066": "661", + "067": "7273", + "068": "6531031914842725", + "069": "510510", + "070": "8319823", + "071": "428570", + "072": "303963552391", + "073": "7295372", + "074": "402", + "075": "161667", + "076": "190569291", + "077": "71", + "078": "55374", + "079": "73162890", + "080": "40886", + "081": "427337", + "082": "260324", + "083": "425185", + "084": "101524", + "085": "2772", + "086": "1818", + "087": "1097343", + "088": "7587457", + "089": "743", + "090": "1217", + "091": "14234", + "092": "8581146", + "093": "1258", + "094": "518408346", + "095": "14316", + "096": "24702", + "097": "8739992577", + "098": "18769", + "099": "709", + "100": "756872327473", + "101": "37076114526", + "102": "228", + "103": "20313839404245", + "104": "329468", + "105": "73702", + "106": "21384", + "107": "259679", + "108": "180180", + "109": "38182", + "110": "9350130049860600", + "111": "612407567715", + "112": "1587000", + "113": "51161058134250", + "114": "16475640049", + "115": "168", + "116": "20492570929", + "117": "100808458960497", + "118": "44680", + "119": "248155780267521", + "120": "333082500", + "121": "2269", + "122": "1582", + "123": "21035", + "124": "21417", + "125": "2906969179", + "126": "18522", + "127": "18407904", + "128": "14516824220", + "129": "1000023", + "130": "149253", + "131": "173", + "132": "843296", + "133": "453647705", + "134": "18613426663617118", + "135": "4989", + "136": "2544559", + "137": "1120149658760", + "138": "1118049290473932", + "139": "10057761", + "140": "5673835352990", + "141": "878454337159", + "142": "1006193", + "143": "30758397", + "144": "354", + "145": "608720", + "146": "676333270", + "147": "846910284", + "148": "2129970655314432", + "149": "52852124", + "150": "-271248680", + "151": "0.464399", + "152": "301", + "153": "17971254122360635", + "154": "479742450", + "155": "3857447", + "156": "21295121502550", + "157": "53490", + "158": "409511334375", + "159": "14489159", + "160": "16576", + "161": "20574308184277971", + "162": "3D58725572C62302", + "163": "343047", + "164": "378158756814587", + "165": "2868868", + "166": "7130034", + "167": "3916160068885", + "168": "59206", + "169": "178653872807", + "170": "9857164023", + "171": "142989277", + "172": "227485267000992000", + "173": "1572729", + "174": "209566", + "175": "1,13717420,8", + "176": "96818198400000", + "177": "129325", + "178": "126461847755", + "179": "986262", + "180": "285196020571078987", + "181": "83735848679360680", + "182": "399788195976", + "183": "48861552", + "184": "1725323624056", + "185": "4640261571849533", + "186": "2325629", + "187": "17427258", + "188": "95962097", + "189": "10834893628237824", + "190": "371048281", + "191": "1918080160", + "192": "57060635927998347", + "193": "684465067343069", + "194": "61190912", + "195": "75085391", + "196": "322303240771079935", + "197": "1.710637717", + "198": "52374425", + "199": "0.00396087", + "200": "229161792008", + "201": "115039000", + "202": "1209002624", + "203": "34029210557338", + "204": "2944730", + "205": "0.5731441", + "206": "1389019170", + "207": "44043947822", + "208": "331951449665644800", + "209": "15964587728784", + "210": "1598174770174689458", + "211": "1922364685", + "212": "328968937309", + "213": "330.721154", + "214": "1677366278943", + "215": "806844323190414", + "216": "5437849", + "217": "6273134", + "218": "0", + "219": "64564225042", + "220": "139776,963904", + "221": "1884161251122450", + "222": "1590933", + "223": "61614848", + "224": "4137330", + "225": "2009", + "226": "0.11316017", + "227": "3780.618622", + "228": "86226", + "229": "11325263", + "230": "850481152593119296", + "231": "7526965179680", + "232": "0.83648556", + "233": "271204031455541309", + "234": "1259187438574927161", + "235": "1.002322108633", + "236": "123/59", + "237": "15836928", + "238": "9922545104535661", + "239": "0.001887854841", + "240": "7448717393364181966", + "241": "482316491800641154", + "242": "997104142249036713", + "243": "892371480", + "244": "96356848", + "245": "288084712410001", + "246": "810834388", + "247": "782252", + "248": "23507044290", + "249": "9275262564250418", + "250": "1425480602091519", + "251": "18946051", + "252": "104924.0", + "253": "11.492847", + "254": "8184523820510", + "255": "4.4474011180", + "256": "85765680", + "257": "139012411", + "258": "12747994", + "259": "20101196798", + "260": "167542057", + "261": "238890850232021", + "262": "2531.205", + "263": "2039506520", + "264": "2816417.1055", + "265": "209110240768", + "266": "1096883702440585", + "267": "0.999992836187", + "268": "785478606870985", + "269": "1311109198529286", + "270": "82282080", + "271": "4617456485273129588", + "272": "8495585919506151122", + "273": "2032447591196869022", + "274": "1601912348822", + "275": "15030564", + "276": "5777137137739632912", + "277": "1125977393124310", + "278": "1228215747273908452", + "279": "416577688", + "280": "430.088247", + "281": "1485776387445623", + "282": "1098988351", + "283": "28038042525570324", + "284": "5a411d7b", + "285": "157055.80999", + "286": "52.6494571953", + "287": "313135496", + "288": "605857431263981935", + "289": "6567944538", + "290": "20444710234716473", + "291": "4037526", + "292": "3600060866", + "293": "2209", + "294": "789184709", + "295": "4884650818", + "296": "1137208419", + "297": "2252639041804718029", + "298": "1.76882294", + "299": "549936643", + "300": "8.0540771484375", + "301": "2178309", + "302": "1170060", + "303": "1111981904675169", + "304": "283988410192", + "305": "18174995535140", + "306": "852938", + "307": "0.7311720251", + "308": "1539669807660924", + "309": "210139", + "310": "2586528661783", + "311": "2466018557", + "312": "324681947", + "313": "2057774861813004", + "314": "132.52756426", + "315": "13625242", + "316": "542934735751917735", + "317": "1856532.8455", + "318": "709313889", + "319": "268457129", + "320": "278157919195482643", + "321": "2470433131948040", + "322": "999998760323313995", + "323": "6.3551758451", + "324": "96972774", + "325": "54672965", + "326": "1966666166408794329", + "327": "34315549139516", + "328": "260511850222", + "329": "199740353/29386561536000", + "330": "15955822", + "331": "467178235146843549", + "332": "2717.751525", + "333": "3053105", + "334": "150320021261690835", + "335": "5032316", + "336": "CAGBIHEFJDK", + "337": "85068035", + "338": "15614292", + "339": "19823.542204", + "340": "291504964", + "341": "56098610614277014", + "342": "5943040885644", + "343": "269533451410884183", + "344": "65579304332", + "345": "13938", + "346": "336108797689259276", + "347": "11109800204052", + "348": "1004195061", + "349": "115384615384614952", + "350": "84664213", + "351": "11762187201804552", + "352": "378563.260589", + "353": "1.2759860331", + "354": "58065134", + "355": "1726545007", + "356": "28010159", + "357": "1739023853137", + "358": "3284144505", + "359": "40632119", + "360": "878825614395267072", + "361": "178476944", + "362": "457895958010", + "363": "0.0000372091", + "364": "44855254", + "365": "162619462356610313", + "366": "88351299", + "367": "48271207", + "368": "253.6135092068", + "369": "862400558448", + "370": "41791929448408", + "371": "40.66368097", + "372": "301450082318807027", + "373": "727227472448913", + "374": "334420941", + "375": "7435327983715286168", + "376": "973059630185670", + "377": "732385277", + "378": "147534623725724718", + "379": "132314136838185", + "380": "6.3202e25093", + "381": "139602943319822", + "382": "697003956", + "383": "22173624649806", + "384": "3354706415856332783", + "385": "3776957309612153700", + "386": "528755790", + "387": "696067597313468", + "388": "831907372805129931", + "389": "2406376.3623", + "390": "2919133642971", + "391": "61029882288", + "392": "3.1486734435", + "393": "112398351350823112", + "394": "3.2370342194", + "395": "28.2453753155", + "396": "173214653", + "397": "141630459461893728", + "398": "2010.59096", + "399": "1508395636674243,6.5e27330467", + "400": "438505383468410633", + "401": "281632621", + "402": "356019862", + "403": "18224771", + "404": "1199215615081353", + "405": "237696125", + "406": "36813.12757207", + "407": "39782849136421", + "408": "299742733", + "409": "253223948", + "410": "799999783589946560", + "411": "9936352", + "412": "38788800", + "413": "3079418648040719", + "414": "552506775824935461", + "415": "55859742", + "416": "898082747", + "417": "446572970925740", + "418": "1177163565297340320", + "419": "998567458,1046245404,43363922", + "420": "145159332", + "421": "2304215802083466198", + "422": "92060460", + "423": "653972374", + "424": "1059760019628", + "425": "46479497324", + "426": "31591886008", + "427": "97138867", + "428": "747215561862", + "429": "98792821", + "430": "5000624921.38", + "431": "23.386029052", + "432": "754862080", + "433": "326624372659664", + "434": "863253606", + "435": "252541322550", + "436": "0.5276662759", + "437": "74204709657207", + "438": "2046409616809", + "439": "968697378", + "440": "970746056", + "441": "5000088.8395", + "442": "1295552661530920149", + "443": "2744233049300770", + "444": "1.200856722e263", + "445": "659104042", + "446": "907803852", + "447": "530553372", + "448": "106467648", + "449": "103.37870096", + "450": "583333163984220940", + "451": "153651073760956", + "452": "345558983", + "453": "104354107", + "454": "5435004633092", + "455": "450186511399999", + "456": "333333208685971546", + "457": "2647787126797397063", + "458": "423341841", + "459": "3996390106631", + "460": "18.420738199", + "461": "159820276", + "462": "5.5350769703e1512", + "463": "808981553", + "464": "198775297232878", + "465": "585965659", + "466": "258381958195474745", + "467": "775181359", + "468": "852950321", + "469": "0.56766764161831", + "470": "147668794", + "471": "1.895093981e31", + "472": "73811586", + "473": "35856681704365", + "474": "9690646731515010", + "475": "75780067", + "476": "110242.87794", + "477": "25044905874565165", + "478": "59510340", + "479": "191541795", + "480": "turnthestarson", + "481": "729.12106947", + "482": "1400824879147", + "483": "4.993401567e22", + "484": "8907904768686152599", + "485": "51281274340", + "486": "11408450515", + "487": "106650212746", + "488": "216737278", + "489": "1791954757162", + "490": "777577686", + "491": "194505988824000", + "492": "242586962923928", + "493": "6.818741802", + "494": "2880067194446832666", + "495": "789107601", + "496": "2042473533769142717", + "497": "684901360", + "498": "472294837", + "499": "0.8660312", + "500": "35407281", + "501": "197912312715", + "502": "749485217", + "503": "3.8694550145", + "504": "694687", + "505": "714591308667615832", + "506": "18934502", + "507": "316558047002627270", + "508": "891874596", + "509": "151725678", + "510": "315306518862563689", + "511": "935247012", + "512": "50660591862310323", + "513": "2925619196", + "514": "8986.86698", + "515": "2422639000800", + "516": "939087315", + "517": "581468882", + "518": "100315739184392", + "519": "804739330", + "520": "238413705", + "521": "44389811", + "522": "96772715", + "523": "37125450.44", + "524": "2432925835413407847", + "525": "44.69921807", + "526": "49601160286750947", + "527": "11.92412011", + "528": "779027989", + "529": "23624465", + "530": "207366437157977206", + "531": "4515432351156203105", + "532": "827306.56", + "533": "789453601", + "534": "11726115562784664", + "535": "611778217", + "536": "3557005261906288", + "537": "779429131", + "538": "22472871503401097", + "539": "426334056", + "540": "500000000002845", + "541": "4580726482872451", + "542": "697586734240314852", + "543": "199007746081234640", + "544": "640432376", + "545": "921107572", + "546": "215656873", + "547": "11730879.0023", + "548": "12144044603581281", + "549": "476001479068717", + "550": "328104836", + "551": "73597483551591773", + "552": "326227335", + "553": "57717170", + "554": "89539872", + "555": "208517717451208352", + "556": "52126939292957", + "557": "2699929328", + "558": "226754889", + "559": "684724920", + "560": "994345168", + "561": "452480999988235494", + "562": "51208732914368", + "563": "27186308211734760", + "564": "12363.698850", + "565": "2992480851924313898", + "566": "329569369413585", + "567": "75.44817535", + "568": "4228020", + "569": "21025060", + "570": "271197444", + "571": "30510390701978", + "572": "19737656", + "573": "1252.9809", + "574": "5780447552057000454", + "575": "0.000989640561", + "576": "344457.5871", + "577": "265695031399260211", + "578": "9219696799346", + "579": "3805524", + "580": "2327213148095366", + "581": "2227616372734", + "582": "19903", + "583": "1174137929000", + "584": "32.83822408", + "585": "17714439395932", + "586": "82490213", + "587": "2240", + "588": "11651930052", + "589": "131776959.25", + "590": "834171904", + "591": "526007984625966", + "592": "13415DF2BE9C", + "593": "96632320042.0", + "594": "47067598", + "595": "54.17529329", + "596": "734582049", + "597": "0.5001817828", + "598": "543194779059", + "599": "12395526079546335", + "600": "2668608479740672", + "601": "1617243", + "602": "269496760", + "603": "879476477", + "604": "1398582231101", + "605": "59992576", + "606": "158452775", + "607": "13.1265108586", + "608": "439689828", + "609": "172023848", + "610": "319.30207833", + "611": "49283233900", + "612": "819963842", + "613": "0.3916721504", + "614": "130694090", + "615": "108424772", + "616": "310884668312456458", + "617": "1001133757", + "618": "634212216", + "619": "857810883", + "620": "1470337306", + "621": "11429712", + "622": "3010983666182123972", + "623": "3679796", + "624": "984524441", + "625": "551614306", + "626": "695577663", + "627": "220196142", + "628": "210286684", + "629": "626616617", + "630": "9669182880384", + "631": "869588692", + "632": "728378714", + "633": "1.0012e-10", + "634": "4019680944", + "635": "689294705", + "636": "888316", + "637": "49000634845039", + "638": "18423394", + "639": "797866893", + "640": "50.317928", + "641": "793525366", + "642": "631499044", + "643": "968274154", + "644": "20.11208767", + "645": "48894.2174", + "646": "845218467", + "647": "563132994232918611", + "648": "301483197", + "649": "924668016", + "650": "538319652", + "651": "448233151", + "652": "983924497", + "653": "1130658687", + "654": "815868280", + "655": "2000008332", + "656": "888873503555187", + "657": "219493139", + "658": "958280177", + "659": "238518915714422000", + "660": "474766783", + "661": "646231.2177", + "662": "860873428", + "663": "1884138010064752", + "664": "35295862", + "665": "11541685709674", + "666": "0.48023168", + "667": "1.5276527928", + "668": "2811077773", + "669": "56342087360542122", + "670": "551055065", + "671": "946106780", + "672": "91627537", + "673": "700325380", + "674": "416678753", + "675": "416146418", + "676": "3562668074339584", + "677": "984183023", + "678": "1986065", + "679": "644997092988678", + "680": "563917241", + "681": "2611227421428", + "682": "290872710", + "683": "2.38955315e11", + "684": "922058210", + "685": "662878999", + "686": "193060223", + "687": "0.3285320869", + "688": "110941813", + "689": "0.56565454", + "690": "415157690", + "691": "11570761", + "692": "842043391019219959", + "693": "699161", + "694": "1339784153569958487", + "695": "0.1017786859", + "696": "436944244", + "697": "4343871.06", + "698": "57808202", + "699": "37010438774467572", + "700": "1517926517777556", + "701": "13.51099836", + "702": "622305608172525546", + "703": "843437991", + "704": "501985601490518144", + "705": "480440153", + "706": "884837055", + "707": "652907799", + "708": "28874142998632109", + "709": "773479144", + "710": "1275000", + "711": "541510990", + "712": "413876461", + "713": "788626351539895", + "714": "2.452767775565e20", + "715": "883188017", + "716": "238948623", + "717": "1603036763131", + "718": "228579116", + "719": "128088830547982", + "720": "688081048", + "721": "700792959", + "722": "3.376792776502e132", + "723": "1395793419248", + "724": "18128250110", + "725": "4598797036650685" +} diff --git a/scripts/validate_solutions.py b/scripts/validate_solutions.py new file mode 100755 index 000000000000..e1f68ff843bb --- /dev/null +++ b/scripts/validate_solutions.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +import importlib.util +import json +import pathlib +from types import ModuleType +from typing import Dict, List + +import pytest + +PROJECT_EULER_DIR_PATH = pathlib.Path.cwd().joinpath("project_euler") +PROJECT_EULER_ANSWERS_PATH = pathlib.Path.cwd().joinpath( + "scripts", "project_euler_answers.json" +) + +with open(PROJECT_EULER_ANSWERS_PATH) as file_handle: + PROBLEM_ANSWERS: Dict[str, str] = json.load(file_handle) + + +def convert_path_to_module(file_path: pathlib.Path) -> ModuleType: + """Converts a file path to a Python module""" + spec = importlib.util.spec_from_file_location(file_path.name, str(file_path)) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def collect_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(): + if problem_dir_path.is_file() or problem_dir_path.name.startswith("_"): + continue + for file_path in problem_dir_path.iterdir(): + if file_path.suffix != ".py" or file_path.name.startswith(("_", "test")): + continue + solution_file_paths.append(file_path) + return 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): + """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) + expected: str = PROBLEM_ANSWERS[problem_number] + solution_module = convert_path_to_module(solution_path) + answer = str(solution_module.solution()) + assert answer == expected, f"Expected {expected} but got {answer}" diff --git a/searches/__init__.py b/searches/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/searches/double_linear_search_recursion.py b/searches/double_linear_search_recursion.py new file mode 100644 index 000000000000..1c483e974ca7 --- /dev/null +++ b/searches/double_linear_search_recursion.py @@ -0,0 +1,35 @@ +def search(list_data: list, key: int, left: int = 0, right: int = 0) -> int: + """ + Iterate through the array to find the index of key using recursion. + :param list_data: the list to be searched + :param key: the key to be searched + :param left: the index of first element + :param right: the index of last element + :return: the index of key value if found, -1 otherwise. + + >>> search(list(range(0, 11)), 5) + 5 + >>> search([1, 2, 4, 5, 3], 4) + 2 + >>> search([1, 2, 4, 5, 3], 6) + -1 + >>> search([5], 5) + 0 + >>> search([], 1) + -1 + """ + right = right or len(list_data) - 1 + if left > right: + return -1 + elif list_data[left] == key: + return left + elif list_data[right] == key: + return right + else: + return search(list_data, key, left + 1, right - 1) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/searches/simple_binary_search.py b/searches/simple_binary_search.py new file mode 100644 index 000000000000..d1f7f7a51cbc --- /dev/null +++ b/searches/simple_binary_search.py @@ -0,0 +1,59 @@ +""" +Pure Python implementation of a binary search algorithm. + +For doctests run following command: +python3 -m doctest -v simple_binary_search.py + +For manual testing run: +python3 simple_binary_search.py +""" +from __future__ import annotations + + +def binary_search(a_list: list[int], item: int) -> bool: + """ + >>> test_list = [0, 1, 2, 8, 13, 17, 19, 32, 42] + >>> print(binary_search(test_list, 3)) + False + >>> print(binary_search(test_list, 13)) + True + >>> print(binary_search([4, 4, 5, 6, 7], 4)) + True + >>> print(binary_search([4, 4, 5, 6, 7], -10)) + False + >>> print(binary_search([-18, 2], -18)) + True + >>> print(binary_search([5], 5)) + True + >>> print(binary_search(['a', 'c', 'd'], 'c')) + True + >>> print(binary_search(['a', 'c', 'd'], 'f')) + False + >>> print(binary_search([], 1)) + False + >>> print(binary_search([-.1, .1 , .8], .1)) + True + >>> binary_search(range(-5000, 5000, 10), 80) + True + >>> binary_search(range(-5000, 5000, 10), 1255) + False + >>> binary_search(range(0, 10000, 5), 2) + False + """ + if len(a_list) == 0: + return False + midpoint = len(a_list) // 2 + if a_list[midpoint] == item: + return True + if item < a_list[midpoint]: + return binary_search(a_list[:midpoint], item) + else: + return binary_search(a_list[midpoint + 1 :], item) + + +if __name__ == "__main__": + user_input = input("Enter numbers separated by comma:\n").strip() + sequence = [int(item.strip()) for item in user_input.split(",")] + target = int(input("Enter the number to be found in the list:\n").strip()) + not_str = "" if binary_search(sequence, target) else "not " + print(f"{target} was {not_str}found in {sequence}") diff --git a/sorts/__init__.py b/sorts/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 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)) diff --git a/sorts/natural_sort.py b/sorts/natural_sort.py new file mode 100644 index 000000000000..001ff2cf5b41 --- /dev/null +++ b/sorts/natural_sort.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import re + + +def natural_sort(input_list: list[str]) -> list[str]: + """ + Sort the given list of strings in the way that humans expect. + + The normal Python sort algorithm sorts lexicographically, + so you might not get the results that you expect... + + >>> example1 = ['2 ft 7 in', '1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '7 ft 6 in'] + >>> sorted(example1) + ['1 ft 5 in', '10 ft 2 in', '2 ft 11 in', '2 ft 7 in', '7 ft 6 in'] + >>> # The natural sort algorithm sort based on meaning and not computer code point. + >>> natural_sort(example1) + ['1 ft 5 in', '2 ft 7 in', '2 ft 11 in', '7 ft 6 in', '10 ft 2 in'] + + >>> example2 = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9'] + >>> sorted(example2) + ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9'] + >>> natural_sort(example2) + ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13'] + """ + + def alphanum_key(key): + return [int(s) if s.isdigit() else s.lower() for s in re.split("([0-9]+)", key)] + + return sorted(input_list, key=alphanum_key) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() 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)) diff --git a/sorts/recursive_quick_sort.py b/sorts/recursive_quick_sort.py new file mode 100644 index 000000000000..c28a14e37ebd --- /dev/null +++ b/sorts/recursive_quick_sort.py @@ -0,0 +1,22 @@ +def quick_sort(data: list) -> list: + """ + >>> for data in ([2, 1, 0], [2.2, 1.1, 0], "quick_sort"): + ... quick_sort(data) == sorted(data) + True + True + True + """ + if len(data) <= 1: + return data + else: + return ( + quick_sort([e for e in data[1:] if e <= data[0]]) + + [data[0]] + + quick_sort([e for e in data[1:] if e > data[0]]) + ) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/strings/__init__.py b/strings/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/strings/aho_corasick.py b/strings/aho_corasick.py new file mode 100644 index 000000000000..b959dbd58c32 --- /dev/null +++ b/strings/aho_corasick.py @@ -0,0 +1,93 @@ +from collections import deque +from typing import Dict, List, Union + + +class Automaton: + def __init__(self, keywords: List[str]): + self.adlist = list() + self.adlist.append( + {"value": "", "next_states": [], "fail_state": 0, "output": []} + ) + + for keyword in keywords: + self.add_keyword(keyword) + self.set_fail_transitions() + + def find_next_state(self, current_state: int, char: str) -> Union[int, None]: + for state in self.adlist[current_state]["next_states"]: + if char == self.adlist[state]["value"]: + return state + return None + + def add_keyword(self, keyword: str) -> None: + current_state = 0 + for character in keyword: + if self.find_next_state(current_state, character): + current_state = self.find_next_state(current_state, character) + else: + self.adlist.append( + { + "value": character, + "next_states": [], + "fail_state": 0, + "output": [], + } + ) + self.adlist[current_state]["next_states"].append(len(self.adlist) - 1) + current_state = len(self.adlist) - 1 + self.adlist[current_state]["output"].append(keyword) + + def set_fail_transitions(self) -> None: + q = deque() + for node in self.adlist[0]["next_states"]: + q.append(node) + self.adlist[node]["fail_state"] = 0 + while q: + r = q.popleft() + for child in self.adlist[r]["next_states"]: + q.append(child) + state = self.adlist[r]["fail_state"] + while ( + self.find_next_state(state, self.adlist[child]["value"]) is None + and state != 0 + ): + state = self.adlist[state]["fail_state"] + self.adlist[child]["fail_state"] = self.find_next_state( + state, self.adlist[child]["value"] + ) + if self.adlist[child]["fail_state"] is None: + self.adlist[child]["fail_state"] = 0 + self.adlist[child]["output"] = ( + self.adlist[child]["output"] + + self.adlist[self.adlist[child]["fail_state"]]["output"] + ) + + def search_in(self, string: str) -> Dict[str, List[int]]: + """ + >>> A = Automaton(["what", "hat", "ver", "er"]) + >>> A.search_in("whatever, err ... , wherever") + {'what': [0], 'hat': [1], 'ver': [5, 25], 'er': [6, 10, 22, 26]} + """ + result = dict() # returns a dict with keywords and list of its occurrences + current_state = 0 + for i in range(len(string)): + while ( + self.find_next_state(current_state, string[i]) is None + and current_state != 0 + ): + current_state = self.adlist[current_state]["fail_state"] + current_state = self.find_next_state(current_state, string[i]) + if current_state is None: + current_state = 0 + else: + for key in self.adlist[current_state]["output"]: + if not (key in result): + result[key] = [] + result[key].append(i - len(key) + 1) + return result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/strings/can_string_be_rearranged_as_palindrome.py b/strings/can_string_be_rearranged_as_palindrome.py new file mode 100644 index 000000000000..7fedc5877e26 --- /dev/null +++ b/strings/can_string_be_rearranged_as_palindrome.py @@ -0,0 +1,115 @@ +# Created by susmith98 + +from collections import Counter +from timeit import timeit + +# Problem Description: +# Check if characters of the given string can be rearranged to form a palindrome. +# Counter is faster for long strings and non-Counter is faster for short strings. + + +def can_string_be_rearranged_as_palindrome_counter( + input_str: str = "", +) -> bool: + """ + A Palindrome is a String that reads the same forward as it does backwards. + Examples of Palindromes mom, dad, malayalam + >>> can_string_be_rearranged_as_palindrome_counter("Momo") + True + >>> can_string_be_rearranged_as_palindrome_counter("Mother") + False + >>> can_string_be_rearranged_as_palindrome_counter("Father") + False + >>> can_string_be_rearranged_as_palindrome_counter("A man a plan a canal Panama") + True + """ + return sum(c % 2 for c in Counter(input_str.replace(" ", "").lower()).values()) < 2 + + +def can_string_be_rearranged_as_palindrome(input_str: str = "") -> bool: + """ + A Palindrome is a String that reads the same forward as it does backwards. + Examples of Palindromes mom, dad, malayalam + >>> can_string_be_rearranged_as_palindrome("Momo") + True + >>> can_string_be_rearranged_as_palindrome("Mother") + False + >>> can_string_be_rearranged_as_palindrome("Father") + False + >>> can_string_be_rearranged_as_palindrome_counter("A man a plan a canal Panama") + True + """ + if len(input_str) == 0: + return True + lower_case_input_str = input_str.replace(" ", "").lower() + # character_freq_dict: Stores the frequency of every character in the input string + character_freq_dict = {} + + for character in lower_case_input_str: + character_freq_dict[character] = character_freq_dict.get(character, 0) + 1 + """ + Above line of code is equivalent to: + 1) Getting the frequency of current character till previous index + >>> character_freq = character_freq_dict.get(character, 0) + 2) Incrementing the frequency of current character by 1 + >>> character_freq = character_freq + 1 + 3) Updating the frequency of current character + >>> character_freq_dict[character] = character_freq + """ + """ + OBSERVATIONS: + Even length palindrome + -> Every character appears even no.of times. + Odd length palindrome + -> Every character appears even no.of times except for one character. + LOGIC: + Step 1: We'll count number of characters that appear odd number of times i.e oddChar + Step 2:If we find more than 1 character that appears odd number of times, + It is not possible to rearrange as a palindrome + """ + oddChar = 0 + + for character_count in character_freq_dict.values(): + if character_count % 2: + oddChar += 1 + if oddChar > 1: + return False + return True + + +def benchmark(input_str: str = "") -> None: + """ + Benchmark code for comparing above 2 functions + """ + print("\nFor string = ", input_str, ":") + print( + "> can_string_be_rearranged_as_palindrome_counter()", + "\tans =", + can_string_be_rearranged_as_palindrome_counter(input_str), + "\ttime =", + timeit( + "z.can_string_be_rearranged_as_palindrome_counter(z.check_str)", + setup="import __main__ as z", + ), + "seconds", + ) + print( + "> can_string_be_rearranged_as_palindrome()", + "\tans =", + can_string_be_rearranged_as_palindrome(input_str), + "\ttime =", + timeit( + "z.can_string_be_rearranged_as_palindrome(z.check_str)", + setup="import __main__ as z", + ), + "seconds", + ) + + +if __name__ == "__main__": + check_str = input( + "Enter string to determine if it can be rearranged as a palindrome or not: " + ).strip() + benchmark(check_str) + status = can_string_be_rearranged_as_palindrome_counter(check_str) + print(f"{check_str} can {'' if status else 'not '}be rearranged as a palindrome") diff --git a/strings/capitalize.py b/strings/capitalize.py new file mode 100644 index 000000000000..63603aa07e2d --- /dev/null +++ b/strings/capitalize.py @@ -0,0 +1,27 @@ +from string import ascii_lowercase, ascii_uppercase + + +def capitalize(sentence: str) -> str: + """ + This function will capitalize the first letter of a sentence or a word + >>> capitalize("hello world") + 'Hello world' + >>> capitalize("123 hello world") + '123 hello world' + >>> capitalize(" hello world") + ' hello world' + >>> capitalize("a") + 'A' + >>> capitalize("") + '' + """ + if not sentence: + return "" + lower_to_upper = {lc: uc for lc, uc in zip(ascii_lowercase, ascii_uppercase)} + return lower_to_upper.get(sentence[0], sentence[0]) + sentence[1:] + + +if __name__ == "__main__": + from doctest import testmod + + testmod() 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() diff --git a/strings/swap_case.py b/strings/swap_case.py new file mode 100644 index 000000000000..107fda4b52ec --- /dev/null +++ b/strings/swap_case.py @@ -0,0 +1,37 @@ +""" +This algorithm helps you to swap cases. + +User will give input and then program will perform swap cases. + +In other words, convert all lowercase letters to uppercase letters and vice versa. +For example: +1. Please input sentence: Algorithm.Python@89 + aLGORITHM.pYTHON@89 +2. Please input sentence: github.com/mayur200 + GITHUB.COM/MAYUR200 + +""" + + +def swap_case(sentence: str) -> str: + """ + This function will convert all lowercase letters to uppercase letters + and vice versa. + + >>> swap_case('Algorithm.Python@89') + 'aLGORITHM.pYTHON@89' + """ + new_string = "" + for char in sentence: + if char.isupper(): + new_string += char.lower() + elif char.islower(): + new_string += char.upper() + else: + new_string += char + + return new_string + + +if __name__ == "__main__": + print(swap_case(input("Please input sentence: "))) diff --git a/traversals/__init__.py b/traversals/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/web_programming/__init__.py b/web_programming/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/web_programming/co2_emission.py b/web_programming/co2_emission.py new file mode 100644 index 000000000000..97927e7ef541 --- /dev/null +++ b/web_programming/co2_emission.py @@ -0,0 +1,25 @@ +""" +Get CO2 emission data from the UK CarbonIntensity API +""" +from datetime import date + +import requests + +BASE_URL = "https://api.carbonintensity.org.uk/intensity" + + +# Emission in the last half hour +def fetch_last_half_hour() -> str: + last_half_hour = requests.get(BASE_URL).json()["data"][0] + return last_half_hour["intensity"]["actual"] + + +# Emissions in a specific date range +def fetch_from_to(start, end) -> list: + return requests.get(f"{BASE_URL}/{start}/{end}").json()["data"] + + +if __name__ == "__main__": + for entry in fetch_from_to(start=date(2020, 10, 1), end=date(2020, 10, 3)): + print("from {from} to {to}: {intensity[actual]}".format(**entry)) + print(f"{fetch_last_half_hour() = }") diff --git a/web_programming/covid_stats_via_xpath.py b/web_programming/covid_stats_via_xpath.py new file mode 100644 index 000000000000..d22ed017878c --- /dev/null +++ b/web_programming/covid_stats_via_xpath.py @@ -0,0 +1,23 @@ +""" +This is to show simple COVID19 info fetching from worldometers site using lxml +* The main motivation to use lxml in place of bs4 is that it is faster and therefore +more convenient to use in Python web projects (e.g. Django or Flask-based) +""" + +from collections import namedtuple + +import requests +from lxml import html + +covid_data = namedtuple("covid_data", "cases deaths recovered") + + +def covid_stats(url: str = "https://www.worldometers.info/coronavirus/") -> covid_data: + xpath_str = '//div[@class = "maincounter-number"]/span/text()' + return covid_data(*html.fromstring(requests.get(url).content).xpath(xpath_str)) + + +fmt = """Total COVID-19 cases in the world: {} +Total deaths due to COVID-19 in the world: {} +Total COVID-19 patients recovered in the world: {}""" +print(fmt.format(*covid_stats())) 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)) 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()), + ) + ) diff --git a/web_programming/instagram_crawler.py b/web_programming/instagram_crawler.py new file mode 100644 index 000000000000..4536257a984e --- /dev/null +++ b/web_programming/instagram_crawler.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json + +import requests +from bs4 import BeautifulSoup +from fake_useragent import UserAgent + +headers = {"UserAgent": UserAgent().random} + + +def extract_user_profile(script) -> dict: + """ + May raise json.decoder.JSONDecodeError + """ + data = script.contents[0] + info = json.loads(data[data.find('{"config"') : -1]) + return info["entry_data"]["ProfilePage"][0]["graphql"]["user"] + + +class InstagramUser: + """ + Class Instagram crawl instagram user information + + Usage: (doctest failing on GitHub Actions) + # >>> instagram_user = InstagramUser("github") + # >>> instagram_user.is_verified + True + # >>> instagram_user.biography + 'Built for developers.' + """ + + def __init__(self, username): + self.url = f"https://www.instagram.com/{username}/" + self.user_data = self.get_json() + + def get_json(self) -> dict: + """ + Return a dict of user information + """ + html = requests.get(self.url, headers=headers).text + scripts = BeautifulSoup(html, "html.parser").find_all("script") + try: + return extract_user_profile(scripts[4]) + except (json.decoder.JSONDecodeError, KeyError): + return extract_user_profile(scripts[3]) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}('{self.username}')" + + def __str__(self) -> str: + return f"{self.fullname} ({self.username}) is {self.biography}" + + @property + def username(self) -> str: + return self.user_data["username"] + + @property + def fullname(self) -> str: + return self.user_data["full_name"] + + @property + def biography(self) -> str: + return self.user_data["biography"] + + @property + def email(self) -> str: + return self.user_data["business_email"] + + @property + def website(self) -> str: + return self.user_data["external_url"] + + @property + def number_of_followers(self) -> int: + return self.user_data["edge_followed_by"]["count"] + + @property + def number_of_followings(self) -> int: + return self.user_data["edge_follow"]["count"] + + @property + def number_of_posts(self) -> int: + return self.user_data["edge_owner_to_timeline_media"]["count"] + + @property + def profile_picture_url(self) -> str: + return self.user_data["profile_pic_url_hd"] + + @property + def is_verified(self) -> bool: + return self.user_data["is_verified"] + + @property + def is_private(self) -> bool: + return self.user_data["is_private"] + + +def test_instagram_user(username: str = "github") -> None: + """ + A self running doctest + >>> test_instagram_user() + """ + import os + + 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) + assert instagram_user.username == username + if username != "github": + return + assert instagram_user.fullname == "GitHub" + assert instagram_user.biography == "Built for developers." + assert instagram_user.number_of_posts > 150 + assert instagram_user.number_of_followers > 120000 + assert instagram_user.number_of_followings > 15 + assert instagram_user.email == "support@github.com" + assert instagram_user.website == "https://github.com/readme" + assert instagram_user.profile_picture_url.startswith("https://instagram.") + assert instagram_user.is_verified is True + assert instagram_user.is_private is False + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + instagram_user = InstagramUser("github") + print(instagram_user) + print(f"{instagram_user.number_of_posts = }") + print(f"{instagram_user.number_of_followers = }") + print(f"{instagram_user.number_of_followings = }") + print(f"{instagram_user.email = }") + print(f"{instagram_user.website = }") + print(f"{instagram_user.profile_picture_url = }") + print(f"{instagram_user.is_verified = }") + print(f"{instagram_user.is_private = }") 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}.") diff --git a/web_programming/recaptcha_verification.py b/web_programming/recaptcha_verification.py new file mode 100644 index 000000000000..47c6c42f2ad0 --- /dev/null +++ b/web_programming/recaptcha_verification.py @@ -0,0 +1,66 @@ +""" +Recaptcha is a free captcha service offered by Google in order to secure websites and +forms. At https://www.google.com/recaptcha/admin/create you can create new recaptcha +keys and see the keys that your have already created. +* Keep in mind that recaptcha doesn't work with localhost +When you create a recaptcha key, your will get two separate keys: ClientKey & SecretKey. +ClientKey should be kept in your site's front end +SecretKey should be kept in your site's back end + +# An example HTML login form with recaptcha tag is shown below + +
+

Log in

+ {% csrf_token %} +
+ +
+
+ +
+
+ +
+ +
+
+ + + + +Below a Django function for the views.py file contains a login form for demonstrating +recaptcha verification. +""" +import requests + +try: + from django.contrib.auth import authenticate, login + from django.shortcuts import redirect, render +except ImportError: + authenticate = login = render = redirect = print + + +def login_using_recaptcha(request): + # Enter your recaptcha secret key here + secret_key = "secretKey" + url = "https://www.google.com/recaptcha/api/siteverify" + + # when method is not POST, direct user to login page + if request.method != "POST": + return render(request, "login.html") + + # from the frontend, get username, password, and client_key + username = request.POST.get("username") + password = request.POST.get("password") + client_key = request.POST.get("g-recaptcha-response") + + # post recaptcha response to Google's recaptcha api + response = requests.post(url, data={"secret": secret_key, "response": client_key}) + # if the recaptcha api verified our keys + if response.json().get("success", False): + # authenticate the user + user_in_database = authenticate(request, username=username, password=password) + if user_in_database: + login(request, user_in_database) + return redirect("/your-webpage") + return render(request, "login.html") 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