From 7190721c5f3b8107454818e7abd59909533828e6 Mon Sep 17 00:00:00 2001 From: daviddoji Date: Sat, 30 Oct 2021 18:56:42 +0200 Subject: [PATCH] Fix problem number --- src/Python/Problem051.py | 39 ++++++++++++-------- src/Python/Problem052.py | 19 +++++----- src/Python/Problem053.py | 4 +-- src/Python/Problem054.py | 55 ++++++++++++++++++----------- src/Python/Problem055.py | 10 +++--- src/Python/Problem056.py | 6 ++-- src/Python/Problem057.py | 12 +++---- src/Python/Problem058.py | 14 ++++---- src/Python/Problem059.py | 44 +++++++++++------------ src/Python/Problems001-050/utils.py | 48 +++++++++++++++++++++++++ 10 files changed, 163 insertions(+), 88 deletions(-) create mode 100644 src/Python/Problems001-050/utils.py diff --git a/src/Python/Problem051.py b/src/Python/Problem051.py index f71ef32..bdcfa58 100644 --- a/src/Python/Problem051.py +++ b/src/Python/Problem051.py @@ -15,34 +15,45 @@ from utils import timeit, list_primes, is_prime @timeit("Problem 51") def compute(): """ - By replacing the 1st digit of the 2-digit number *3, it turns out that + 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 + 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 + 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. """ - + primes = sorted(set(list_primes(1_000_000)) - set(list_primes(57_000))) - digits = {'0':[], '1':[], '2':[],'3':[], '4':[], '5':[],'6':[], '7':[], '8':[], '9':[]} + digits = { + "0": [], + "1": [], + "2": [], + "3": [], + "4": [], + "5": [], + "6": [], + "7": [], + "8": [], + "9": [], + } for d in digits.keys(): for p in primes: p = str(p) if p.count(d) == 3 and p[-1] != d: digits[d].append(p) - for d in {'0', '1', '2'}: + for d in {"0", "1", "2"}: for p in digits[d]: res = 0 i = 10 - for D in {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}-{d}: + for D in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} - {d}: i -= 1 q = int(p.replace(d, D)) if is_prime(q) and q > 57_000: @@ -55,4 +66,4 @@ def compute(): if __name__ == "__main__": - print(f"Result for Problem {int(51):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 51: {compute()}") diff --git a/src/Python/Problem052.py b/src/Python/Problem052.py index b2f151f..e3ce8ea 100644 --- a/src/Python/Problem052.py +++ b/src/Python/Problem052.py @@ -15,22 +15,25 @@ from utils import timeit @timeit("Problem 52") def compute(): """ - It can be seen that the number, 125874, and its double, 251748, + 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, + Find the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and 6x, contain the same digits. """ for number in range(123456, 1_000_000): - if sorted(str(number)) \ - == sorted(str(2*number)) \ - == sorted(str(3*number)) \ - == sorted(str(4*number)) \ - == sorted(str(5*number)) == sorted(str(6*number)): + if ( + sorted(str(number)) + == sorted(str(2 * number)) + == sorted(str(3 * number)) + == sorted(str(4 * number)) + == sorted(str(5 * number)) + == sorted(str(6 * number)) + ): return number if __name__ == "__main__": - print(f"Result for Problem {int(52):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 52: {compute()}") diff --git a/src/Python/Problem053.py b/src/Python/Problem053.py index 0d15afa..15a7a7a 100644 --- a/src/Python/Problem053.py +++ b/src/Python/Problem053.py @@ -27,7 +27,7 @@ def compute(): It is not until , that a value exceeds one-million: (23 over 10) = 1144066. - How many, not necessarily distinct, values of (n over r) for 1<=n<=100, are greater than one-million? + How many, not necessarily distinct, values of (n over r) for 1<=n<=100, are greater than one-million? """ ans = 0 @@ -41,4 +41,4 @@ def compute(): if __name__ == "__main__": - print(f"Result for Problem {int(53):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 53: {compute()}") diff --git a/src/Python/Problem054.py b/src/Python/Problem054.py index de56b4c..087719c 100644 --- a/src/Python/Problem054.py +++ b/src/Python/Problem054.py @@ -16,7 +16,7 @@ from utils import timeit @timeit("Problem 54") def compute(): """ - In the card game poker, a hand consists of five cards and are ranked, + 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. @@ -34,11 +34,11 @@ def compute(): 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 - (see example 1 below). But if two ranks tie, for example, both players - have a pair of queens, then highest cards in each hand are compared - (see example 4 below); if the highest cards tie then the next highest + 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 + (see example 1 below). But if two ranks tie, for example, both players + have a pair of queens, then highest cards in each hand are compared + (see example 4 below); if the highest cards tie then the next highest cards are compared, and so on. Consider the following five hands dealt to two players: @@ -56,12 +56,12 @@ def compute(): 5 2H 2D 4C 4D 4S 3C 3D 3S 9S 9D Player 1 Full House Full House With Three Fours with Three Threes - - 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 + + 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? @@ -79,7 +79,6 @@ def compute(): def to_numerical(hand: list) -> list: return sorted([int(x[:-1]) for x in hand], reverse=True) - # 10 Ranks functions. def high_card(str_hand: list) -> list: return to_numerical(str_hand) @@ -87,7 +86,7 @@ def compute(): def one_pair(hand: list) -> int: return n_of_a_kind(hand, 2) - def two_pair(hand: list) -> int: + def two_pair(hand: list) -> int: pairs = set([x for x in hand if hand.count(x) == 2]) return 0 if len(pairs) < 2 else max(pairs) @@ -95,7 +94,7 @@ def compute(): return n_of_a_kind(hand, 3) def straight(hand: list) -> int: - return 0 if not list(range(hand[0], hand[-1]-1, -1)) == hand else max(hand) + return 0 if not list(range(hand[0], hand[-1] - 1, -1)) == hand else max(hand) def flush(str_hand: list) -> bool: return len(set([x[-1] for x in str_hand])) == 1 @@ -113,7 +112,6 @@ def compute(): def royal_flush(str_hand: list) -> bool: return flush(str_hand) and list(range(14, 9, -1)) == to_numerical(str_hand) - replace_map = {"T": 10, "J": 11, "Q": 12, "K": 13, "A": 14} score = [0, 0] @@ -121,10 +119,25 @@ def compute(): for line in open(file, "r").read().splitlines(): line = replace_values_in_string(line, replace_map).split() hands = line[:5], line[5:] - for rank in (royal_flush, straight_flush, four_of_a_kind, full_house, flush, - straight, three_of_a_kind, two_pair, one_pair, high_card): - should_convert_hand = "str" not in rank.__code__.co_varnames[0] # Checks parameter name. - result = [rank(to_numerical(hand) if should_convert_hand else hand) for hand in hands] + for rank in ( + royal_flush, + straight_flush, + four_of_a_kind, + full_house, + flush, + straight, + three_of_a_kind, + two_pair, + one_pair, + high_card, + ): + should_convert_hand = ( + "str" not in rank.__code__.co_varnames[0] + ) # Checks parameter name. + result = [ + rank(to_numerical(hand) if should_convert_hand else hand) + for hand in hands + ] if result[0] != result[1]: score[0 if result[0] > result[1] else 1] += 1 break @@ -134,4 +147,4 @@ def compute(): if __name__ == "__main__": - print(f"Result for Problem {int(54):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 54: {compute()}") diff --git a/src/Python/Problem055.py b/src/Python/Problem055.py index 1075a0c..c5272f4 100644 --- a/src/Python/Problem055.py +++ b/src/Python/Problem055.py @@ -31,13 +31,13 @@ def compute(): 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, + (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; @@ -57,10 +57,10 @@ def compute(): break if is_lychrel: ans += 1 - + return ans if __name__ == "__main__": - print(f"Result for Problem {int(55):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 55: {compute()}") diff --git a/src/Python/Problem056.py b/src/Python/Problem056.py index 507f132..ca77dd4 100644 --- a/src/Python/Problem056.py +++ b/src/Python/Problem056.py @@ -15,7 +15,7 @@ from utils import timeit @timeit("Problem 56") def compute(): """ - A googol (10^100) is a massive number: one followed by one-hundred zeros; + A googol (10^100) is a massive number: one followed by one-hundred zeros; 100100 is almost unimaginably large: one followed by two-hundred zeros. Despite their size, the sum of the digits in each number is only 1. @@ -26,7 +26,7 @@ def compute(): ans = 0 for a in range(100): for b in range(100): - num = sum([int(digit) for digit in str(a**b)]) + num = sum([int(digit) for digit in str(a ** b)]) if num > ans: ans = num @@ -35,4 +35,4 @@ def compute(): if __name__ == "__main__": - print(f"Result for Problem {int(56):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 56: {compute()}") diff --git a/src/Python/Problem057.py b/src/Python/Problem057.py index a92a66e..bc703f5 100644 --- a/src/Python/Problem057.py +++ b/src/Python/Problem057.py @@ -16,7 +16,7 @@ from utils import timeit @timeit("Problem 57") def compute(): """ - It is possible to show that the square root of two can be expressed + It is possible to show that the square root of two can be expressed as an infinite continued fraction. By expanding this for the first four iterations, we get: @@ -26,11 +26,11 @@ def compute(): 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 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 + In the first one-thousand expansions, how many fractions contain a numerator with more digits than the denominator? """ @@ -41,10 +41,10 @@ def compute(): result = 1 + f if len(str(result.numerator)) > len(str(result.denominator)): ans += 1 - + return ans if __name__ == "__main__": - print(f"Result for Problem {int(57):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 57: {compute()}") diff --git a/src/Python/Problem058.py b/src/Python/Problem058.py index b66064a..611fc59 100644 --- a/src/Python/Problem058.py +++ b/src/Python/Problem058.py @@ -15,7 +15,7 @@ from utils import timeit, is_prime @timeit("Problem 58") def compute(): """ - Starting with 1 and spiralling anticlockwise in the following way, + 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 @@ -26,13 +26,13 @@ def compute(): 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 + 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 + 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%? """ @@ -53,4 +53,4 @@ def compute(): if __name__ == "__main__": - print(f"Result for Problem {int(58):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 58: {compute()}") diff --git a/src/Python/Problem059.py b/src/Python/Problem059.py index 062feda..ec58bbb 100644 --- a/src/Python/Problem059.py +++ b/src/Python/Problem059.py @@ -17,37 +17,37 @@ from utils import timeit @timeit("Problem 59") def compute(): """ - Each character on a computer is assigned a unique code and the preferred - standard is ASCII (American Standard Code for Information Interchange). + Each character on a computer is assigned a unique code and the preferred + standard is ASCII (American Standard Code for Information Interchange). For example, uppercase A = 65, asterisk (*) = 42, and lowercase k = 107. - A modern encryption method is to take a text file, convert the bytes to - ASCII, then XOR each byte with a given value, taken from a secret key. - The advantage with the XOR function is that using the same encryption key - on the cipher text, restores the plain text; for example, 65 XOR 42 = 107, + A modern encryption method is to take a text file, convert the bytes to + ASCII, then XOR each byte with a given value, taken from a secret key. + The advantage with the XOR function is that using the same encryption key + on the cipher text, restores the plain text; for example, 65 XOR 42 = 107, then 107 XOR 42 = 65. - For unbreakable encryption, the key is the same length as the plain text - message, and the key is made up of random bytes. The user would keep the - encrypted message and the encryption key in different locations, and + For unbreakable encryption, the key is the same length as the plain text + message, and the key is made up of random bytes. The user would keep the + encrypted message and the encryption key in different locations, and without both "halves", it is impossible to decrypt the message. - Unfortunately, this method is impractical for most users, so the modified - method is to use a password as a key. If the password is shorter than the - message, which is likely, the key is repeated cyclically throughout the - message. The balance for this method is using a sufficiently long password + Unfortunately, this method is impractical for most users, so the modified + method is to use a password as a key. If the password is shorter than the + message, which is likely, the key is repeated cyclically throughout the + message. The balance for this method is using a sufficiently long password key for security, but short enough to be memorable. - Your task has been made easy, as the encryption key consists of three - lower case characters. Using p059_cipher.txt, a file containing the - encrypted ASCII codes, and the knowledge that the plain text must contain - common English words, decrypt the message and find the sum of the ASCII + Your task has been made easy, as the encryption key consists of three + lower case characters. Using p059_cipher.txt, a file containing the + encrypted ASCII codes, and the knowledge that the plain text must contain + common English words, decrypt the message and find the sum of the ASCII values in the original text. """ - with open('../files/Problem59.txt', 'r') as f: + with open("../files/Problem59.txt", "r") as f: # encrypted = list(map(int, f.read().split(','))) - encrypted = [int(char) for char in f.read().split(',')] + encrypted = [int(char) for char in f.read().split(",")] # print(encrypted) # print(test) plain_text = len(encrypted) // 3 @@ -55,12 +55,12 @@ def compute(): decrypted = "" for k, i in zip(list(key) * plain_text, encrypted): decrypted += chr(ord(k) ^ i) - + # assuming Euler will be in the text - if 'Euler' in decrypted: + if "Euler" in decrypted: return sum([ord(c) for c in decrypted]) if __name__ == "__main__": - print(f"Result for Problem {int(59):003d}: {compute()}") \ No newline at end of file + print(f"Result for Problem 59: {compute()}") diff --git a/src/Python/Problems001-050/utils.py b/src/Python/Problems001-050/utils.py new file mode 100644 index 0000000..6d70861 --- /dev/null +++ b/src/Python/Problems001-050/utils.py @@ -0,0 +1,48 @@ +import math +from functools import wraps + + +def timeit(name): + def profile(original): + import time + @wraps(original) + def wrapper(*args, **kwargs): + t0 = time.perf_counter() + result = original(*args, **kwargs) + t1 = time.perf_counter() + print(f"Time to evaluate problem {int(name[7:]):003d}: {(t1 - t0)*1000:.3f} ms\n") + return result + return wrapper + return profile + + +def is_prime(n): + if n <2: + return False + for i in range(2, int(math.sqrt(n)) + 1): + if n % i == 0: + return False + return True + + +# Returns a list of True and False indicating whether each number is prime. +# For 0 <= i <= n, result[i] is True if i is a prime number, False otherwise. +def list_primality(n): + # Sieve of Eratosthenes + result = [True] * (n + 1) + result[0] = result[1] = False + for i in range(int(math.sqrt(n) + 1)): + if result[i]: + for j in range(i * i, len(result), i): + result[j] = False + return result + + +# Returns all the prime numbers less than or equal to n, in ascending order +# For example: list_primes(97) = [2, 3, 5, 7, 11, ..., 83, 89, 97]. +def list_primes(n): + return [i for (i, is_prime) in enumerate(list_primality(n)) if is_prime] + + +def is_palindrome(num): + return str(num) == str(num)[::-1] \ No newline at end of file