From 32e611f6c5b9025e6d93e126dc661525f9542aa8 Mon Sep 17 00:00:00 2001 From: Srishtik2310 Date: Wed, 20 Oct 2021 21:18:00 +0530 Subject: [PATCH 1/4] Added solution for probelm_686 of project_euler --- project_euler/problem_686/__init__.py | 0 project_euler/problem_686/sol1.py | 160 ++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 project_euler/problem_686/__init__.py create mode 100644 project_euler/problem_686/sol1.py diff --git a/project_euler/problem_686/__init__.py b/project_euler/problem_686/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_686/sol1.py b/project_euler/problem_686/sol1.py new file mode 100644 index 000000000000..9c538ff17fb7 --- /dev/null +++ b/project_euler/problem_686/sol1.py @@ -0,0 +1,160 @@ +""" +Project Euler Problem 686: https://projecteuler.net/problem=686 + +2**7 = 128 is the first power of two whose leading digits are "12". +The next power of two whose leading digits are "12" is 2**80. + +Define p(L,n) to be the nth-smallest value of j such that +the base 10 representation of 2**j begins with the digits of L. + +So p(12, 1) = 7 and p(12, 2) = 80. + +You are given that p(123, 45) = 12710. + +Find p(123, 678910). +""" + +import math + + +def log_difference(number: int) -> float: + """ + This function returns the decimal value of a number multiplied with log(2) + Since the problem is on powers of two, finding the powers of two with + large exponents is time consuming. Hence we use log to reduce compute time. + + We can find out that the first power of 2 with starting digits 123 is 90. + Computing 2**90 is time consuming. + Hence we find log(2**90) = 90*log(2) = 27.092699609758302 + But we require only the decimal part to determine whether the power starts with 123. + SO we just return the decimal part of the log product. + Therefore we return 0.092699609758302 + + >>> log_difference(90) + 0.092699609758302 + >>> log_difference(379) + 0.090368356648852 + + """ + + log_number = math.log(2, 10) * number + difference = round((log_number - int(log_number)), 15) + + return difference + + +# series = 90, 379, 575, 864, 1060, 1545, 1741, 2030, 2226, 2515 + + +def solution(number: int = 678910) -> int: + """ + This function calculates the power of two which is nth (n = number) + smallest value of power of 2 + such that the starting digits of the 2**power is 123. + + For example the powers of 2 for which starting digits is 123 are: + 90, 379, 575, 864, 1060, 1545, 1741, 2030, 2226, 2515 and so on. + 90 is the first power of 2 whose starting digits are 123, + 379 is second power of 2 whose starting digits are 123, + and so on. + + So if number = 10, then solution returns 2515 as we observe from above series. + + Wwe will define a lowerbound and upperbound. + lowerbound = log(1.23), upperbound = log(1.24) + because we need to find the powers that yield 123 as starting digits. + + log(1.23) = 08990511143939792, log(1,24) = 09342168516223506. + We use 1.23 and not 12.3 or 123, because log(1.23) yields only decimal value + which is less than 1. + log(12.3) will be same decimal vale but 1 added to it + which is log(12.3) = 1.093421685162235. + We observe that decimal value remains same no matter 1.23 or 12.3 + Since we use the function log_difference(), + which returns the value that is only decimal part, using 1.23 is logical. + + If we see, 90*log(2) = 27.092699609758302, + decimal part = 0.092699609758302, which is inside the range of lowerbound + and upperbound. + + If we compute the difference between all the powers which lead to 123 + starting digits is as follows: + + 379 - 90 = 289 + 575 - 379 = 196 + 864 - 575 = 289 + 1060 - 864 = 196 + + We see a pattern here. The difference is either 196 or 289 = 196 + 93. + + Hence to optimize the algorithm we will increment by 196 or 93 depending upon the + log_difference() value. + + Lets take for example 90. + Since 90 is the first power leading to staring digits as 123, + we will increment iterator by 196. + Because the difference between any two powers leading to 123 + as staring digits is greater than or equal to 196. + After incrementing by 196 we get 286. + + log_difference(286) = 0.09457875989861 which is greater than upperbound. + The next power is 379, and we need to add 93 to get there. + The iterator will now become 379, + which is the next power leading to 123 as starting digits. + + Lets take 1060. We increment by 196, we get 1256. + log_difference(1256) = 0.09367455396034, + Which is greater than upperbound hence we increment by 93. Now iterator is 1349. + log_difference(1349) = 0.08946415071057 which is less than lowerbound. + The next power is 1545 and we need to add 196 to get 1545. + + Conditions are as follows: + + 1) If we find a power, whose log_difference() is in the range of + lower and upperbound, we will increment by 196. + which implies that the power is a number which will lead to 123 as starting digits. + 2) If we find a power, whose log_difference() is greater than or equal upperbound, + we will increment by 93. + 3) if log_difference() < lowerbound, we increment by 196. + + >>> solution(1000) + 284168 + + >>> solution(56000) + 15924915 + + >>> solution(678910) + 193060223 + + """ + + power_iterator = 90 + position = 0 + + lower_limit = math.log(1.23, 10) + upper_limit = math.log(1.24, 10) + previous_power = 0 + + while position < number: + difference = log_difference(power_iterator) + + if difference >= upper_limit: + power_iterator += 93 + + elif difference < lower_limit: + power_iterator += 196 + + else: + previous_power = power_iterator + power_iterator += 196 + position += 1 + + return previous_power + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print(f"{solution() = }") From 2459cfd6acaba449b8cee875f3918ac8921d9417 Mon Sep 17 00:00:00 2001 From: Srishtik2310 Date: Thu, 21 Oct 2021 12:53:51 +0530 Subject: [PATCH 2/4] Changed documentation and formatting. --- project_euler/problem_686/sol1.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/project_euler/problem_686/sol1.py b/project_euler/problem_686/sol1.py index 9c538ff17fb7..7462811dcbd0 100644 --- a/project_euler/problem_686/sol1.py +++ b/project_euler/problem_686/sol1.py @@ -1,11 +1,11 @@ """ Project Euler Problem 686: https://projecteuler.net/problem=686 -2**7 = 128 is the first power of two whose leading digits are "12". -The next power of two whose leading digits are "12" is 2**80. +2^7 = 128 is the first power of two whose leading digits are "12". +The next power of two whose leading digits are "12" is 2^80. Define p(L,n) to be the nth-smallest value of j such that -the base 10 representation of 2**j begins with the digits of L. +the base 10 representation of 2^j begins with the digits of L. So p(12, 1) = 7 and p(12, 2) = 80. @@ -24,8 +24,8 @@ def log_difference(number: int) -> float: large exponents is time consuming. Hence we use log to reduce compute time. We can find out that the first power of 2 with starting digits 123 is 90. - Computing 2**90 is time consuming. - Hence we find log(2**90) = 90*log(2) = 27.092699609758302 + Computing 2^90 is time consuming. + Hence we find log(2^90) = 90*log(2) = 27.092699609758302 But we require only the decimal part to determine whether the power starts with 123. SO we just return the decimal part of the log product. Therefore we return 0.092699609758302 @@ -43,14 +43,11 @@ def log_difference(number: int) -> float: return difference -# series = 90, 379, 575, 864, 1060, 1545, 1741, 2030, 2226, 2515 - - def solution(number: int = 678910) -> int: """ This function calculates the power of two which is nth (n = number) smallest value of power of 2 - such that the starting digits of the 2**power is 123. + such that the starting digits of the 2^power is 123. For example the powers of 2 for which starting digits is 123 are: 90, 379, 575, 864, 1060, 1545, 1741, 2030, 2226, 2515 and so on. @@ -64,7 +61,7 @@ def solution(number: int = 678910) -> int: lowerbound = log(1.23), upperbound = log(1.24) because we need to find the powers that yield 123 as starting digits. - log(1.23) = 08990511143939792, log(1,24) = 09342168516223506. + log(1.23) = 0.08990511143939792, log(1,24) = 0.09342168516223506. We use 1.23 and not 12.3 or 123, because log(1.23) yields only decimal value which is less than 1. log(12.3) will be same decimal vale but 1 added to it From 6089766c663fe33191bc38c66544cda1780cd298 Mon Sep 17 00:00:00 2001 From: Srishtik2310 Date: Fri, 22 Oct 2021 17:13:57 +0530 Subject: [PATCH 3/4] Added ref link to optimization logic --- project_euler/problem_686/sol1.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/project_euler/problem_686/sol1.py b/project_euler/problem_686/sol1.py index 7462811dcbd0..106fce4b2fb1 100644 --- a/project_euler/problem_686/sol1.py +++ b/project_euler/problem_686/sol1.py @@ -114,6 +114,9 @@ def solution(number: int = 678910) -> int: we will increment by 93. 3) if log_difference() < lowerbound, we increment by 196. + Ref link to the above logic: + https://math.stackexchange.com/questions/4093970/powers-of-2-starting-with-123-does-a-pattern-exist + >>> solution(1000) 284168 From 58e4e69eb050dea65f595d9633b55a58d2da9863 Mon Sep 17 00:00:00 2001 From: Srishtik Bhandarkar <53395406+srishtik2310@users.noreply.github.com> Date: Tue, 26 Oct 2021 22:18:48 +0530 Subject: [PATCH 4/4] Update project_euler/problem_686/sol1.py Co-authored-by: John Law --- project_euler/problem_686/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_euler/problem_686/sol1.py b/project_euler/problem_686/sol1.py index 106fce4b2fb1..3b6bdb655170 100644 --- a/project_euler/problem_686/sol1.py +++ b/project_euler/problem_686/sol1.py @@ -114,7 +114,7 @@ def solution(number: int = 678910) -> int: we will increment by 93. 3) if log_difference() < lowerbound, we increment by 196. - Ref link to the above logic: + Reference to the above logic: https://math.stackexchange.com/questions/4093970/powers-of-2-starting-with-123-does-a-pattern-exist >>> solution(1000)