我做的事情太糟糕了,但我不知道如何做得更好。
我正在形成名为SomeList的List元素的所有成对总和,但我不想看到重复(我想我想要"所有可能的成对总和"):
sets:to_list(sets:from_list([A+B || A <- SomeList, B <- SomeList]))
SomeList不包含重复项。
这样可行,但效率非常低,因为设置转换之前的原始列表是GIGANTIC。
有更好的方法吗?
答案 0 :(得分:3)
您只需使用lists:usort/1
lists:usort([X+Y || X <- L, Y <- L]).
如果有重复的机会非常高,那么您可以使用2个循环生成总和并将总和存储在 ets 集中(或使用 map ,I没有检查两者的表现。
7> Inloop = fun Inloop(_,[],_) -> ok; Inloop(Store,[H|T],X) -> ets:insert(Store,{X+H}), Inloop(Store,T,X) end.
#Fun<erl_eval.42.54118792>
8> Outloop = fun Outloop(Store,[],_) -> ok; Outloop(Store,[H|T],List) -> Inloop(Store,List,H), Outloop(Store,T,List) end.
#Fun<erl_eval.42.54118792>
9> Makesum = fun(L) -> S = ets:new(temp,[set]), Outloop(S,L,L), R =ets:foldl(fun({X},Acc) -> [X|Acc] end,[],S), ets:delete(S), R end.
#Fun<erl_eval.6.54118792>
10> Makesum(lists:seq(1,10)).
[15,13,8,11,20,14,16,12,7,3,10,9,19,18,4,17,6,2,5]
11> lists:sort(Makesum(lists:seq(1,10))).
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
12>
答案 1 :(得分:2)
此模块允许您比较使用列表理解,设置或 ets 时的执行时间。您当然可以为此比较添加其他功能:
-module(pairwise).
-export([start/2]).
start(Type, X) ->
L = lists:seq(1, X),
timer:tc(fun do/2, [Type, L]).
do(compr, L) ->
sets:to_list(sets:from_list([A+B || A <- L, B <- L]));
do(set, L) ->
F = fun(Sum, Set) -> sets:add_element(Sum, Set) end,
R = fun(Set) -> sets:to_list(Set) end,
do(L, L, sets:new(), {F, R});
do(ets, L) ->
F = fun(Sum, Tab) -> ets:insert(Tab, {Sum}), Tab end,
R = fun(Tab) ->
Fun = fun({X}, Acc) -> [X|Acc] end,
Res = ets:foldl(Fun, [], Tab),
ets:delete(Tab),
Res
end,
do(L, L, ets:new(?MODULE, []), {F, R}).
do([A|AT], [B|BT], S, {F, _} = Funs) -> do([A|AT], BT, F(A+B, S), Funs);
do([_AT], [], S, {_, R}) -> R(S);
do([_A|AT], [], S, Funs) -> do(AT, AT, S, Funs).
结果:
36> {_, Res1} = pairwise:start(compr, 20).
{282,
[16,32,3,19,35,6,22,38,9,25,12,28,15,31,2,18,34,5,21,37,8,
24,40,11,27,14,30|...]}
37> {_, Res2} = pairwise:start(set, 20).
{155,
[16,32,3,19,35,6,22,38,9,25,12,28,15,31,2,18,34,5,21,37,8,
24,40,11,27,14,30|...]}
38> {_, Res3} = pairwise:start(ets, 20).
{96,
[15,25,13,8,21,24,40,11,26,20,14,28,23,16,12,39,34,36,7,32,
35,3,33,10,9,19,18|...]}
39> R1=lists:usort(Res1), R2=lists:usort(Res2), R3=lists:usort(Res3).
[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|...]
40> R1 = R2 = R3.
[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|...]
最后一行是比较所有函数返回相同的结果,但排序方式不同。
每个结果元组中的第一个数字是从timer:tc(fun do/2, [Type, L]).
返回的执行时间。在这个例子中,列表理解为282,集为155, ets 为96。
答案 2 :(得分:1)
一种有效的方法是使用foldl而不是列表理解,因为在这种情况下你在每一步上都有一个状态
sets:to_list(
lists:foldl(fun(A, S1) ->
lists:foldl(fun(B, S2) ->
sets:add_element(A+B, S2)
end, S1, SomeListA)
end, sets:new(), SomeListB)).
答案 3 :(得分:0)
此解决方案使其保持相对较快,并使用尽可能多的预编写库代码。
请注意,我使用list:zip / 2而不是numeric +,仅用于说明此方法适用于任何类型的唯一列表的非重复排列。你可能只关心算术,但如果你想要更多,这可以做到。
-export([permute_unique/1]).
permute_unique([]) ->
[];
permute_unique([A|Ab]) ->
lists:zip(lists:duplicate(length(Ab)+1, A), [A|Ab])
++
permute_unique(Ab).
%to sum integers, replace the lists:zip... line with
% [B+C || {B,C} <- lists:zip(lists:duplicate(length(Ab)+1, A), [A|Ab])]
%to perform normal arithmetic and yield a numeric value for each element
我不确定你认为什么是巨大的 - 你将在置换列表中得到N *(N + 1)/ 2个元素,以获得N个原始元素的唯一列表,所以这个变得非常快。
我做了一些基本的性能测试,使用Intel(Haswell)Core i7 @ 4GHz和32GB内存,运行Erlang / OTP 17 64位。
根据计时器,列表中的5001个元素占用2到5秒:tc / 1。
列表中的10001个元素需要15到17秒,并且需要大约9GB的内存。这将生成一个包含50,015,001个元素的列表。
列表中的15001个元素需要21到25秒,并且需要大约19GB的内存。
列表中的20001个元素在一次运行中耗时49秒,并且在大约30GB的内存中达到峰值,结果中有大约2亿个元素。这是我可以测试的极限。