簡體   English   中英

將列表拆分為2個相等和erlang的列表

[英]Split list into 2 lists of equal sum erlang

我想知道如何將給定列表分為兩個列表,以使兩個列表的總和相同。 我想通過使用並發做到這一點。 我用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").

但是,這樣我有時會陷入無限循環。 有人可以幫忙嗎?

編輯:

Pascal是完全正確的:沒有一種算法(至少不是我能想到的)可以一次只列出一個項目來解決某些集合。 (特別是當列表的總和的一半等於X * N時,其中X在列表中存在N次。)我最初在這里放置了一個有缺陷的算法。

這讓我以最瘋狂的方式感到興奮,所以這里是涉及[{P, (List - P)} || P <- powerset(List)]對的詳盡算法。 [{P, (List - P)} || P <- powerset(List)]

這里有一些lists:usort/1 shenanigans,在最終比較之前,我沒有清理過以統一列表(否則您會得到重復的相似對,這很丑陋)。 無論如何,很難看,但現在正確了:

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。 那很有趣。

我有此解決方案不是並發的:

-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注釋之后的代碼(但是在[]-> {ok,[],[]}:o上仍然失敗),並且我還創建了一個並發版本。 有趣的是,對於這種類型的問題,我使用的輸入列表(一個list:seq)有很多解決方案,因此我選擇的任何啟動順序都可以提供解決方案,因此並發版本的速度較慢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM