From bbdaeb15c0c4e6054cc1f7d1e1f03bf9d9c4c7b5 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Thu, 8 Oct 2020 00:56:37 -0700 Subject: [PATCH 1/9] Add solution for Project Euler 70, Fixes: #2695 --- DIRECTORY.md | 2 + project_euler/problem_70/__init__.py | 0 project_euler/problem_70/sol1.py | 91 ++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 project_euler/problem_70/__init__.py create mode 100644 project_euler/problem_70/sol1.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 6a3d31709ed6..c188ba6e8345 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -654,6 +654,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_63/sol1.py) * Problem 67 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_67/sol1.py) + * Problem 70 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_70/sol1.py) * Problem 76 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_76/sol1.py) * Problem 97 diff --git a/project_euler/problem_70/__init__.py b/project_euler/problem_70/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_70/sol1.py b/project_euler/problem_70/sol1.py new file mode 100644 index 000000000000..a52b94a51764 --- /dev/null +++ b/project_euler/problem_70/sol1.py @@ -0,0 +1,91 @@ +""" +Project Euler 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. +""" + + +def solution(max: int) -> int: + """ + 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. + """ + + min_num = 1 # i + min_den = 0 # φ(i) + totients = get_totients(max + 1) + + for i in range(2, max + 1): + t = totients[i] + + if i * min_den < min_num * t and has_same_digits(i, t): + min_num = i + min_den = t + + return min_num + + +def get_totients(max_one: int) -> list: + """ + Calculates a list of totients from 0 to max_one exclusive, using the + definition of Euler's product formula: + https://en.wikipedia.org/wiki/Euler's_totient_function#Euler's_product_formula + """ + 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. + """ + 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 + + +if __name__ == "__main__": + print(solution(10000000)) From ca5da045c9b4223e1dc7b619a850f9f26e4bac79 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Thu, 8 Oct 2020 02:02:29 -0700 Subject: [PATCH 2/9] Remove parameter from solution() --- project_euler/problem_70/sol1.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/project_euler/problem_70/sol1.py b/project_euler/problem_70/sol1.py index a52b94a51764..87c5a7e6e94c 100644 --- a/project_euler/problem_70/sol1.py +++ b/project_euler/problem_70/sol1.py @@ -18,7 +18,7 @@ """ -def solution(max: int) -> int: +def solution() -> int: """ 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 @@ -30,6 +30,7 @@ def solution(max: int) -> int: min_num = 1 # i min_den = 0 # φ(i) + max = 10000000 totients = get_totients(max + 1) for i in range(2, max + 1): @@ -88,4 +89,4 @@ def has_same_digits(num1: int, num2: int) -> bool: if __name__ == "__main__": - print(solution(10000000)) + print(solution()) From ebdd4edd053c52101d4ea473db7c38cd81cd453e Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Wed, 14 Oct 2020 21:02:28 -0700 Subject: [PATCH 3/9] Add tests for all functions, add fstring and positional arg for solution() --- project_euler/problem_70/sol1.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_70/sol1.py b/project_euler/problem_70/sol1.py index 87c5a7e6e94c..4e765ca19cca 100644 --- a/project_euler/problem_70/sol1.py +++ b/project_euler/problem_70/sol1.py @@ -18,7 +18,7 @@ """ -def solution() -> int: +def solution(max: int = 10000000) -> int: """ 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 @@ -26,11 +26,16 @@ def solution() -> int: 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. + + >>> solution(100) + 21 + + >>> solution(10000) + 4435 """ min_num = 1 # i min_den = 0 # φ(i) - max = 10000000 totients = get_totients(max + 1) for i in range(2, max + 1): @@ -48,6 +53,12 @@ def get_totients(max_one: int) -> list: Calculates a list of totients from 0 to max_one exclusive, using the definition of Euler's product formula: https://en.wikipedia.org/wiki/Euler's_totient_function#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 @@ -72,6 +83,15 @@ def has_same_digits(num1: int, num2: int) -> bool: 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 @@ -89,4 +109,4 @@ def has_same_digits(num1: int, num2: int) -> bool: if __name__ == "__main__": - print(solution()) + print(f"{solution() = }") From 4d189fb87a7178073a705d280952386688a000c9 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Thu, 15 Oct 2020 20:24:00 -0700 Subject: [PATCH 4/9] Rename directory to 070 --- DIRECTORY.md | 2 +- project_euler/{problem_70 => problem_070}/__init__.py | 0 project_euler/{problem_70 => problem_070}/sol1.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename project_euler/{problem_70 => problem_070}/__init__.py (100%) rename project_euler/{problem_70 => problem_070}/sol1.py (100%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 9e0dfa5a56a7..17c86781b18e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -664,7 +664,7 @@ * Problem 069 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_069/sol1.py) * Problem 070 - * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_70/sol1.py) + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_070/sol1.py) * Problem 071 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_071/sol1.py) * Problem 072 diff --git a/project_euler/problem_70/__init__.py b/project_euler/problem_070/__init__.py similarity index 100% rename from project_euler/problem_70/__init__.py rename to project_euler/problem_070/__init__.py diff --git a/project_euler/problem_70/sol1.py b/project_euler/problem_070/sol1.py similarity index 100% rename from project_euler/problem_70/sol1.py rename to project_euler/problem_070/sol1.py From 8d89b90a2ead71e7e6ef8b56e93270c17afe77c7 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Thu, 15 Oct 2020 20:39:06 -0700 Subject: [PATCH 5/9] Move up explanation to module code block --- project_euler/problem_070/sol1.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 4e765ca19cca..10f8ff4f723c 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -15,17 +15,25 @@ 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 """ def solution(max: int = 10000000) -> int: """ - 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. + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. >>> solution(100) 21 @@ -51,8 +59,7 @@ def solution(max: int = 10000000) -> int: def get_totients(max_one: int) -> list: """ Calculates a list of totients from 0 to max_one exclusive, using the - definition of Euler's product formula: - https://en.wikipedia.org/wiki/Euler's_totient_function#Euler's_product_formula + definition of Euler's product formula. >>> get_totients(5) [0, 1, 1, 2, 2] From c34bd6a32c405869d5752238f7a570e2d7e96ba8 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Fri, 16 Oct 2020 11:15:55 -0700 Subject: [PATCH 6/9] Move solution() below helper functions, rename variables --- project_euler/problem_070/sol1.py | 53 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 10f8ff4f723c..7b7824ee3ed5 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -1,6 +1,5 @@ """ -Project Euler 70 -https://projecteuler.net/problem=70 +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 @@ -31,31 +30,6 @@ """ -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_num = 1 # i - min_den = 0 # φ(i) - totients = get_totients(max + 1) - - for i in range(2, max + 1): - t = totients[i] - - if i * min_den < min_num * t and has_same_digits(i, t): - min_num = i - min_den = t - - return min_num - - def get_totients(max_one: int) -> list: """ Calculates a list of totients from 0 to max_one exclusive, using the @@ -115,5 +89,30 @@ def has_same_digits(num1: int, num2: int) -> bool: return True +def solution(max: int = 10000000) -> int: + """ + Finds the value of n from 1 to max such that n/φ(n) produces a minimum. + + >>> solution(100) + 21 + + >>> solution(10000) + 4435 + """ + + min_numerator = 1 # i + min_denominator = 0 # φ(i) + totients = get_totients(max + 1) + + for i in range(2, max + 1): + t = totients[i] + + if i * min_denominator < min_numerator * t and has_same_digits(i, t): + min_numerator = i + min_denominator = t + + return min_numerator + + if __name__ == "__main__": print(f"{solution() = }") From 4c4d3d2228cb0a69d27d8d3d4042689120751206 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Fri, 16 Oct 2020 11:18:55 -0700 Subject: [PATCH 7/9] Remove whitespace from defining min_numerator --- project_euler/problem_070/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index 7b7824ee3ed5..f206f7af7d65 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -100,7 +100,7 @@ def solution(max: int = 10000000) -> int: 4435 """ - min_numerator = 1 # i + min_numerator = 1 # i min_denominator = 0 # φ(i) totients = get_totients(max + 1) From bd354feb3841c861600cd3e5ef2ff7378932e642 Mon Sep 17 00:00:00 2001 From: "peteryao7@gmail.com" Date: Fri, 16 Oct 2020 11:21:36 -0700 Subject: [PATCH 8/9] Add whitespace --- project_euler/problem_070/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index f206f7af7d65..a5ad81e24e89 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -100,7 +100,7 @@ def solution(max: int = 10000000) -> int: 4435 """ - min_numerator = 1 # i + min_numerator = 1 # i min_denominator = 0 # φ(i) totients = get_totients(max + 1) From efc6e1d91e4b3f6c183e6f9626274bfeca377630 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 21 Nov 2020 08:06:56 +0530 Subject: [PATCH 9/9] Improve type hints with typing.List --- project_euler/problem_070/sol1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project_euler/problem_070/sol1.py b/project_euler/problem_070/sol1.py index a5ad81e24e89..9d27119ba95c 100644 --- a/project_euler/problem_070/sol1.py +++ b/project_euler/problem_070/sol1.py @@ -28,9 +28,10 @@ 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: +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.