Erlang遍历嵌套列表的所有组合,而不存储所有组合

时间:2016-11-06 13:18:35

标签: erlang combinations nested-lists

我遇到了以下问题,我坚持使用。

我有一个这样的方格式嵌套列表(这只是一个例子,我事先并不知道输入的维度 - 它可以是4 * 4或9 * 9或27 * 27甚至更多,唯一可靠的信息是列表中有k ^ 4个成员,其中k = 1,2,3 ......):

[[[2],[1,3],[4],[1,3]],
 [[1,3],[4],[2,3],[1,3]],
 [[3,4],[2,3],[1],[2,3,4]],
 [[1,3,4],[1,2,3],[2,3],[2,3,4]]]

可以生成子列表的笛卡尔积,然后生成列表的笛卡尔积,得到这个:

[[[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,2,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,2,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,2,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,3,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,3,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,1,3,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,2,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,2,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,2,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,3,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,3,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,2,3,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,2,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,2,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,2,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,3,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,3,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[1,3,3,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[3,1,2,2]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[3,1,2,3]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[3,1,2,4]],
 [[2,1,4,1],[1,4,2,1],[3,2,1,2],[3,1,3|...]],
 [[2,1,4,1],[1,4,2,1],[3,2,1|...],[3,1|...]],
 [[2,1,4,1],[1,4,2|...],[3,2|...],[3|...]],
 [[2,1,4|...],[1,4|...],[3|...],[...]],
 [[2,1|...],[1|...],[...]|...],
 [[2|...],[...]|...],
 [[...]|...],
 [...]|...]

所以我可能生成并遍历此矩阵,但由于内存限制,我无法做到这一点。即使在9 * 9矩阵中,问题也有太多组合。

所以我想顺序生成这些组合以进行遍历,而不存储所有组合。 红利问题:生成的子列表不能包含重复值。但即使没有红利问题,如果您提供解决方案我很高兴。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

答案有点迟,但我遇到了一些麻烦来找到解决方案。

我建议你使用一些代码,我对你可以用中间结果做了什么假设,这个例子对所有最终结果和中间结果进行排序并将它们累积在#{Key = sorted_intermediate_list, Value = occurrence}形式的地图中,它可以改变通过修改函数accumulate / 3。

-module (combi).

-export ([traverse/1,traverse_with_trace/1,get_all_solutions/1]).
% test exports
-export ([t1/0,t1/3,l/0,l2/0,l_ori/0]). 

t1() ->
    F = fun(X,Acc,_) ->
        io:format("---->  ~p~n",[X]),
        [X|Acc]
    end,
    lists:reverse(cross_prod_1(l1(),[],F,[],1)).

t1(L,F,Acc) ->
    cross_prod_1(L,[],F,Acc,1).

traverse(L) -> traverse(L,{no,0}).

traverse_with_trace(L) -> traverse(L,{yes,0}).

traverse([H|T],I) ->
    print(I,"enter traverse      ",[[H|T]]),
    F = fun(P1,P2,N) -> accumulate(P1,P2,N) end,
    print(I,"leave traverse      ",cross_prod_1(H,[],F,{T,[],#{}},next(I))).

cross_prod_1([],L_acc,F,Acc,I) ->
    print(I,"enter cross_prod_1 1",[[],L_acc,Acc]),
    Res = lists:sort(L_acc),
    print(I,"leave cross_prod_1 1",F(Res,Acc,next(I)));
cross_prod_1([[X1]|T],L_acc,F,Acc,I) ->
    print(I,"enter cross_prod_1 2",[[[X1]|T],L_acc,Acc]),
    print(I,"leave cross_prod_1 2",cross_prod_1(T,[X1|L_acc],F,Acc,next(I)));
cross_prod_1([[X1|T1]|T],L_acc,F,Acc = {E1,E2,_},I) ->
    print(I,"enter cross_prod_1 3",[[[X1|T1]|T],L_acc,Acc]),
    Res = cross_prod_1(T,[X1|L_acc],F,Acc,next(I)),
    print(I,"leave cross_prod_1 3",cross_prod_1([T1|T],L_acc,F,{E1,E2,Res},next(I))).

accumulate(X,{[],SoFar,BigAcc},I) ->
    print(I,"enter accumulate   1",[X,{[],SoFar,BigAcc}]),
    print(I,"leave accumulate   1",insert(lists:sort([X|SoFar]),BigAcc));
accumulate(X,{[H1|T1],SoFar,BigAcc},I) ->
    print(I,"enter accumulate   2",[X,{[H1|T1],SoFar,BigAcc}]),
    F = fun(P1,P2,N) -> accumulate(P1,P2,N) end,
    print(I,"leave accumulate   2",cross_prod_1(H1,[],F,{T1,[X|SoFar],BigAcc},next(I)));
accumulate(X,Y,N) -> % should not occur, for debug purpose
    io:format("### ~p, ~p, ~p ###~n",[X,Y,N]),
    Y.

insert(Elem,Map) ->
    maps:update_with(Elem,fun (V) -> V+1 end ,1,Map).


next({Print,I}) -> {Print,I+1}.

% debug function, it returns Par and, if needed prints indented messages
print({no,_},_,Par) -> Par;
print({yes,N},From,Par) ->
    H = lists:duplicate(2*N,$.),
    io:format("~s~s : ~p~n",[H,From,Par]),
    Par.

% list generator

l() -> 
    [[[a1],[a2,a3],[a4],[a5,a6]],
    [[b1,b2],[b3],[b4,b5],[b6,b7]],
    [[c1,c2],[c3,c4],[c5],[c6,c7,c8]],
    [[d1,d2,d3],[d4,d5,d6],[d7,d8],[d9,d10,d11]]].

l1() -> [[a1],[a2,a3],[a4],[a5,a6]].

l2() -> [[[1,2],[3,4]],[[5,6],[7,8]]].

l_ori() -> 
    [[[2],[1,3],[4],[1,3]],
    [[1,3],[4],[2,3],[1,3]],
    [[3,4],[2,3],[1],[2,3,4]],
    [[1,3,4],[1,2,3],[2,3],[2,3,4]]].

% reference functions

get_all_solutions(PossibleSolution) -> 
    Solutions = lists:map(fun(Solution) -> crossproduct(Solution) end, PossibleSolution),
    crossproduct(Solutions).

crossproduct([H|T]) -> 
    [[A|B] || A <- H, B <- crossproduct(T)];
crossproduct([]) -> 
    [[]].

这里有一些结果:

1> c(combi).                                                        
{ok,combi}
2> length(combi:get_all_solutions(combi:l_ori())). % length of result using your solution and the list given as example   
20736
3> maps:size(combi:traverse(combi:l_ori())). % size of the resulting map with my code and your list     
2189
4> maps:fold(fun(_,V,A) -> V+A end,0,combi:traverse(combi:l_ori())). % check I have got all the elements
20736
5> combi:traverse_with_trace([[[1,2],[3]],[[4],[5]]]). % show the program progress with a tiny input list
enter traverse       : [[[[1,2],[3]],[[4],[5]]]]
..enter cross_prod_1 3 : [[[1,2],[3]],[],{[[[4],[5]]],[],#{}}]
....enter cross_prod_1 2 : [[[3]],[1],{[[[4],[5]]],[],#{}}]
......enter cross_prod_1 1 : [[],[3,1],{[[[4],[5]]],[],#{}}]
........enter accumulate   2 : [[1,3],{[[[4],[5]]],[],#{}}]
..........enter cross_prod_1 2 : [[[4],[5]],[],{[],[[1,3]],#{}}]
............enter cross_prod_1 2 : [[[5]],[4],{[],[[1,3]],#{}}]
..............enter cross_prod_1 1 : [[],[5,4],{[],[[1,3]],#{}}]
................enter accumulate   1 : [[4,5],{[],[[1,3]],#{}}]
................leave accumulate   1 : #{[[1,3],[4,5]] => 1}
..............leave cross_prod_1 1 : #{[[1,3],[4,5]] => 1}
............leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1}
..........leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1}
........leave accumulate   2 : #{[[1,3],[4,5]] => 1}
......leave cross_prod_1 1 : #{[[1,3],[4,5]] => 1}
....leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1}
....enter cross_prod_1 2 : [[[2],[3]],[],{[[[4],[5]]],[],#{[[1,3],[4,5]] => 1}}]
......enter cross_prod_1 2 : [[[3]],[2],{[[[4],[5]]],[],#{[[1,3],[4,5]] => 1}}]
........enter cross_prod_1 1 : [[],[3,2],{[[[4],[5]]],[],#{[[1,3],[4,5]] => 1}}]
..........enter accumulate   2 : [[2,3],{[[[4],[5]]],[],#{[[1,3],[4,5]] => 1}}]
............enter cross_prod_1 2 : [[[4],[5]],
                                    [],
                                    {[],[[2,3]],#{[[1,3],[4,5]] => 1}}]
..............enter cross_prod_1 2 : [[[5]],
                                      [4],
                                      {[],[[2,3]],#{[[1,3],[4,5]] => 1}}]
................enter cross_prod_1 1 : [[],
                                        [5,4],
                                        {[],[[2,3]],#{[[1,3],[4,5]] => 1}}]
..................enter accumulate   1 : [[4,5],
                                          {[],[[2,3]],#{[[1,3],[4,5]] => 1}}]
..................leave accumulate   1 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
................leave cross_prod_1 1 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
..............leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
............leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
..........leave accumulate   2 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
........leave cross_prod_1 1 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
......leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
....leave cross_prod_1 2 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
..leave cross_prod_1 3 : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
leave traverse       : #{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
#{[[1,3],[4,5]] => 1,[[2,3],[4,5]] => 1}
6>