a = [1, 2, 3],
b = [4, 5, 6],
c = [1, 7, 8],
has_common_element(a, b)
为false
,因为两个数组之间没有公共元素。
has_common_element(a, c)
为true
,因为两个数组之间至少有一个公共元素(1)。
我可以使用lists:member
来检查列表中是否有单个元素。如何在Erlang中实现has_common_element
,以检查两个列表是否具有相同的元素?
答案 0 :(得分:2)
从列表中创建集合并使用sets:is_disjoint
:
has_common_member(L1, L2) ->
not(sets:is_disjoint(sets:from_list(L1), sets:from_list(L2))).
答案 1 :(得分:1)
您可以使用以下列表理解:
test(A, B)->
[] /= [X || X <- A, Y <- B, X == Y].
shell中的结果:
1> A = [1, 2, 3].
[1,2,3]
2> B = [4, 5, 6].
[4,5,6]
3> C = [1, 7, 8].
[1,7,8]
4> foo:test(A, B).
false
5> foo:test(A, C).
true
答案 2 :(得分:0)
@bxdoan和@choroba解决方案都可以使用,并且非常简洁。您没有提供有关列表大小及其内容的信息,这可能很重要。
具有列表理解功能的解决方案将测试一个列表,该列表的大小是2个输入列表的叉积,因此对于大输入列表而言,它变得非常慢。带集合的解决方案的增长速度较慢。我向您提出另一个建议,它不那么简洁,但是对于较长的列表,其表现更好。我还添加了一个使用list:member的解决方案(效率不高),另一个使用map的解决方案(类似于sort)。
-module (inter).
-export ([test/2,s1/2,s2/2,s3/2,s4/2,s5/2]).
test(Method,Size) ->
A = [rand:uniform(Size*10000) || _ <- lists:duplicate(Size, 0)],
% B may contain common element with A
B = [rand:uniform(Size*10000) || _ <- lists:duplicate(Size, 0)],
% C is by construction disjoint with A
C = [rand:uniform(Size*10000)+Size*10000 || _ <- lists:duplicate(Size, 0)],
io:format("test algorithm ~s~n",[method(Method)]),
{test(Method,A,B),test(Method,A,C)}.
test(M,L1,L2) ->
timer:tc(?MODULE, M, [L1,L2]).
method(s1) ->
"List comprehension";
method(s2) ->
"List sorted";
method(s3) ->
"List to set";
method(s4) ->
"List to map";
method(s5) ->
"lists:member".
s1(L1,L2) ->
[] =/= [X || X <- L1, Y <- L2, X =:= Y].
s2(L1,L2) ->
s2_help(lists:sort(L1),lists:sort(L2)).
s2_help([],_) -> false;
s2_help(_,[]) -> false;
s2_help([_A|_],[_A|_]) -> true;
s2_help([H1|T1],[H2|T2]) when H1 > H2 -> s2_help([H1|T1],T2);
s2_help(L1,L2) -> s2_help(tl(L1),L2).
s3(L1,L2) ->
not(sets:is_disjoint(sets:from_list(L1), sets:from_list(L2))).
s4(L1,L2) ->
s4_help(lists:foldl(fun(X,Acc) -> maps:put(X, 0, Acc) end,#{},L1),L2).
s4_help(_,[]) -> false;
s4_help(Map,[H|T]) ->
case maps:is_key(H, Map) of
true -> true;
false -> s4_help(Map,T)
end.
s5([],_) -> false;
s5([H|T],L2) ->
case lists:member(H, L2) of
true -> true;
false -> s5(T,L2)
end.
然后是结果:
55> c(inter).
{ok,inter}
56> inter:test(s1,100).
test algorithm List comprehension
{{0,false},{0,false}}
57> inter:test(s2,100).
test algorithm List sorted
{{0,false},{0,false}}
58> inter:test(s3,100).
test algorithm List to set
{{0,false},{0,false}}
59> inter:test(s1,1000).
test algorithm List comprehension
{{16000,true},{0,false}}
60> inter:test(s2,1000).
test algorithm List sorted
{{0,false},{0,false}}
61> inter:test(s3,1000).
test algorithm List to set
{{0,false},{0,false}}
62> inter:test(s1,10000).
test algorithm List comprehension
{{468999,false},{484000,false}}
63> inter:test(s2,10000).
test algorithm List sorted
{{15000,false},{0,false}}
64> inter:test(s3,10000).
test algorithm List to set
{{31000,false},{32000,false}}
65> inter:test(s1,100000).
test algorithm List comprehension
{{48515953,true},{48030953,false}}
66> inter:test(s2,100000).
test algorithm List sorted
{{62000,true},{78000,false}}
67> inter:test(s3,100000).
test algorithm List to set
{{1233999,true},{1296999,false}}
...
69> inter:test(s4,100).
test algorithm List to map
{{0,false},{0,false}}
70> inter:test(s4,1000).
test algorithm List to map
{{0,false},{0,false}}
71> inter:test(s4,10000).
test algorithm List to map
{{0,false},{16000,false}}
72> inter:test(s4,100000).
test algorithm List to map
{{62000,true},{78000,false}}
73> inter:test(s5,100).
test algorithm lists:member
{{0,false},{0,false}}
74> inter:test(s5,1000).
test algorithm lists:member
{{0,false},{0,false}}
75> inter:test(s5,10000).
test algorithm lists:member
{{31000,true},{171999,false}}
76> inter:test(s5,100000).
test algorithm lists:member
{{921000,true},{19031980,false}}
77>
答案 3 :(得分:0)
您可以使用erlang:--/2
运算符来快速解决问题:
has_common_element(A, B) when length(B) > length(A) ->
has_common_element(B, A); % not necessary optimization but helps for very uneven sets
has_common_element(A, B) ->
A =/= A -- B.
在较旧的版本中它将是O(N * M),但是由于OTP21,我认为它会变成O((N + M)* logM)(我现在找不到相关的发行说明)。在使用的版本中为自己做基准测试,并正确选择。
无论如何,您可以使用地图来制作非常快速的O((N + M)* logM)解决方案:
has_common_element(A, B) when length(B) > length(A) ->
has_common_element(B, A); % not necessary optimization but helps for very uneven sets
has_common_element(A, B) ->
M = maps:from_list([{X,[]} || X <- B]),
any_in_map(M, A).
any_in_map(_, []) -> false;
any_in_map(M, [H|T]) ->
maps:is_key(H, M) orelse any_in_map(M, T).
并且使用process_dictionary有一个较讨厌的先前版本。当您可以很好地控制列表元素并且process_dictionary不会打扰您时(通常在OTP处理中不是个好主意),您可以直接使用它。
has_common_element_dirty(A, B) when length(B) > length(A) ->
has_common_element_dirty(B, A); % not necessary optimization but helps for very uneven sets
has_common_element_dirty(A, B) ->
[put(X, []) || X <- B],
any_in_dict(A).
any_in_dict([]) -> false;
any_in_dict([H|T]) ->
[] =:= get(H) orelse any_in_dict(T).
您可以将其包装在链接过程中并使其安全:
has_common_element(A, B) ->
Self = self(),
Ref = make_ref(),
Pid = spawn_link(fun() -> Self ! {Ref, has_common_element_dirty(A, B)} end),
receive {Ref, Result} -> Result end.
为您的应用程序确定基准并列出大小,并进行相应选择,因为所有这些解决方案均具有不同的GC和原始性能特征。通常,第一个应该足够。
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
Eshell V10.4 (abort with ^G)
1> Size=100000, A = [rand:uniform(Size*10000) || _ <- lists:duplicate(Size, 0)], B = [rand:uniform(Size*10000) || _ <- lists:duplicate(Size, 0)], C = [rand:uniform(Size*10000)+Size*10000 || _ <- lists:duplicate(Size, 0)], ok.
ok
2> timer:tc(fun() -> A =/= A -- B end).
{77943,true}
3> timer:tc(fun() -> A =/= A -- C end).
{44325,false}
比较两个100k列表不到100毫秒。与Pascal的解决方案中最好的(s2)相当或更好。注意OTP22版本。