我想知道如何将给定列表拆分为两个列表,以便两个列表具有相同的总和。我想通过使用并发来做到这一点。我在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").
但是,这样我有时会陷入无限循环。有人可以帮忙吗?
答案 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)有很多解决方案,我选择的任何启动序列都可以提供解决方案,因此并发版本较慢。