[erlang-questions] speeding up 3N+1
June Kim
juneaftn@REDACTED
Sat Apr 7 15:57:14 CEST 2007
Well, I tried it with ets:
Replace the three cache functions with the following:
newCache()->
Cache=ets:new(test,[set]),
ets:insert(Cache,{1,1}),
Cache.
store(K,V,Cache)->
ets:insert(Cache,{K,V}),
Cache.
find(K,Cache)->
case ets:lookup(Cache,K) of
[] -> error;
[{K,V}] -> {ok,V}
end.
Now max(1,100000) takes about 0.9 secs, and max(1,1000000) takes about 10 secs.
Exploiting the trick in the process dictionary version(when N is odd,
3N+1 is even), for one million, it reduces to about 8 secs.
It's a huge improvement, but still inferior to the process dictionary
version. Moreover, ets isn't functional data structure(with
side-effects).
So the process dictionary is the king?
2007/4/7, June Kim <juneaftn@REDACTED>:
> Hello,
>
> First of all, please keep in mind that I am a newbie in Erlang. (sorry
> if the codes annoy you)
>
> Please have a look at this famous problem. http://acm.uva.es/p/v1/100.html
>
> The input number can be upto 1,000,000. (let's just suppose 1 million
> is included, even though the problem states the maximal input is less
> than 1 million) Let's see the worst case(when the start/end pair is
> {1, 1000000}) time performance of the program.
>
> Following is a trial at solving this problem:
>
> -module(tnp1_process_dict).
> -compile(export_all).
>
> cycle(1) ->
> 1;
> cycle(N) ->
> case get(N) of
> undefined ->
> SN = if
> N rem 2 =:= 0 ->
> 1 + cycle(N div 2);
> true ->
> 2 + cycle((3 * N + 1) div 2)
> end,
> put(N, SN),
> SN;
> Cached ->
> Cached
> end.
>
> max(A, B) ->
> lists:max(lists:map(fun cycle/1, lists:seq(A, B))).
>
> Actually, it's from a friend of mine, who is also in the same Erlang
> community(we are all just novices though) with me. It's quite
> effiecient.
>
> 1> timer:tc(tnp1_process_dict,max,[1,1000000]).
> {2547000,525}
>
> Less than 3 seconds on my sluggish machine. Good.
>
> However, I didn't quite like the solution. It uses the process
> dictionary. From reading Erlang books and manuals, I thought the
> process dictionary should be the last resort for this kind of usage. I
> thought I could deal the problem without using the process dictionary,
> without trading off the speed.
>
> Following is my trial:
>
> -module(tnp1_functional).
> -compile(export_all).
>
> newCache()->
> dict:from_list([{1,1}]).
> store(K,V,Cache)->
> dict:store(K,V,Cache).
> find(K,Cache)->
> dict:find(K,Cache).
>
> nn(N) when N rem 2 =:= 0 ->
> N div 2;
> nn(N) ->
> 3*N+1.
>
> update([],_,Cache)->
> Cache;
> update([H|ToStore],V,Cache)->
> Cache2=store(H,V+1,Cache),
> update(ToStore,V+1,Cache2).
>
> cl(N,Cnt,Cache,ToStore)->
> case find(N,Cache) of
> {ok,V} ->
> Cache2=update(ToStore,V,Cache),
> {Cnt+V,Cache2};
> error ->
> cl(nn(N),Cnt+1,Cache,[N|ToStore])
> end.
>
> maxcl(From,To,Cache,Max) when From>To ->
> {Max,Cache};
> maxcl(From,To,Cache,Max)->
> {Cl,Cache2}=cl(From,0,Cache,[]),
> maxcl(From+1,To,Cache2,lists:max([Cl,Max])).
>
> maxcl(From,To)->
> Cache=newCache(),
> maxcl(From,To,Cache,1).
>
> max(From,To)->
> {V,_}=maxcl(From,To),
> V.
>
> It's much longer. And much slower; I'd say excruciatingly.
>
> 1> timer:tc(tnp1_functional,max,[1,100000]).
> {7953000,351}
>
> See? It's 100,000, not one million, even. When I ran it with one
> million, it didn't end at least in a couple of minutes.
>
> Maybe my choice of using dict is wrong? Could ets be substantially
> better? Or my approach totally wrong? I don't know yet. (I'll
> experiment with other data structures and approaches, though)
>
> Okay, that's it. I would appreciate any suggestions for improvement.
>
> June
>
More information about the erlang-questions
mailing list