Skip to content

Commit b4036b7

Browse files
srishtik2310poyea
andauthored
Add solution for probelm_686 of project_euler (#5480)
* Added solution for probelm_686 of project_euler * Changed documentation and formatting. * Added ref link to optimization logic * Update project_euler/problem_686/sol1.py Co-authored-by: John Law <[email protected]> Co-authored-by: John Law <[email protected]>
1 parent 31061aa commit b4036b7

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

project_euler/problem_686/__init__.py

Whitespace-only changes.

project_euler/problem_686/sol1.py

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Project Euler Problem 686: https://projecteuler.net/problem=686
3+
4+
2^7 = 128 is the first power of two whose leading digits are "12".
5+
The next power of two whose leading digits are "12" is 2^80.
6+
7+
Define p(L,n) to be the nth-smallest value of j such that
8+
the base 10 representation of 2^j begins with the digits of L.
9+
10+
So p(12, 1) = 7 and p(12, 2) = 80.
11+
12+
You are given that p(123, 45) = 12710.
13+
14+
Find p(123, 678910).
15+
"""
16+
17+
import math
18+
19+
20+
def log_difference(number: int) -> float:
21+
"""
22+
This function returns the decimal value of a number multiplied with log(2)
23+
Since the problem is on powers of two, finding the powers of two with
24+
large exponents is time consuming. Hence we use log to reduce compute time.
25+
26+
We can find out that the first power of 2 with starting digits 123 is 90.
27+
Computing 2^90 is time consuming.
28+
Hence we find log(2^90) = 90*log(2) = 27.092699609758302
29+
But we require only the decimal part to determine whether the power starts with 123.
30+
SO we just return the decimal part of the log product.
31+
Therefore we return 0.092699609758302
32+
33+
>>> log_difference(90)
34+
0.092699609758302
35+
>>> log_difference(379)
36+
0.090368356648852
37+
38+
"""
39+
40+
log_number = math.log(2, 10) * number
41+
difference = round((log_number - int(log_number)), 15)
42+
43+
return difference
44+
45+
46+
def solution(number: int = 678910) -> int:
47+
"""
48+
This function calculates the power of two which is nth (n = number)
49+
smallest value of power of 2
50+
such that the starting digits of the 2^power is 123.
51+
52+
For example the powers of 2 for which starting digits is 123 are:
53+
90, 379, 575, 864, 1060, 1545, 1741, 2030, 2226, 2515 and so on.
54+
90 is the first power of 2 whose starting digits are 123,
55+
379 is second power of 2 whose starting digits are 123,
56+
and so on.
57+
58+
So if number = 10, then solution returns 2515 as we observe from above series.
59+
60+
Wwe will define a lowerbound and upperbound.
61+
lowerbound = log(1.23), upperbound = log(1.24)
62+
because we need to find the powers that yield 123 as starting digits.
63+
64+
log(1.23) = 0.08990511143939792, log(1,24) = 0.09342168516223506.
65+
We use 1.23 and not 12.3 or 123, because log(1.23) yields only decimal value
66+
which is less than 1.
67+
log(12.3) will be same decimal vale but 1 added to it
68+
which is log(12.3) = 1.093421685162235.
69+
We observe that decimal value remains same no matter 1.23 or 12.3
70+
Since we use the function log_difference(),
71+
which returns the value that is only decimal part, using 1.23 is logical.
72+
73+
If we see, 90*log(2) = 27.092699609758302,
74+
decimal part = 0.092699609758302, which is inside the range of lowerbound
75+
and upperbound.
76+
77+
If we compute the difference between all the powers which lead to 123
78+
starting digits is as follows:
79+
80+
379 - 90 = 289
81+
575 - 379 = 196
82+
864 - 575 = 289
83+
1060 - 864 = 196
84+
85+
We see a pattern here. The difference is either 196 or 289 = 196 + 93.
86+
87+
Hence to optimize the algorithm we will increment by 196 or 93 depending upon the
88+
log_difference() value.
89+
90+
Lets take for example 90.
91+
Since 90 is the first power leading to staring digits as 123,
92+
we will increment iterator by 196.
93+
Because the difference between any two powers leading to 123
94+
as staring digits is greater than or equal to 196.
95+
After incrementing by 196 we get 286.
96+
97+
log_difference(286) = 0.09457875989861 which is greater than upperbound.
98+
The next power is 379, and we need to add 93 to get there.
99+
The iterator will now become 379,
100+
which is the next power leading to 123 as starting digits.
101+
102+
Lets take 1060. We increment by 196, we get 1256.
103+
log_difference(1256) = 0.09367455396034,
104+
Which is greater than upperbound hence we increment by 93. Now iterator is 1349.
105+
log_difference(1349) = 0.08946415071057 which is less than lowerbound.
106+
The next power is 1545 and we need to add 196 to get 1545.
107+
108+
Conditions are as follows:
109+
110+
1) If we find a power, whose log_difference() is in the range of
111+
lower and upperbound, we will increment by 196.
112+
which implies that the power is a number which will lead to 123 as starting digits.
113+
2) If we find a power, whose log_difference() is greater than or equal upperbound,
114+
we will increment by 93.
115+
3) if log_difference() < lowerbound, we increment by 196.
116+
117+
Reference to the above logic:
118+
https://math.stackexchange.com/questions/4093970/powers-of-2-starting-with-123-does-a-pattern-exist
119+
120+
>>> solution(1000)
121+
284168
122+
123+
>>> solution(56000)
124+
15924915
125+
126+
>>> solution(678910)
127+
193060223
128+
129+
"""
130+
131+
power_iterator = 90
132+
position = 0
133+
134+
lower_limit = math.log(1.23, 10)
135+
upper_limit = math.log(1.24, 10)
136+
previous_power = 0
137+
138+
while position < number:
139+
difference = log_difference(power_iterator)
140+
141+
if difference >= upper_limit:
142+
power_iterator += 93
143+
144+
elif difference < lower_limit:
145+
power_iterator += 196
146+
147+
else:
148+
previous_power = power_iterator
149+
power_iterator += 196
150+
position += 1
151+
152+
return previous_power
153+
154+
155+
if __name__ == "__main__":
156+
import doctest
157+
158+
doctest.testmod()
159+
160+
print(f"{solution() = }")

0 commit comments

Comments
 (0)