将列表拆分为2个等额和erlang的列表

时间:2014-10-02 00:07:33

标签: erlang erl

我想知道如何将给定列表拆分为两个列表,以便两个列表具有相同的总和。我想通过使用并发来做到这一点。我在erlang中这样做。

所以,我做这样的事情: 读取列表,如果其总和是偶数,则继续其他失败。获取列表的第一个元素并检查它是否大于总和的一半,如果没有,那么我将此元素添加到新列表中。接下来,我获取列表的第二个元素,检查此元素的总和以及新列表的总和,并执行相同的操作。等等。这样当新列表中的总和等于第一个列表总和的一半时,它会调用另一个函数来发送剩余的元素。

-module(piles_hw).
-compile(export_all).

start([]) -> 0;

start(List) -> 
Total = lists:foldl(fun(X, Sum)-> X+Sum end,0,List), 

if (Total rem 2) == 0 ->
    Total/2, 
    copy_to_list_one([],List,start(List));  
   true ->
    func_fail()
end.

copy_to_list_one(L1,[H|T],X)->
    Y =lists:sum(L1)+H,
    if Y<X ->
        copy_to_list_one(lists:append(L1,[H]),lists:delete(H,[H|T]),X);
       Y==X ->
        take(lists:append(L1,[H]));
      Y>X ->
        copy_to_list_one(L1,lists:delete(H,[H|T]),X)
end;
copy_to_list_one(L1,[],X)->
    copy_func_two([1,2,3,4,19,20,28,14,11],X).
copy_func_two([H|T],X)->
    copy_to_list_one([],lists:append(T,[H]),X).

    take(L3)->    
    io:format("~w",[L3]).

func_fail() ->
    io:format("~n fail ~n").

但是,这样我有时会陷入无限循环。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:0)

编辑:

Pascal是完全正确的:没有算法(至少不是我能想到的)可以通过一次运行列表一个项来解决某些集合。 (特别是当列表总和的一半等于X * N,其中X在列表中出现N次。)我最初在这里提出了一个有缺陷的算法。

这让我兴奋不已,所以这里有一个涉及[{P, (List - P)} || P <- powerset(List)]对的详尽算法。

在那里有一些lists:usort/1恶作剧我没有清理以在最终比较之前统一列表(否则你得到重复的类似对,这是丑陋的)。无论如何,丑陋,但现在正确:

comblit(List) ->
    Power = powerset(List),
    Lists = lists:usort([lists:sort([Z, lists:subtract(List, Z)]) || Z <- Power]),
    Pairs = lists:map(fun([H|[B|[]]]) -> {H, B} end, Lists),
    [{Z, X} || {Z, X} <- Pairs, lists:sum(Z) == lists:sum(X)].

powerset([H|T]) ->
    Part = powerset(T),
    powerset(Part, H, Part);
powerset([]) -> [[]].

powerset(A, Part, [H|T]) ->
    powerset([[Part|H]|A], Part, T);
powerset(A, _, []) -> A.

这仍然不是一个并发解决方案,但现在让它并发的道路更加明显。

感谢你指出这一点,Pascal。这很有趣。

答案 1 :(得分:0)

我有这个不兼并的解决方案:

-module(split).

-export([split/1,t_ok/0,t_too_long/0,t_fail/0,t_crash/0]).
%% [EDIT]
%% Don't use this code, it fails with negative integers!


% Exported

%% take a list and split it in 2 list which sum are equals
split(L=[_|_]) ->
    T2 = lists:sum(L),
    {ok, TRef} = timer:send_after(20000,too_long),
    R = case T2 rem 2 of
        1 -> {error,fail};
        0 -> split(tl(L),[hd(L)],[],T2 div 2,hd(L),0)
    end,
    timer:cancel(TRef),
    R.

% test

t_ok() -> split([1,2,3,4,5,6,7]).

t_too_long() -> split(lists:seq(1,3+4*100000)).

t_fail() -> split([2,4,6,10000,8,6]).

t_crash() -> split([]).

% private

split([H|Q],A,B,T,Asf,_Bsf) when H + Asf == T -> {ok,{[H|A],B ++ Q}};                           
split([H|Q],A,B,T,_Asf,Bsf) when H + Bsf == T -> {ok,{A ++ Q,[H|B]}};                           
split([H|Q],A,B,T,Asf,Bsf) when H + Asf > T, H + Bsf < T -> c_split(Q,A,[H|B],T,Asf,Bsf+H);     
split([H|Q],A,B,T,Asf,Bsf) when H + Asf < T, H + Bsf > T -> c_split(Q,[H|A],B,T,Asf+H,Bsf);     
split([H|Q],A,B,T,Asf,Bsf) when H + Asf < T, H + Bsf < T -> 
    case c_split(Q,A,[H|B],T,Asf,Bsf+H) of
        {error,fail} -> c_split(Q,[H|A],B,T,Asf+H,Bsf);                                                   
        R -> R                                                                              
    end;  
split([],A,B,_T,_T,_T)-> {ok,{A,B}};                                                                                 
split(_,_,_,_,_,_) -> {error,fail}.

c_split(L,A,B,T,Asf,Bsf) ->
    receive
        too_long -> {error,too_long}
    after 0 ->
        split(L,A,B,T,Asf,Bsf)
    end.

要将它并发,你可以通过调用spawn_link几个进程的函数来替换行0 -> split(tl(L),[hd(L)],[],T2 div 2,hd(L),0)(尽管有核心可用),它们以不同的初始条件启动split / 6函数。 split / 6必须有第7个参数:主进程的Pid,它将发回它的答案。主要过程等待答案并停止

  • 如果找到解决方案
  • 如果所有进程无法找到一个
  • 如果超时

我已经编辑了@Odobenus备注之后的代码(但它仍然在[] - &gt; {ok,[],[]}:o)上失败,我也制作了并发版本。有趣的是,对于这类问题,我使用的输入列表(列表:seq)有很多解决方案,我选择的任何启动序列都可以提供解决方案,因此并发版本较慢。