From fc4a97994a741b8daa24ee2aceb591684016f1fd Mon Sep 17 00:00:00 2001 From: Michael D Date: Mon, 5 Oct 2020 19:55:26 +0200 Subject: [PATCH 1/8] Project Euler problem 188 solution --- project_euler/problem_188/__init__.py | 0 project_euler/problem_188/sol1.py | 53 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 project_euler/problem_188/__init__.py create mode 100644 project_euler/problem_188/sol1.py diff --git a/project_euler/problem_188/__init__.py b/project_euler/problem_188/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py new file mode 100644 index 000000000000..318806c6b668 --- /dev/null +++ b/project_euler/problem_188/sol1.py @@ -0,0 +1,53 @@ +""" +The hyperexponentiation of a number +Problem 188 + +The hyperexponentiation or tetration of a number a by a positive integer b, +denoted by a↑↑b or ba, 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. +""" + + +def solution(a=1777, k=1855, digits=8): + """Returns the last 8 digits of the hyperexponentiation of a by k. + + >>> solution() + 95962097 + >>> solution(3, 2) + 27 + >>> solution(3, 3) + 97484987 + """ + + # we calculate everything modulo 10^8, since we only care about the + # last 8 digits + modulo_value = 10 ** digits + + # small helper function for modular exponentiation, to keep the result + # values small enough + def modexpt(base, exponent): + if exponent == 1: + return base + if exponent % 2 == 0: + x = modexpt(base, exponent / 2) % modulo_value + return (x * x) % modulo_value + else: + return (base * modexpt(base, exponent - 1)) % modulo_value + + # calculate a ↑↑ k (mod modulo_value) + result = a + for i in range(1, k): + result = modexpt(a, result) % modulo_value + + return result + + +if __name__ == "__main__": + print(solution()) From 8e6dab9d78c4faef44ca1c8f3de117eaf3f572c2 Mon Sep 17 00:00:00 2001 From: Michael D Date: Tue, 6 Oct 2020 00:00:39 +0200 Subject: [PATCH 2/8] fix superscript notation --- project_euler/problem_188/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 318806c6b668..620fc8d1b22b 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -3,7 +3,7 @@ Problem 188 The hyperexponentiation or tetration of a number a by a positive integer b, -denoted by a↑↑b or ba, is recursively defined by: +denoted by a↑↑b or b^a, is recursively defined by: a↑↑1 = a, a↑↑(k+1) = a(a↑↑k). From 2fa521b08cb1983b9b1803e7a5f985bfd655294a Mon Sep 17 00:00:00 2001 From: Michael D Date: Tue, 6 Oct 2020 19:44:52 +0200 Subject: [PATCH 3/8] split out modexpt() function, and rename parameters --- project_euler/problem_188/sol1.py | 39 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 620fc8d1b22b..3e076352f53d 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -15,8 +15,24 @@ """ -def solution(a=1777, k=1855, digits=8): - """Returns the last 8 digits of the hyperexponentiation of a by k. +# small helper function for modular exponentiation +def modexpt(base, exponent, modulo_value): + """Returns the modular exponentiation, that is the value + of `base ** exponent % modulo_value`, without calculating + the actual number + """ + + 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=1777, height=1855, digits=8): + """Returns the last 8 digits of the hyperexponentiation of base by + height, i.e. the number base↑↑height: >>> solution() 95962097 @@ -30,21 +46,10 @@ def solution(a=1777, k=1855, digits=8): # last 8 digits modulo_value = 10 ** digits - # small helper function for modular exponentiation, to keep the result - # values small enough - def modexpt(base, exponent): - if exponent == 1: - return base - if exponent % 2 == 0: - x = modexpt(base, exponent / 2) % modulo_value - return (x * x) % modulo_value - else: - return (base * modexpt(base, exponent - 1)) % modulo_value - - # calculate a ↑↑ k (mod modulo_value) - result = a - for i in range(1, k): - result = modexpt(a, result) % modulo_value + # calculate base↑↑height (mod modulo_value) + result = base + for i in range(1, height): + result = modexpt(base, result, modulo_value) return result From dcc9aa43e64e05e51027fe4db4c986ac1e6910ba Mon Sep 17 00:00:00 2001 From: Michael D Date: Tue, 6 Oct 2020 19:51:23 +0200 Subject: [PATCH 4/8] Add some more doctest, and add type hints --- project_euler/problem_188/sol1.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 3e076352f53d..7da5ba5437d3 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -16,10 +16,16 @@ # small helper function for modular exponentiation -def modexpt(base, exponent, modulo_value): +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 + the actual number. + >>> modexpt(2, 4, 10) + 6 + >>> modexpt(2, 1024, 100) + 16 + >>> modexpt(13, 65535, 7) + 6 """ if exponent == 1: @@ -30,7 +36,8 @@ def modexpt(base, exponent, modulo_value): else: return (base * modexpt(base, exponent - 1, modulo_value)) % modulo_value -def solution(base=1777, height=1855, digits=8): + +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: From bee3abf185a9ea7fcab8727c9c88bf0a359af9ba Mon Sep 17 00:00:00 2001 From: Michael D Date: Tue, 6 Oct 2020 19:57:43 +0200 Subject: [PATCH 5/8] Add some reference links --- project_euler/problem_188/sol1.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 7da5ba5437d3..29ffdf518013 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -12,6 +12,12 @@ 3↑↑4 is roughly 103.6383346400240996*10^12. Find the last 8 digits of 1777↑↑1855. + +References: + - The original Project Euler project page: + https://projecteuler.net/problem=188 + - Wikipedia article about Hyperexponentiation, aka. Tetration + https://en.wikipedia.org/wiki/Tetration """ From c8747e41b736c525da85ab0fbb01ddaf2da516b1 Mon Sep 17 00:00:00 2001 From: Michael D Date: Sat, 10 Oct 2020 19:37:28 +0200 Subject: [PATCH 6/8] Update docstrings and mark helper function private --- project_euler/problem_188/sol1.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 29ffdf518013..89d6b191351f 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -22,29 +22,31 @@ # small helper function for modular exponentiation -def modexpt(base: int, exponent: int, modulo_value: int) -> int: - """Returns the modular exponentiation, that is the value +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) + >>> _modexpt(2, 4, 10) 6 - >>> modexpt(2, 1024, 100) + >>> _modexpt(2, 1024, 100) 16 - >>> modexpt(13, 65535, 7) + >>> _modexpt(13, 65535, 7) 6 """ if exponent == 1: return base if exponent % 2 == 0: - x = modexpt(base, exponent / 2, modulo_value) % modulo_value + 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 + 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 + """ + Returns the last 8 digits of the hyperexponentiation of base by height, i.e. the number base↑↑height: >>> solution() @@ -59,10 +61,11 @@ def solution(base: int = 1777, height: int = 1855, digits: int = 8) -> int: # last 8 digits modulo_value = 10 ** digits - # calculate base↑↑height (mod modulo_value) + # calculate base↑↑height (mod modulo_value) by repeated modular + # exponentiation 'height' times result = base for i in range(1, height): - result = modexpt(base, result, modulo_value) + result = _modexpt(base, result, modulo_value) return result From 780ebe0477fdd9534bd9a705b64e735201ee4422 Mon Sep 17 00:00:00 2001 From: Michael D Date: Mon, 12 Oct 2020 22:17:34 +0200 Subject: [PATCH 7/8] Fix doctests and remove/improve redundant comments --- project_euler/problem_188/sol1.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 89d6b191351f..04c69851160b 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -49,23 +49,19 @@ 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() - 95962097 - >>> solution(3, 2) + >>> solution(base=3, height=2) 27 - >>> solution(3, 3) + >>> solution(base=3, height=3) 97484987 + >>> solution(base=123, height=456, digits=4) + 2547 """ - # we calculate everything modulo 10^8, since we only care about the - # last 8 digits - modulo_value = 10 ** digits - - # calculate base↑↑height (mod modulo_value) by repeated modular - # exponentiation 'height' times + # calculate base↑↑height by right-assiciative repeated modular + # exponentiation result = base for i in range(1, height): - result = _modexpt(base, result, modulo_value) + result = _modexpt(base, result, 10 ** digits) return result From 2de8c591788c928c689a3f9c13ff5fe902bf5c3f Mon Sep 17 00:00:00 2001 From: Michael D Date: Sat, 17 Oct 2020 00:23:44 +0200 Subject: [PATCH 8/8] fix as per style guide --- project_euler/problem_188/sol1.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/project_euler/problem_188/sol1.py b/project_euler/problem_188/sol1.py index 04c69851160b..6473c63620ed 100644 --- a/project_euler/problem_188/sol1.py +++ b/project_euler/problem_188/sol1.py @@ -1,6 +1,7 @@ """ +Project Euler Problem 188: https://projecteuler.net/problem=188 + The hyperexponentiation of a number -Problem 188 The hyperexponentiation or tetration of a number a by a positive integer b, denoted by a↑↑b or b^a, is recursively defined by: @@ -14,10 +15,7 @@ Find the last 8 digits of 1777↑↑1855. References: - - The original Project Euler project page: - https://projecteuler.net/problem=188 - - Wikipedia article about Hyperexponentiation, aka. Tetration - https://en.wikipedia.org/wiki/Tetration + - https://en.wikipedia.org/wiki/Tetration """ @@ -67,4 +65,4 @@ def solution(base: int = 1777, height: int = 1855, digits: int = 8) -> int: if __name__ == "__main__": - print(solution()) + print(f"{solution() = }")