Erlang算法返回一组整数,当它们相加时等于X

Erlang算法返回一组整数,当它们相加时等于X,erlang,Erlang,例如,给定一个列表 [1, 45, 1, 99, 3, 5, 95, 1, 5, 97, 3, 99, 87] 返回该列表中总计为100的对。当项目被消耗时,不应重新评估。在上述情况下,输出为: [{1,99}, {1, 99}, {3,97}, {5,95}] 该列表不被假定为已排序,因为在示例中,重复对应该起作用 了解BigO的复杂性、空间/时间,了解各种方法的优缺点是很好的。我对算法及其性能不是很精通,但以下是我所了解的。我的总体想法是将列表分成两个子列表,元素>=50和=50和

例如,给定一个列表

[1, 45, 1, 99, 3, 5, 95, 1, 5, 97, 3, 99, 87]
返回该列表中总计为100的对。当项目被消耗时,不应重新评估。在上述情况下,输出为:

[{1,99}, {1, 99}, {3,97}, {5,95}]
该列表不被假定为已排序,因为在示例中,重复对应该起作用


了解BigO的复杂性、空间/时间,了解各种方法的优缺点是很好的。

我对算法及其性能不是很精通,但以下是我所了解的。我的总体想法是将列表分成两个子列表,元素>=50和<50。至少这可以确保您不必在每个列表中查找对,而只需在它们之间查找对:

-module(test).
-export([add_to_100/1]).

add_to_100([]) -> [];
add_to_100(List) ->
    {L1, L2} = split(List, [], []),
    find_match(L1, L2).

%% Do matching between 2 lists
find_match([], _) -> [];
find_match(_, []) -> [];
find_match([H1|T1], L2) ->
    case match_element(H1, L2, []) of
        {{A, B}, Left} -> [{A, B} | find_match(T1, Left)];
        {{}, Left} -> find_match(T1, Left)
    end.

%% Match every element with list of integers.
%% Return match pair and exclude matched element from list of integers.
match_element(_, [], Rest) -> {{}, Rest};
match_element(E, [H|T], Rest) when E + H == 100 -> {{E,H}, Rest ++ T};
match_element(E, [H|T], Rest) -> match_element(E, T, [H|Rest]).

%% Split input list into two sublists - L1 < 50 and L2 >= 50
split([], L1, L2) -> {L1, L2};
split([H|T], L1, L2) when H < 50 -> split(T, [H|L1], L2);
split([H|T], L1, L2) -> split(T, L1, [H|L2]).

在实现它之后,我意识到它不是在处理对{50,50}——无论如何,你可以将它作为一个特例添加到这个算法中。尽管这个解决方案还不完善,但它应该能让您深入了解Erlang中的模式匹配、尾部递归和列表操作,这在解决这个问题时是绝对需要的。

我对算法及其性能不是很精通,但以下是我所了解到的。我的总体想法是将列表分成两个子列表,元素>=50和<50。至少这可以确保您不必在每个列表中查找对,而只需在它们之间查找对:

-module(test).
-export([add_to_100/1]).

add_to_100([]) -> [];
add_to_100(List) ->
    {L1, L2} = split(List, [], []),
    find_match(L1, L2).

%% Do matching between 2 lists
find_match([], _) -> [];
find_match(_, []) -> [];
find_match([H1|T1], L2) ->
    case match_element(H1, L2, []) of
        {{A, B}, Left} -> [{A, B} | find_match(T1, Left)];
        {{}, Left} -> find_match(T1, Left)
    end.

%% Match every element with list of integers.
%% Return match pair and exclude matched element from list of integers.
match_element(_, [], Rest) -> {{}, Rest};
match_element(E, [H|T], Rest) when E + H == 100 -> {{E,H}, Rest ++ T};
match_element(E, [H|T], Rest) -> match_element(E, T, [H|Rest]).

%% Split input list into two sublists - L1 < 50 and L2 >= 50
split([], L1, L2) -> {L1, L2};
split([H|T], L1, L2) when H < 50 -> split(T, [H|L1], L2);
split([H|T], L1, L2) -> split(T, L1, [H|L2]).

在实现它之后,我意识到它不是在处理对{50,50}——无论如何,你可以将它作为一个特例添加到这个算法中。尽管这个解决方案还不完善,但它应该能让您深入了解Erlang中的模式匹配、尾部递归和列表操作,这在解决这个问题时是绝对需要的。

您可以使用带guard的列表理解来完成这项工作

find_pairs(List) ->
  [{X, Y} || X <- List,
             Y <- List,
             X+Y =:= 100].
关于这个算法的复杂性。find_pairs只在列表中出现,pop也是如此,所以它看起来是n^2。事实证明,事情并非如此简单。另外还有一个函数+,由于链表的性质,它可能有n个复杂度。所以最后,根据输入,我们可以体验n*2*n。在BigO中仍然是n^2,但值得注意的是,在算法中投入更多的工作或代码行并不能保证性能的提高


还有一个简单的修复方法具有其左元素的复杂性。因此,在pop中连接两个列表,而不是将Processed添加到Tail,可以将Tail添加到Processed。这样,当我们在k位置上找到值时,在k次调用之后,我们只需要在连接过程中做n-k次额外的工作。这保证了pop将完成n项以上的工作。对于整个算法,我们回到n^2,不依赖于数据顺序。

您可以使用列表理解和guard来完成

find_pairs(List) ->
  [{X, Y} || X <- List,
             Y <- List,
             X+Y =:= 100].
关于这个算法的复杂性。find_pairs只在列表中出现,pop也是如此,所以它看起来是n^2。事实证明,事情并非如此简单。另外还有一个函数+,由于链表的性质,它可能有n个复杂度。所以最后,根据输入,我们可以体验n*2*n。在BigO中仍然是n^2,但值得注意的是,在算法中投入更多的工作或代码行并不能保证性能的提高


还有一个简单的修复方法具有其左元素的复杂性。因此,在pop中连接两个列表,而不是将Processed添加到Tail,可以将Tail添加到Processed。这样,当我们在k位置上找到值时,在k次调用之后,我们只需要在连接过程中做n-k次额外的工作。这保证了pop将完成n项以上的工作。对于整个算法,我们回到n^2,不依赖于数据顺序。

在shell中,由于它使用递归匿名函数,它只适用于R17,但在早期erlang版本的模块中也可以

1> L = [1, 45, 1, 99, 3, 5, 95, 1, 5, 97, 3, 99, 87].
2> F= fun F([],R) -> R;
2>        F([H|T],R) -> Rest = lists:dropwhile(fun(X) -> X+H /= 100 end,T),                 
2>                      case Rest of
2>                          [] -> F(T,R);
2>                          [Found|_] -> F(lists:delete(Found,T),[{H,Found}|R])
2>                      end
2> end.
#Fun<erl_eval.36.90072148>
3> F(L,[]).                                                                                                   
[{5,95},{3,97},{1,99},{1,99}]
4> 
它准确地再现了如果我必须自己做的话我会做的事情:

以列表的第一个元素为例, 在列表的其余部分中查找一对, 如果没有配对,请使用列表的其余部分重新启动进程, 如果找到一对,记录该对,从列表的其余部分删除找到的元素,然后使用剩余的列表重新启动。 继续,直到列表显示为emty。 第一次实现是本着“让它工作”的精神,我做了一个更快的实现,首先对列表进行排序。以下模块实现了两个解决方案以及一些功能,用于测试和评估性能。在两种极端情况下,对于长列表,新解决方案的速度要快得多:没有解决方案或每个术语都属于一对。在我的电脑上,s2比s1快2500多倍,随机列出100000个元素

-module (sum).

-compile([export_all]).

s1(L,S) -> s1(L,S,[]).

s1([],_S,R) -> R;
s1([H|T],S,R) -> 
    Rest = lists:dropwhile(fun(X) -> X+H /= S end,T),                 
    case Rest of
        [] -> s1(T,S,R);
        [Found|_] -> s1(lists:delete(Found,T),S,[{H,Found}|R])
    end.

s2(L,S) ->
    Linc = lists:sort(L),
    Ldec = lists:reverse(Linc),
    s2(Linc,Ldec,S,[]).

s2(Linc,Ldec,_S,R) when Linc == [] ; Ldec == [] ; hd(Linc) > hd(Ldec) -> R;
s2([H,H|Linc],[H,H|Ldec],S,R) when S == 2*H -> s2(Linc,Ldec,S,[{H,H}|R]);
s2([H1|Linc],[H2|Ldec],S,R) when S == H1+H2, H1/=H2 -> s2(Linc,Ldec,S,[{H1,H2}|R]);
s2([H|Linc],Ldec,S,R) when H + hd(Ldec) < S -> s2(Linc,Ldec,S,R);
s2(Linc,[_H|Ldec],S,R) -> s2(Linc,Ldec,S,R).


%% Test and performance

compare(S1,S2) ->
    S = normalize(S1),
    S = normalize(S2).


normalize(S) -> lists:sort([{min(X,Y),max(X,Y)} || {X,Y} <- S]).

shuffle(P) when is_list(P) ->
    Max = length(P)*10000,
    {_,R}= lists:unzip(lists:keysort(1,[{random:uniform(Max),X} || X <- P])),
    R.

test1(S) -> % every term is part of a solution pair
    random:seed(erlang:now()),
    L = shuffle(lists:seq(1,S)),
    test(L,S+1).

test2(S) -> % no solution
    random:seed(erlang:now()),
    L = shuffle(lists:seq(1,S)),
    test(L,2*S).

test3(S) -> % random
    random:seed(erlang:now()),
    L = [random:uniform(2*S) || _ <- lists:seq(1,S)],
    test(L,S).

test(L,S) -> 
    {T1,S1} = timer:tc(sum,s1,[L,S]),
    {T2,S2} = timer:tc(sum,s2,[L,S]),
    compare(S1,S2),
    {T1,T2,S1}.

在shell中,由于它使用递归匿名函数,因此它仅适用于R17,但在具有早期erlang版本的模块中也可以

1> L = [1, 45, 1, 99, 3, 5, 95, 1, 5, 97, 3, 99, 87].
2> F= fun F([],R) -> R;
2>        F([H|T],R) -> Rest = lists:dropwhile(fun(X) -> X+H /= 100 end,T),                 
2>                      case Rest of
2>                          [] -> F(T,R);
2>                          [Found|_] -> F(lists:delete(Found,T),[{H,Found}|R])
2>                      end
2> end.
#Fun<erl_eval.36.90072148>
3> F(L,[]).                                                                                                   
[{5,95},{3,97},{1,99},{1,99}]
4> 
它准确地再现了如果我必须自己做的话我会做的事情:

以列表的第一个元素为例, 在列表的其余部分中查找一对, 我 如果没有配对,请使用列表的其余部分重新启动进程, 如果找到一对,记录该对,从列表的其余部分删除找到的元素,然后使用剩余的列表重新启动。 继续,直到列表显示为emty。 第一次实现是本着“让它工作”的精神,我做了一个更快的实现,首先对列表进行排序。以下模块实现了两个解决方案以及一些功能,用于测试和评估性能。在两种极端情况下,对于长列表,新解决方案的速度要快得多:没有解决方案或每个术语都属于一对。在我的电脑上,s2比s1快2500多倍,随机列出100000个元素

-module (sum).

-compile([export_all]).

s1(L,S) -> s1(L,S,[]).

s1([],_S,R) -> R;
s1([H|T],S,R) -> 
    Rest = lists:dropwhile(fun(X) -> X+H /= S end,T),                 
    case Rest of
        [] -> s1(T,S,R);
        [Found|_] -> s1(lists:delete(Found,T),S,[{H,Found}|R])
    end.

s2(L,S) ->
    Linc = lists:sort(L),
    Ldec = lists:reverse(Linc),
    s2(Linc,Ldec,S,[]).

s2(Linc,Ldec,_S,R) when Linc == [] ; Ldec == [] ; hd(Linc) > hd(Ldec) -> R;
s2([H,H|Linc],[H,H|Ldec],S,R) when S == 2*H -> s2(Linc,Ldec,S,[{H,H}|R]);
s2([H1|Linc],[H2|Ldec],S,R) when S == H1+H2, H1/=H2 -> s2(Linc,Ldec,S,[{H1,H2}|R]);
s2([H|Linc],Ldec,S,R) when H + hd(Ldec) < S -> s2(Linc,Ldec,S,R);
s2(Linc,[_H|Ldec],S,R) -> s2(Linc,Ldec,S,R).


%% Test and performance

compare(S1,S2) ->
    S = normalize(S1),
    S = normalize(S2).


normalize(S) -> lists:sort([{min(X,Y),max(X,Y)} || {X,Y} <- S]).

shuffle(P) when is_list(P) ->
    Max = length(P)*10000,
    {_,R}= lists:unzip(lists:keysort(1,[{random:uniform(Max),X} || X <- P])),
    R.

test1(S) -> % every term is part of a solution pair
    random:seed(erlang:now()),
    L = shuffle(lists:seq(1,S)),
    test(L,S+1).

test2(S) -> % no solution
    random:seed(erlang:now()),
    L = shuffle(lists:seq(1,S)),
    test(L,2*S).

test3(S) -> % random
    random:seed(erlang:now()),
    L = [random:uniform(2*S) || _ <- lists:seq(1,S)],
    test(L,S).

test(L,S) -> 
    {T1,S1} = timer:tc(sum,s1,[L,S]),
    {T2,S2} = timer:tc(sum,s2,[L,S]),
    compare(S1,S2),
    {T1,T2,S1}.

这是家庭作业吗?你试过什么?对于重叠对有什么限制吗?这是家庭作业吗?您尝试过什么?重叠对有任何限制吗?不幸的是,这种方法无法满足要求,因为一个项目被消耗,不应该重新评估。@SteveVinoski您当然是wright。我应该更加小心地阅读问题:/不幸的是,这种方法不符合要求,因为一件物品被消耗了,不应该重新评估。@SteveVinoski当然你是wright。我应该更加小心地阅读问题:/这基本上就是我采用的方法。我认为如果不先对输入进行排序,就没有什么可以改进的。我使用排序添加了一个更快的解决方案,在我看来它更复杂,而且我无法在第一时间正确地编写它。我不知道在面试中我更喜欢哪一种?这基本上就是我找到的方法。我认为如果不先对输入进行排序,就没有什么可以改进的。我使用排序添加了一个更快的解决方案,在我看来它更复杂,而且我无法在第一时间正确地编写它。我不知道在面试中我更喜欢哪一个?