将列表拆分为 2 个相等和的列表
Posted
技术标签:
【中文标题】将列表拆分为 2 个相等和的列表【英文标题】:Split list into 2 lists of equal sum erlang 【发布时间】:2014-11-26 22:59:35 【问题描述】:我想知道如何将给定列表拆分为两个列表,以使两个列表具有相同的总和。我想通过使用并发来做到这一点。我正在用 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").
但是,这样我有时会陷入无限循环。有人可以帮忙吗?
【问题讨论】:
你能告诉我们你到目前为止的代码吗? -模块(piles_hw)。 -编译(export_all)。开始([])-> 0; start(List) -> nums(length(List)), %def_list(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() 结束。数字(l)-> l。 copy_to_list_one(L1,[H|T],X)-> Y =lists:sum(L1)+H, 如果 Y编辑:
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.
这仍然不是一个并发解决方案,但使其并发的路径现在更加明显。
感谢您指出这一点,帕斯卡。那很有趣。
【讨论】:
您好,谢谢!我有一个关于并发的问题。如何将某个特定元素从一个进程传递到另一个进程?就像我从一个流程发送消息一样,我是否也可以包含该流程的某些元素? 要向上面的 Lpid 下运行的进程添加一个元素,您可以使用Lpid ! self(), add, H
。它从那里开始。我建议您在继续之前阅读introductory bit from LYSE on concurrency。
从您给出的示例中,在 list_loop() 中,消息 sum 和 add,Number 的发送发生在哪里?我需要在哪里写?
发送“sum”消息代替lists:sum()
,发送“add, Number”消息代替[H|R]
或[H|L]
——你而是将消息发送到上面的“Lpid”或“Rpid”。这里所有的变化是,不是直接使用 L 和 R 列表,而是创建了一个代表/管理每个列表的进程,并与任何进程通信,你向它发送消息。我真的建议您阅读我在上一条评论中链接的 LYSE 部分,然后再进一步搞砸。这个网站是关于学习的,而不是让人们免费解决家庭作业。
不适用于 [-1, 1]。解决方案必须是 [-1,1]/[] 。【参考方案2】:
我有这个不是并发的解决方案:
-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),有很多解决方案,我选择的任何开始序列都可以给出解决方案,所以并发版本比较慢。
【讨论】:
在 [0] 上失败。解决方案应该是[0] / []
我很惊讶,因为它和我使用的模式完全一样,所以我尝试了一下,split:split([3,4,5,6,7,8,9])。给出结果 ok,[7,6,5,3],[4,8,9] 每个总和都是 21,这是正确的???
在@EricaMaine 评论之后,我查看了代码,它只适用于正整数:o(。我会找时间发布更正。
嗨,我说的是跟踪中的第一个代码,而不是你的。仅在那种模式下失败了,我不明白为什么。以上是关于将列表拆分为 2 个相等和的列表的主要内容,如果未能解决你的问题,请参考以下文章