我试图获得关于我上面提出的算法性能的更准确信息,阅读了 Stemm 非常有趣的解决方案,我决定使用 tc:timer/3 函数。大骗局:o)。在我的笔记本电脑上,我的时间准确性非常差。所以我决定留下我的 corei5(2 核 * 2 线程)+ 2Gb DDR3 + windows XP 32bit 来使用我的家用电脑:Phantom(6 核)+ 8Gb + Linux 64bit。
现在 tc:timer 按预期工作,我能够操作 100 000 000 个整数的列表。我能够看到我在每一步调用 length 函数都浪费了很多时间,所以我稍微重构了代码以避免它:
-module(finder).
-export([test/2,find/2,insert/2,remove/2,new/0]).
%% interface
new() -> {0,[]}.
insert(V,{S,L}) ->
{R,P} = locate(V,L,S,undefined,-1),
insert(V,R,P,L,S).
find(V,{S,L}) ->
locate(V,L,S,undefined,-1).
remove(V,{S,L}) ->
{R,P} = locate(V,L,S,undefined,-1),
remove(V,R,P,L,S).
remove(_,_,-1,L,S) -> {S,L};
remove(V,V,P,L,S) ->
{L1,[V|L2]} = lists:split(P,L),
{S-1,L1 ++ L2};
remove(_,_,_,L,S) ->{S,L}.
%% local
insert(V,V,_,L,S) -> {S,L};
insert(V,_,-1,L,S) -> {S+1,[V|L]};
insert(V,_,P,L,S) ->
{L1,L2} = lists:split(P+1,L),
{S+1,L1 ++ [V] ++ L2}.
locate(_,[],_,R,P) -> {R,P};
locate (V,L,S,R,P) ->
S1 = S div 2,
S2 = S - S1 -1,
{L1,[M|L2]} = lists:split(S1, L),
locate(V,R,P,S1+1,L1,S1,M,L2,S2).
locate(V,_,P,Le,_,_,V,_,_) -> {V,P+Le};
locate(V,_,P,Le,_,_,M,L2,S2) when V > M -> locate(V,L2,S2,M,P+Le);
locate(V,R,P,_,L1,S1,_,_,_) -> locate(V,L1,S1,R,P).
%% test
test(Max,Iter) ->
{A,B,C} = erlang:now(),
random:seed(A,B,C),
L = {Max+1,lists:seq(0,100*Max,100)},
Ins = test_insert(L,Iter,[]),
io:format("insert:~n~s~n",[stat(Ins,Iter)]),
Fin = test_find(L,Iter,[]),
io:format("find:~n ~s~n",[stat(Fin,Iter)]).
test_insert(_L,0,Res) -> Res;
test_insert(L,I,Res) ->
V = random:uniform(1000000000),
{T,_} = timer:tc(finder,insert,[V,L]),
test_insert(L,I-1,[T|Res]).
test_find(_L,0,Res) -> Res;
test_find(L,I,Res) ->
V = random:uniform(1000000000),
{T,_} = timer:tc(finder,find,[V,L]),
test_find(L,I-1,[T|Res]).
stat(L,N) ->
Aver = lists:sum(L)/N,
{Min,Max,Var} = lists:foldl(fun (X,{Mi,Ma,Va}) -> {min(X,Mi),max(X,Ma),Va+(X-Aver)*(X-Aver)} end, {999999999999999999999999999,0,0}, L),
Sig = math:sqrt(Var/N),
io_lib:format(" average: ~p,~n minimum: ~p,~n maximum: ~p,~n sigma : ~p.~n",[Aver,Min,Max,Sig]).
以下是一些结果。
1> 查找器:测试(1000,10)。插入:
平均:266.7,
最低:216,
最大:324,
西格玛:36.98121144581393。
寻找:
average: 136.1,
最低:105,
最大:162,
西格玛:15.378231367748375。
行
2> 查找器:测试(100000,10)。
插入:
平均:10096.5,
最低:9541,
最大值:12222,
西格玛:762.5642595873478。
寻找:
average: 5077.4,
最低:4666,
最大值:6937,
西格玛:627.126494417195。
行
3> 查找器:测试(1000000,10)。
插入:
平均:109871.1,
最低:94747,
最大值:139916,
西格玛:13852.211285206417。
发现:平均:40428.0,
最低:31297,
最大值:56965,
西格玛:7797.425562325042。
行
4> 查找器:测试(100000000,10)。
插入:
平均:8067547.8,
最低:6265625,
最大值:16590349,
西格玛:3199868.809140206。
寻找:
average: 8484876.4,
最低:5158504,
最大值:15950944,
西格玛:4044848.707872872。
行
在 100 000 000 列表上,它很慢,并且多进程解决方案无法帮助解决这种二分法算法......这是该解决方案的一个弱点,但如果您有多个进程并行请求找到最接近的值,它无论如何都可以使用多核。
帕斯卡。