0

I want to know how to split a given list into two lists such that both lists have the same sum. I want to do that by using concurrency. I am doing this in erlang.

So, I'm doing something like this: Read the list, if its sum is even, then proceed else fail. Take the first element of the list and check if it is greater than half of the sum, if not, then I add this element to a new list. Next, I take the second element of the list, check the sum of this element and that of the new list and do the same operation. And so on.. Such that when the sum in the new list is equal to half of the sum of the first list, it calls another function to send the remaining elements.

-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").

But, in this way I go into an infinite loop sometimes. Could somebody help?

Erica Maine
  • 147
  • 1
  • 8
  • Can you show us the code you have so far? – Luís Cruz Oct 02 '14 at 00:15
  • -module(piles_hw). -compile(export_all). start([]) -> 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() end. nums(l) -> l. copy_to_list_one(L1,[H|T],X)-> Y =lists:sum(L1)+H, if 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; – Erica Maine Oct 02 '14 at 00:22
  • Please update your question and add the code there. You can use code blocks if you use four space indentation so you code can be more readable. – Luís Cruz Oct 02 '14 at 00:24
  • What is the required return value when it is not possible to construct two lists of equal value? – zxq9 Oct 02 '14 at 00:31
  • Just an output fail. – Erica Maine Oct 02 '14 at 00:34

2 Answers2

0

Edit:

Pascal was entirely correct: there is no algorithm (at least not that I could come up with) that can solve certain sets by running down the list one item at a time. (In particular when half the sum of the list equals X * N where X is present in the list N times.) I initially put a flawed algorithm here.

That got me excited in the nerdiest of ways, so here is an exhaustive algorithm involving the pairs of [{P, (List - P)} || P <- powerset(List)].

There are some lists:usort/1 shenanigans in there that I didn't clean up to uniquify the list prior to the final comparison (otherwise you get duplicate similar pairs, which is ugly). Anyway, ugly, but now correct:

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.

This is still not a concurrent solution, but the path to making it concurrent is a lot more obvious now.

Thanks for pointing that out, Pascal. That was sort of fun.

zxq9
  • 13,020
  • 1
  • 43
  • 60
  • Hi,Thank you!! I had one question about concurrency though. How can I pass some particular element from one process to another? Like when from one process, I send a message, can I include some element of that process as well? – Erica Maine Oct 02 '14 at 01:36
  • To add an element to, say, the process operating under Lpid above, you would do `Lpid ! {self(), {add, H}}`. It picks up from there. I recommend you read the [introductory bit from LYSE on concurrency](http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency) before going any further. – zxq9 Oct 02 '14 at 01:43
  • From the example that you have given, in the list_loop(), where does the sending of the message sum and add,Number occur? Where do I need to write that? – Erica Maine Oct 02 '14 at 02:49
  • The "sum" message would be sent in place of `lists:sum()`, and the "{add, Number}" message would be sent in place of doing `[H|R]` or `[H|L]` -- you would instead send the message to "Lpid" or "Rpid" above. All that changes here is that instead of using the L and R lists directly a process has been created which represents/manages each list, and to communicate with any process you send it a message. I *really* recommend you read the part of LYSE I linked in my last comment before messing with this any further. This site is about learning, not getting people to solve homework for free. – zxq9 Oct 02 '14 at 09:47
  • Will not work for [-1, 1]. Solution must be [-1,1]/[] . – Odobenus Rosmarus Oct 02 '14 at 15:40
  • @OdobenusRosmarus Oh, mustn’t it? Was wondering if anyone was going to say that... Fine, I'll add an inverse clause -- but the real point of this answer is to get the OP to dig a bit deeper instead of writing free code for them on SO. – zxq9 Oct 02 '14 at 18:58
  • I don't think it exists an algorithm that can go straight to a solution by looking the elements 1 by 1. this one fails with the input list [7,7,4,4,4,2] – Pascal Oct 03 '14 at 04:47
  • @Pascal You're right! Or at least I think you are (anyway, my algorithm was wrong). I put up a different version using an exhaustive comparison over P(List). Its still not concurrent in execution, but a way to make it so without being concurrent for concurrency's sake is now more obvious. – zxq9 Oct 03 '14 at 06:39
0

I have this solution that is not concurrent:

-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.

To turn it concurrent, you could replace the line 0 -> split(tl(L),[hd(L)],[],T2 div 2,hd(L),0) by a call to a function which spawn_link several processes (as much as there are core available) which start the split/6 function with different initial conditions. The split/6 must have a 7th parameter: the Pid of the main process where it will send back its answer. The main process wait for answers and stop

  • if a solution is found
  • if all processes fail to find one
  • if the time out occurs

I have edited the code following @Odobenus remark (but it still fail on [] -> {ok,[],[]} :o), and I also made a concurrent version. The funny thing is that for this kind of problem, and with the input list I use (a lists:seq) there are so many solution that any start sequence I choose can give a solution, so the concurrent version is slower.

Pascal
  • 13,977
  • 2
  • 24
  • 32
  • fails on [0]. Solution should be `[0] / []` – Odobenus Rosmarus Oct 02 '14 at 15:45
  • I was surprised because it is exactly the same pattern I used, so I tried on my side, split:split([3,4,5,6,7,8,9]). gives the result {ok,{[7,6,5,3],[4,8,9]}} each sum are 21 which is correct??? – Pascal Oct 06 '14 at 16:14
  • Following @EricaMaine comment I reviewed the code, and it works only with positive integers :o(. I will try to find time to post a correction. – Pascal Oct 10 '14 at 10:16
  • Hi, I was talking of the first code in the trail, not yours. That fails at only that pattern, I didn't understand why. – Erica Maine Oct 12 '14 at 09:34