在Erlang中查找子集,请帮我优化解决方案

时间:2013-07-19 18:19:52

标签: math erlang subset

完全披露。这是一个面试/预筛选问题,我在采访中没有解决。为了自己的利益,我决定在Erlang中实现它。

这是问题陈述:

  

您必须找到数组的子集数,其中最大数字是剩余数字的总和。例如,输入:   1,2,3,4,6

     

子集将是

     

1 + 2 = 3

     

1 + 3 = 4

     

2 + 4 = 6

     

1 + 2 + 3 = 6

这是我的解决方案:

% credit: http://stackoverflow.com/questions/1459152/erlang-listsindex-of-function
index_of(Item, List) -> index_of(Item, List, 1).
index_of(_, [], _)  -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).

% find sums
findSums(L) ->
    Permutations=generateAllCombos(L),
    lists:filter(fun(LL) -> case index_of(lists:sum(LL), L) of
                    not_found -> false;
                    _ -> true
                end
        end, Permutations).


% generate all combinations of size 2..legnth(L)-1
generateAllCombos(L) ->
    NewL=L--[lists:last(L)],
    Sizes=lists:seq(2,length(NewL)),
    lists:flatmap(fun(X) -> simplePermute(NewL,X) end, Sizes).

% generate a list of permutations of size R from list L
simplePermute(_,R) when R == 0 ->
    [[]];

simplePermute(L,R) ->
    [[X|T] || X <- L, T<-simplePermute(lists:nthtail(index_of(X,L),L),R-1)].

以下是一个示例运行:

实施例

18> maxsubsetsum_app:findSums([1,2,3,4,6]).
[[1,2],[1,3],[2,4],[1,2,3]]

问题

  1. 亲爱的Erlangers(Erlangists?)这对你来说看起来像是规范的Erlang吗?
  2. 有没有更好的方式说出我做了什么?
  3. 是否有一个清洁剂超过所有解决方案(这是非常强力)。
  4. 谢谢!

2 个答案:

答案 0 :(得分:2)

这是一个更优雅的版本。

在这个版本中,我假设只有正数,希望加快速度。此外,我有点累,所以它可能有一些小错字,但大多是正确的:)

get_tails([]) -> [];
get_tails([_]) -> [];
get_tails([X:XS]) -> [[X:XS],get_tails(XS)].

get_sums([]) -> [];
get_sums([_]) -> [];
get_sums([X:XS]) -> [get_sums_worker(X,XS):get_sums(XS)]

get_sums_worker(S,_) when S < 0 -> [];
get_sums_worker(S,_) when S == 0 -> [[]];
get_sums_worker(S,[X:XS]) when S > 0 ->
    get_sums_worker(S, XS) ++ [[X:L] || L <- get_sums_worker(S - X, XS)].

sums(A0) ->
    A = lists:reverse(lists:sort(A0)),
    B = get_tails(A),
    lists:flatmap(fun get_sums/1, B).    

我不确定这可以加快多少,因为我怀疑背包问题会减少到这个问题。

答案 1 :(得分:2)

这似乎是你的算法:

  1. 生成所有组合(2 ^ n)
  2. 总结每组组合(n)
  3. 在列表中搜索每个总和(n)
  4. 看起来像是n*2^n。我认为这与计算方面的速度一样快,因为你必须尝试列表中每个数字的所有组合的顺序。也许有人可以纠正我。

    但是,您的空间效率似乎是2^n,因为它存储了所有组合,这是不必要的。

    这就是我提出的,只会累积结果:

    1. 对于每个项目,搜索列表的其余部分以查找与其相加的组合。
    2. 要查找组合,请从目标号码中减去列表的第一个号码,然后在列表的其余部分中搜索可累计差异的组合。
    3. -module(subsets).
      
      -export([find_subsets/1]).
      
      
      find_subsets(NumList) ->
        ReverseSorted = lists:reverse(lists:sort(NumList)),
        find_each_subset(ReverseSorted, []).
      
      find_each_subset([], Subsets) ->
        Subsets;
      find_each_subset([First | ReverseSorted], Subsets) ->
        [ { First, recurse_find_subsets(First, ReverseSorted, [])} | find_each_subset(ReverseSorted, Subsets)].
      
      recurse_find_subsets(_Target, [], Sets) ->
        Sets;
      recurse_find_subsets(Target, [Target | _Numbers], []) ->
        [[Target]];
      recurse_find_subsets(Target, [First | Numbers], Sets) when Target - First > 0 ->
        Subsets = recurse_find_subsets(Target - First, Numbers, []),
        NewSets = lists:map(fun(Subset) -> [ First | Subset] end, Subsets),
        recurse_find_subsets(Target, Numbers, lists:append(NewSets, Sets));
      recurse_find_subsets(Target, [_First | Numbers], Sets) ->
        recurse_find_subsets(Target, Numbers, Sets).
      

      输出:

      5> subsets:find_subsets([6,4,3,2,1]).
      [{6,[[3,2,1],[4,2]]},{4,[[3,1]]},{3,[[2,1]]},{2,[]},{1,[]}]