Project Euler Problem 10
Summation of primes
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
Link to original description
Source code examples on Github
Erlang version 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p10 % vim:syn=erlang -mode(compile). main(_) -> Answer = sum(2000000,2,0), io:format("Answer: ~p ~n", [Answer]). sum(Max, Start, Sum) when Start =< Max -> case isprime(Start, 2, math:sqrt(Start)) of true -> sum(Max, Start+1, Sum + Start); _ -> sum(Max, Start+1,Sum) end; sum(_, _, Sum) -> Sum. isprime(N,PFactor,Max) when PFactor =< Max, N rem PFactor =:= 0 -> false; isprime(N,PFactor,Max) when PFactor =< Max -> isprime(N,PFactor+1,Max); isprime(_, _, _) -> true. |
performance test:
1 2 3 4 5 6 7 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10.erl Answer: 142913828922 real 0m8.845s user 0m8.837s sys 0m0.042s |
Erlang version 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p10_1 % vim:syn=erlang -mode(compile). main(_) -> Answer = lists:foldl(fun(E,A)-> E+A end, 0, sieve(2000000)), io:format("Answer: ~p ~n", [Answer]). sieve(N) -> [1] ++ sieve(lists:seq(2, N), N). sieve([Head|L], N) when Head * 2 < N -> [Head] ++ sieve(L -- lists:seq(Head * 2, N, Head), N); sieve([Head|L], _) -> [Head] ++ L. |
performance test:
1 2 3 4 5 6 7 8 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_1.erl ^C real 1m28.705s user 1m28.664s sys 0m0.088s |
so, performance test is failed, i stopped script after 1 and half minute. This implementation working good for small number, but really slow.
Erlang version 3
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 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p10_2 % vim:syn=erlang -mode(compile). sieve(Candidates,SearchList,Primes,_Maximum) when length(SearchList) == 0 -> ordsets:union(Primes,Candidates); sieve(Candidates,SearchList,Primes,Maximum) -> H = lists:nth(1,string:substr(Candidates,1,1)), Reduced1 = ordsets:del_element(H, Candidates), {Reduced2, ReducedSearch} = remove_multiples_of(H, Reduced1, SearchList), NewPrimes = ordsets:add_element(H,Primes), sieve(Reduced2, ReducedSearch, NewPrimes, Maximum). remove_multiples_of(Number,Candidates,SearchList) -> NewSearchList = ordsets:filter( fun(X) -> X >= Number * Number end, SearchList), RemoveList = ordsets:filter( fun(X) -> X rem Number == 0 end, NewSearchList), {ordsets:subtract(Candidates, RemoveList), ordsets:subtract(NewSearchList, RemoveList)}. main(_) -> N = 2000000, CandidateList = lists:seq(3,N,2), Candidates = ordsets:from_list(CandidateList), ResultSet = ordsets:add_element(2,sieve(Candidates,Candidates,ordsets:new(),N)), io:format("Answer: ~p ~n", [lists:foldl(fun(E,A)->E+A end, 0, ResultSet)]). |
this implementation i found on Rosettacode.org and decide to try it
performance test:
1 2 3 4 5 6 7 8 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_2.erl Answer: 142913828922 real 0m5.608s user 0m5.571s sys 0m0.083s |
it was better but i decide to try to use ets tables instead dict, see next example
Erlang version 4
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 31 32 33 34 35 36 37 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p10_3 % vim:syn=erlang -mode(compile). main(_) -> N = 2000000, ets:new(comp, [public, named_table]), ets:new(prim, [public, named_table]), composite(N), primes(N), Answer = lists:foldl(fun({E,_},A)-> E+A end, 0, ets:tab2list(prim)), io:format("Answer: ~p ~n", [Answer]). primes(N) -> primes(2, N). primes(I,N) when I =< N -> case ets:lookup(comp, I) of [] -> ets:insert(prim, {I,1}) ;_ -> ok end, primes(I+1, N); primes(I,N) when I > N -> ok. composite(N) -> composite(N,2,round(math:sqrt(N))). composite(N,I,M) when I =< M -> case ets:lookup(comp, I) of [] -> comp_i(I*I, I, N) ;_ -> ok end, composite(N,I+1,M); composite(_,_,_) -> ok. comp_i(J, I, N) when J =< N -> ets:insert(comp, {J, 1}), comp_i(J+I, I, N); comp_i(J, _, N) when J > N -> ok. |
it have pretty same preformance:
1 2 3 4 5 6 7 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_3.erl Answer: 142913828922 real 0m5.543s user 0m5.501s sys 0m0.068s |
and after that i decide to let this implementation to use all CPU cores, see next example. This script getting number of available CPU cores by erlang:system_info(schedulers) and spread calculation between them.
Erlang version 5. Sieve of Eratosthenes, simultaneous, multi-core implementation
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #!/usr/bin/env escript %% -*- erlang -*- %%! -smp enable -sname p10_4 % vim:syn=erlang -mode(compile). main(_) -> N = 20000000, ets:new(comp, [public, named_table, {write_concurrency, true} ]), ets:new(prim, [public, named_table, {write_concurrency, true}]), composite_mc(N), io:format("com ok ~n", []), primes_mc(N), Answer = lists:foldl(fun({E,_},A)-> E+A end, 0, ets:tab2list(prim)), io:format("Answer: ~p ~n", [Answer]). primes_mc(N) -> case erlang:system_info(schedulers) of 1 -> primes(N); C -> launch_primes(lists:seq(1,C), C, N, N div C) end. launch_primes([1|T], C, N, R) -> P = self(), spawn(fun()-> primes(2,R), P ! {ok, prm} end), launch_primes(T, C, N, R); launch_primes([H|[]], C, N, R)-> P = self(), spawn(fun()-> primes(R*(H-1)+1,N), P ! {ok, prm} end), wait_primes(C); launch_primes([H|T], C, N, R) -> P = self(), spawn(fun()-> primes(R*(H-1)+1,R*H), P ! {ok, prm} end), launch_primes(T, C, N, R). wait_primes(0) -> ok; wait_primes(C) -> receive {ok, prm} -> wait_primes(C-1) after 1000 -> wait_primes(C) end. primes(N) -> primes(2, N). primes(I,N) when I =< N -> case ets:lookup(comp, I) of [] -> ets:insert(prim, {I,1}) ;_ -> ok end, primes(I+1, N); primes(I,N) when I > N -> ok. composite_mc(N) -> composite_mc(N,2,round(math:sqrt(N)),erlang:system_info(schedulers)). composite_mc(N,I,M,C) when I =< M, C > 0 -> C1 = case ets:lookup(comp, I) of [] -> comp_i_mc(I*I, I, N), C-1 ;_ -> C end, composite_mc(N,I+1,M,C1); composite_mc(_,I,M,_) when I > M -> ok; composite_mc(N,I,M,0) -> receive {ok, cim} -> composite_mc(N,I,M,1) after 1000 -> composite_mc(N,I,M,0) end. comp_i_mc(J, I, N) -> Parent = self(), spawn(fun() -> comp_i(J, I, N), Parent ! {ok, cim} end). comp_i(J, I, N) when J =< N -> ets:insert(comp, {J, 1}), comp_i(J+I, I, N); comp_i(J, _, N) when J > N -> ok. |
performance
1 2 3 4 5 6 7 8 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_4.erl Answer: 142913828922 real 0m2.436s user 0m8.029s sys 0m0.135s |
much better. And i test 3,4,5 scripts for 20 000 000 primes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_2.erl Answer: 12272577818052 real 1m59.066s user 1m56.761s sys 0m2.359s mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_3.erl Answer: 12272577818052 real 1m6.099s user 1m5.950s sys 0m0.213s mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10_4.erl Answer: 12272577818052 real 0m29.205s user 1m43.910s sys 0m1.373s |
so, multicore ets version won. But python version almost 10 times faster :)
Python version
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/usr/bin/python sieve = [True] * 2000000 def mark(sieve, x): for i in xrange(x+x, len(sieve), x): sieve[i] = False for x in xrange(2, int(len(sieve) ** 0.5) + 1): if sieve[x]: mark(sieve, x) print sum(i for i in xrange(2, len(sieve)) if sieve[i]) |
performance test:
1 2 3 4 5 6 7 8 | mkh@mkh-xps:~/work/mblog/pr_euler/p10$ time ./p10.py 142913828922 real 0m0.354s user 0m0.351s sys 0m0.004s |