由于cnblogs的代码着色系统不支持erlang,所以就直接从博客上贴过来了,如果大家看的不习惯的话,就直接来我的博客上看吧

本文章为本人个人博客相应文章的镜像:

原文地址: http://www.greatony.com/index.php/2010/03/18/a-combination-algorithm-written-in-erlang/

 上一篇博客中,我用Erlang实现了一个优惠计算器,其中的核心一块内容就是组合算法的实现。

在这里组合的概念呢,是指,一些数的全部组合方式。举个例子来说:现在有这些数[1,2,3],那么他们一共有几种组合方式呢?

  1. 组合成1个组
    • [1,2,3]
  2. 组合成2个组
    • [1,2], [3]
    • [1, 3], [2]
    • [2,3], [1]
  3. 组合成3个组
    • [1],[2],[3]

所以一共有6种方式。

所以,我们的问题就是,现在有一个列表List,我们需要找出List中的元素的所有组合,该如何写这个算法呢?

我们从我们习惯的数学的角度出发:

  1. 对于一个空的列表[],它没有任何组合方式(我们规定[[]]不是一个合法的组合)
  2. 对于包含一个元素的列表[Head],它只有一种组合方式:[[Head]]
  3. 对于包含两个及以上元素的列表,我们记为[Head|Rest],它的组合方式的数量取决于Rest中所有组合的数量和组数

这里,对于第3点需要再详细的解释一下:
假设Rest为[1,2,3,4,5],它的一种组合为如下形式:
[[1], [2,3], [4,5]]
而Head为6,那么[6,1,2,3,4,5]就具有(不仅具有)如下几种组合方式:

  1. [[1,6],[2,3],[4,5]]
  2. [[1],[2,3,6],[4,5]]
  3. [[1],[2,3],[4,5,6]]
  4. [[1],[2,3],[4,5],[6]]

也就说,如果将Rest分为m组,那么对于每种组合,[Head|List]就有相应的(m+1)种组合。
对于长度为n的list,它应该有几种组合方式,我们可以放下不管(因为我们要的是效果)。
根据上面的推理,我们可以将第3种情况细化成如下的描述:

  • 如果List包含2个及以上的元素,可以表示为[Head|Rest]的形式,那么对于Rest中的每一种组合,我们可以将Head分别放入这个组合中的每一个分组或新的一个分组中,得到Rest在这个排列下,[Head|Rest]的所有组合。

好了,思路整理清楚了,我们就可以开始写代码了:
首先,我们写一个函数(我称之为reduce_comb)来根据Rest的一种组合Comb以及Head来找到对应的[Head|Rest]的所有组合:

01reduce_comb(CombHead) -> reduce_comb_inner([], CombHead);
02 
03% 将元素加为新的一个组
04reduce_comb_inner(Prefix, [], Element) -> [Prefix ++ [[Element]]];
05% 将元素加到Head这个组
06reduce_comb_inner(Prefix, [Head Rest], Element) ->
07    [
08        Prefix ++ [[Element Head]] ++ Rest
09      | reduce_comb_inner(Prefix ++ [Head], RestElement)
10    ].

好了,这个大问题解决了,现在解决小问题,就是把上面写好的那三点逻辑写出来:

01% 对于第一种情况
02combination(_FunctionDefault, []) -> Default;
03% 对于第二种情况
04combination(FunctionDefault, [Head]) -> Function([[Head]], Default);
05% 对于第三种情况(精髓啊)
06combination(FunctionDefault, [Head List]) ->
07    combination(fun(CombinationOldSolution) ->
08        ReducedCombination = reduce_comb(CombinationHead),
09        lists:foldl(Function, oldSolutionReducedCombination)
10    endDefaultList).
posted on 2010-03-18 21:51  TonyHuang  阅读(867)  评论(1编辑  收藏  举报