数学问题(1) — 组合问题的程序实现

        数学中有个经典的组合问题,即一个m元素组成结合C取其中n个元素 (0<n<=m ,n,m为整数),与多少种组合。在读高中时这个问题是数学的一大重点难点,笔者印象深刻。在大学研究算法时也碰到这种问题,当时试着按高中时的解决方案如:挡板法、归类法去遍历所有的组合情况时却发现没那么容易,用函数递归的编程方法,很容易就死循环了。当时学线性代数的矩阵时有种算法叫 “0|1置换法”跟排列组合的概念相符合,就尝试用这种方法来遍历组合问题。后来参加工作了,在某互联网婚恋网站负责一个情缘匹配的需求,里面会根据男女的身材、相貌、工作、收入、家庭、年龄、性格等多维因素做一个匹配,因为数据量大不能实时计算、要事先计算好结果,所以就要先计算出有多少种组合,每种组合人群归类,然后给红娘去定向推广。因为涉及公司业务,此处不举这个例子。今天整理文档,发现了这个算法,直接贴出算法和代码实现,供有兴趣者参考:

求一个组合C(m,n)

算法如下

 1、开一个数组a[m],其下标表示1到m个数
 2、数组元素的值为1表示其下标代表的元素被选中,为0则没选中。
 3、首先初始化,将数组a[0]~a[n-1]个元素置1,a[n]~a[m-1]置0,由此得到第一个组合
 4、从左到右扫描,找到a[i-1]=1,a[i]=0的组合 ,找到第一个这种组合后将其值对换,同时将其左边的所有“1”全部移动到数组的最左端
 5、当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得 到了最后一个组合。

比如组合 [A,B,C,D,E]中5取3的组合情况,按算法步骤如下:
 1 1 1 0 0
 1 1 0 1 0
 1 0 1 1 0
 0 1 1 1 0
 1 1 0 0 1
 1 0 1 0 1
 0 1 1 0 1
 1 0 0 1 1
 0 1 0 1 1
 0 0 1 1 1

根据算法第2点 1位选中 0未选中,得到的结果即是:

 A B C
 A B D
 A C D
 B C D
 A B E
 A C E
 B C E
 A D E
 B D E
 C D E

代码实现比较粗陋,可以优化:

import java.util.ArrayList;
import java.util.List;

/**
 * 组合算法
 *
 * @author zkd
 * @Date 2014/5/9
 */
public class Combination {

    //返回n中选m的组合序列 1<=m<=n
    public List<int[]> generateCombination(int n, int m) {
        List<int[]> list = new ArrayList<>();
        int[] tempNum = new int[n];
        boolean flag; // 判断算法的结束
        // 初始化数组,同时得到第一组组合系列
        for (int i = 0; i < n; i++) {
            if (i < m) {
                tempNum[i] = 1;
            } else {
                tempNum[i] = 0;
            }
        }
        int[] b = tempNum.clone();
        list.add(b);
        if (n == m) {
            return list;
        }
        do {
            int pose = 0; // 记录改变的位置
            int sum = 0; // 记录改变位置 左侧 1 的个数
            // 然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为“01”
            for (int i = 0; i < (n - 1); i++) {
                if (tempNum[i] == 1 && tempNum[i + 1] == 0) {
                    tempNum[i] = 0;
                    tempNum[i + 1] = 1;
                    pose = i;
                    break;
                }
            }
            // 同时将其左边的所有“1”全部移动到数组的最左端。
            for (int i = 0; i < pose; i++) {
                if (tempNum[i] == 1)
                    sum++;
            }
            for (int i = 0; i < pose; i++) {
                if (i < sum)
                    tempNum[i] = 1;
                else
                    tempNum[i] = 0;
            }
            // 判断是否为最后一个组合:当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。
            flag = false;
            for (int i = n - m; i < n; i++) {
                if (tempNum[i] == 0)
                    flag = true;
            }
            int[] a = tempNum.clone();
            list.add(a);
        } while (flag);
        return list;
    }

    public void displayCombinationObject(String[] object, int n, int m) {
        System.out.println("------" + n + "中选" + m + "的组合的种类总数有:"
                + countOfCombination(n, m) + "--------");
        List<int[]> list = generateCombination(n, m);
        String s = "";
        int j;
        int[] a;
        for (int i = 0; i < list.size(); i++) {
            a = list.get(i);
            for (j = 0; j < a.length; j++) {
                if (1 == a[j]) {
                    s += (" " + object[j]);
                }
            }
            System.out.println(s);
            s = "";
        }
    }

    //返回n中选m的组合的种类总数
    public int countOfCombination(int n, int m) {
        int sum = 1;
        int div = 1;
        for (int i = n; i >= (n - m + 1); i--) {
            sum *= i;
        }
        for (int j = 1; j <= m; j++) {
            div *= j;
        }
        return sum / div;
    }

    public static void main(String[] args) {
        Combination pac = new Combination();
        String[] object5 = { "A", "B", "C", "D", "E" };
        pac.displayCombinationObject(object5, 5, 3);
    }

}


  

猜你喜欢

转载自blog.csdn.net/zhangniuagezhang/article/details/81540743