Project Euler Problem 40
Champernowne’s constant.
An irrational decimal fraction is created by concatenating the positive integers:
0.123456789101112131415161718192021…
It can be seen that the 12th digit of the fractional part is 1.
If dn represents the nth digit of the fractional part, find the value of the following expression.
d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000
Link to original description
Source code examples on Github
At first butforce python solution:
Python version 1
1 2 3 4 5 6 7 8 9 10 11 12 | #!/usr/bin/python3 from functools import reduce s = "" for i in range(1, 1000001): s += str(i) l = list(s) print("Answer: %s" % reduce(lambda a,b: a*b, [int(l[x-1]) for x in [1,10,100,1000,10000,100000,1000000]])) |
it is pretty slow:
1 2 3 4 5 6 | mkh@mkh-xps:~/work/mblog/pr_euler/p40$ time ./p40.py Answer: 210 real 0m0.503s user 0m0.491s sys 0m0.012s |
So we can try to find another algorithm. Integers 1-9 represents digits 1-9, 10-99 -> digits 10-189 (90 * 2 + 9) …. :
1-9: 1-9 10-99: 10-189 100-999: 190-2889 1000-9999: 2890-38889 10000-99999: 38890 – 488889 100000-999999: 488890 – 5888889
So, d1 = 1 d10: we are in 10-99 range -> 10-9 = 1 (we are on the first integer of the range 10-99). Each integer have 2 digits, so (10-9)/2+9 -> 9 and ½. This means we have first digits of tenth number. Tenth number is 10, its first digit is 1.
d100: we are also in 10-99 range -> 100 - 9 = 91 (we are on the 91 intereg of the range 10-99). As for d10: (100-9)/2+9 -> 54 and ½. This means the have first digits of integer 55. It is 5.
d1000: now we are in 100-999 range -> 1000-189 = 811. Each integer in this range has 3 digits: (1000-189)/3+99 = 369 and 1/3. (99 - numbers of integers from 2 previous ranges 1-9 and 10-99). So we have first digits from 370 -> 3.
and for all our numbers:
d1 : 1/1 + 0 = 1 => 1 d10 : (10-9)/2 + 9 = 9 1/2 => 1 d100 : (100-9)/2 + 9 = 54 1/2 => 5 d1.000 : (1.000-189)/3 + 99 = 369 1/3 => 3 d10.000 : (10.000 – 2.889) / 4 + 999 = 2776 3/4 => 7 d100.000 : (100.000 – 38.889) /5 + 9.999 = 22.221 1/5 => 2 d1.000.000 : (1.000.000 – 488.889)/6 + 99.999 = 185.184 1/6 => 1
and here python implementation of this algorithm:
Python solution 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/python3 from functools import reduce def dn(n, l): if n == 1: return 1 (ln, start, end) = next((i+1,l[i-1],v) for i,v in enumerate(l) if v > n) d = (n - start) // ln + 10**(ln-1) return list(str(d))[(n-start) % ln -1] l, m, previous = [1,10,100,1000,10000,100000,1000000], [], 0 for x in l: a = len(str(x)) previous += 9*(10**(a-1))*a m.append(previous) print("Answer: %s" % reduce(lambda a,b: a*b, [int(dn(x,m)) for x in l])) |
this solution much faster brutforce solution:
1 2 3 4 5 6 | mkh@mkh-xps:~/work/mblog/pr_euler/p40$ time ./p40_1.py Answer: 210 real 0m0.026s user 0m0.022s sys 0m0.004s |
and erlang version:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p40 % vim:syn=erlang main(_) -> L = [1,10,100,1000,10000,100000,1000000], M = gb(L, [0]), Answer = lists:foldl(fun(E,A)-> E*A end, 1, lists:map(fun(X)-> dm(X,M) end, L)), io:format("Answer: ~p ~n", [Answer]). gb([], [_|A]) -> A; gb([H|T], A) -> Len = length(integer_to_list(H)), gb(T, A ++ [lists:last(A) + 9 * pow(10, Len-1) * Len]). pow(X,Y) -> pow(X,Y,1). pow(_,0,A) -> A; pow(X,Y,A) -> pow(X,Y-1,A*X). dm(1, _) -> 1; dm(N, L) -> {Ln, Start} = lse(N, L), D = (N - Start) div Ln + pow(10, Ln-1), list_to_integer([lists:nth((N-Start) rem Ln, integer_to_list(D))]). lse(N,[H|T]) -> lse(N,T,H,1). lse(N,[H|_],P,I) when H > N -> {I+1, P}; lse(N,[H|T],_,I) -> lse(N,T,H,I+1). |
performance check:
1 2 3 4 5 6 | mkh@mkh-xps:~/work/mblog/pr_euler/p40$ time ./p40.erl Answer: 210 real 0m0.151s user 0m0.136s sys 0m0.029s |