0

我想知道如何将给定列表拆分为两个列表,以使两个列表具有相同的总和。我想通过使用并发来做到这一点。我正在用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").

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

4

2 回答 2

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.

要将其变为并发,您可以0 -> split(tl(L),[hd(L)],[],T2 div 2,hd(L),0)通过调用一个函数来替换该行,该函数生成链接多个进程(只要有可用的核心),这些进程以不同的初始条件启动 split/6 函数。split/6 必须有第 7 个参数:主进程的 Pid,它将发送回它的答案。主进程等待答案并停止

  • 如果找到解决方案
  • 如果所有进程都找不到一个
  • 如果发生超时

我在@Odobenus 评论之后编辑了代码(但它仍然在 [] -> {ok,[],[]} :o 上失败),并且我还制作了一个并发版本。有趣的是,对于这类问题,加上我使用的输入列表(a lists:seq)有很多解决方案,我选择的任何开始序列都可以给出解决方案,所以并发版本比较慢。

于 2014-10-02T04:23:55.360 回答
0

编辑:

帕斯卡是完全正确的:没有算法(至少不是我能想出的)可以通过一次运行一个项目来解决某些集合。(特别是当列表总和的一半等于 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.

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

感谢您指出这一点,帕斯卡。那是一种乐趣。

于 2014-10-02T01:04:05.650 回答