将列表拆分为 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 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; 请更新您的问题并在此处添加代码。如果您使用四个空格缩进,则可以使用代码块,这样您的代码就更具可读性。 我编辑了我的问题。 当无法构造两个相等值的列表时,需要什么返回值? 【参考方案1】:

编辑:

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

这让我非常兴奋,所以这里有一个包含 [P, (List - P) || P &lt;- 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 -&gt; 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 个相等和的列表的主要内容,如果未能解决你的问题,请参考以下文章

将数字列表划分为2个相等总和列表的算法

将数字列表分成大致相等的总数

如何将一个巨大的列表分成相等的块并将条件应用于这些块?

如何将数组列表分成相等的部分?

如何将大文本文件拆分为行数相等的小文件?

Java递归回溯问题