[erlang-questions] speeding up 3N+1

June Kim juneaftn@REDACTED
Sat Apr 7 16:55:38 CEST 2007


2007/4/7, Vance Shipley <vances@REDACTED>:
> On Sat, Apr 07, 2007 at 10:18:59PM +0900, June Kim wrote:
> }  Please have a look at this famous problem. http://acm.uva.es/p/v1/100.html
>
> June,
>
> In functional programming you pass arguments instead of using
> global variables.
>
> -module(threeNplus1).
> -export([do/1]).
>
> do(1) ->
>        io:fwrite(" 1~n");
> do(N) when N rem 2 /= 0 ->
>        io:fwrite(" ~b", [N]),
>        do(3 * N + 1);
> do(N) ->
>        io:fwrite(" ~b", [N]),
>        do(N div 2).
>
> 1> threeNplus1:do(22).
>  22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
> ok
>
> }  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.
>
> The really big problem above is in tail recursion.  You don't
> want to have to wait for the answer to cycle(N div 2) before
> knowing the answer to cycle(N).
>
>        -Vance
>
>

Yes. I am well aware of those issues. (the code isn't mine, but from a
friend of mine who's into functional programming for the first time
with Erlang -- I have programmed in other FP languages before)

His code uses global variable(process dictionary) and not
tail-recursive. However, it's the fastest. (even in a "functional
programming" language -- ironic, isn't it?)

My version using dict, which is also included in my original posting(I
don't think you have seen it yet), is tail-recursive and functional,
but slow.

I did some experimentations.

%%<data structure> <time(sec) for upto 10^5>/<time for upto 10^6>

%%dict 7s/?s

newCache()->
   dict:from_list([{1,1}]).
store(K,V,Cache)->
   dict:store(K,V,Cache).
find(K,Cache)->
   dict:find(K,Cache).




%%ets/set 0.1s/10s

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.


%%gb_trees 2.6s/32s
newCache()->
   T1=gb_trees:empty(),
   gb_trees:enter(1,1,T1).
store(K,V,Cache)->
   gb_trees:enter(K,V,Cache).
find(K,Cache)->
   case gb_trees:lookup(K,Cache) of
       none -> error;
       {value,V} -> {ok,V}
   end.


%%process dict 0.4s/5s
newCache()->
   erase(),
   put(1,1),
   cache.
store(K,V,_Cache)->
   put(K,V),
   cache.
find(K,_Cache)->
   case get(K) of
       undefined -> error;
       V -> {ok,V}
   end.

Could you help me improving the speed of these codes with FP approach?



More information about the erlang-questions mailing list