Loading

组合数计算-java

排列组合是计算应用经常使用的算法,通常使用递归的方式计算,但是由于n!的过于大,暴力计算很不明智。一般使用以下两种方式计算。

一,递归的思想:假设m中取n个数计算排列组合数,表示为comb(m,n)。那么comb(m,n)= comb(m-1,n-1)+comb(m-1,n)

解释思想,从m个球中取出n个球可以分成两种情况相加,从m个球中取出一个球,如果它属于n,还需要从m-1中取出n-1个球;如果它不属于n,则需要从m-1中取出n个球

根据这种思想可以通过递归的思想计算组合数:

private static long comb(int m,int n){if(n==0)
            return 1;
        if (n==1) 
            return m;
        if(n>m/2)
            return comb(m,m-n);
        if(n>1)
            return comb(m-1,n-1)+comb(m-1,n);  
    
return -1; //通过编译需要,数字无实际意义
}

适用递归计算,当数字较大时,递归深度过深,会相对耗时,甚至堆栈溢出。如果对性能有要求,可以建立键-值对,存储计算结果,防止,反复计算。

static Map<String,Long> map= new HashMap<String, Long>();
private static long comb(int m,int n){
        String key= m+","+n;
        if(n==0)
            return 1;
        if (n==1) 
            return m;
        if(n>m/2)
            return comb(m,m-n);
        if(n>1){
            if(!map.containsKey(key))
                map.put(key, comb(m-1,n-1)+comb(m-1,n));
            return map.get(key);
        }
        return -1;
    }

二,对数的计算思想:跟据定义,comb(m,n)=m!/(m-n)!n!

两边取对数,log(comb(m,n))=log(m!/n!)-log((m-n)!)

计算之后,再通过exp计算最终结果。优点,不会出现数组越界,缺点:计算非常大的数字时,由于精度误差,结果转化为整数时可能有偏差

 private static double comb_log(int m,int n){
        int  i;
        if(n>m-n) n=m-n;
        double s1=0.0;
        double s2=0.0;
        for (int j = m-n+1; j <=m; j++) {
            s1+=Math.log(j);
        }
        for (int j = 1; j <=n; j++) {
         s2+=Math.log(j);
        }
        return Math.exp(s1-s2);
    }

 

posted @ 2018-08-18 19:04  CodeMonkey404  阅读(7496)  评论(0编辑  收藏  举报