Erlang:两个列表是否具有公共元素

时间:2019-05-30 08:49:17

标签: erlang

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,以检查两个列表是否具有相同的元素?

4 个答案:

答案 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版本。