组合数

排列组合是高中课程的重要内容,坑爹的计算各种花样百出的排列组合数。最近又遇到了组合,突然想起来刚毕业时,研究过这种方法,甚为巧妙,这里记录下。
组合有种应用是在把一堆物品分成两类,应该怎样分,有多少种分法,本文就是针对这种情况。

1. 有多少种分法

我们假设有k个物品,分别为k(0), …,k(n-1),则其分为两类可分成以下的情况

  • 分成1/(k-1)两类,则为 C1k

  • 分成2/(k-2)两类,则为 C2k

  • 分成(k-1)/1两类,则为 Ck1k
    则所有的分类为

C1k+C2k+...+Ck2k+Ck1k=2k2

明显,里面第一种情况和最后一种情况是重复的分法,也就是上下对称的部分是重复的,因此实际的分法是 2k11 种。

2. 怎么分

我们在上面计算了有多少种分法,但是没有具体列举应该怎么分,例如当k=4时,分别用0, 1, 2, 3表示,其可能的分法种类是

2k11=7

其分法
0|1,2,31|0,1,22|0,1,33|0,1,20,1|2,30,2|1,30,3|1,2

如果要输出其所有的分法,可以用递归的方法

#include <iostream>
#include <vector>
using namespace std;
vector<int> result;
int k = 4;
void combi(int begin)
{
    if(begin >= k){ 
        if(result.size() == k || result.size() == 0){ 
            return;
        }   
        //print
        for(int i=0; i<result.size(); ++i){
            cout<<result[i]<<" ";
        }   
        cout<<endl;
        return;
    }   
    //include begin
    result.push_back(begin);
    combi(begin+1);
    //not include begin
    result.pop_back();
    combi(begin+1);
}

int main()
{
    combi(0);
}

快3年没写C++了,凑合看吧。这里输出了所有情况,第一种输出是0,1,2和最后一种3实际是一种。

3. 一种巧妙构造方法

有种巧妙的方法可以用来表示其具体分的策略,我们把4种物品看成是4个位置,分到左边我们用1表示,分到右边用0表示,则上面的分法变成

1000010000100001110010101001

受到这个启发,我们来看从4位的2进制是什么样子的

0000(0),0001(1),0010(2),0011(3),0100(4),0101(5),0110(6),0111(7),1000(8),1001(9),1010(10),1011(11),1100(12),1101(13),1110(14),1111(15)

按上面的想法,这些数字就是分成2类时不同的组合方法,去掉首尾0和15,也是类似上面情况,首尾折叠的情况是重复的,因此想要得到组合的分法,只需要从1开始遍历至 2k1 ,输出其分法即可。我们给出spark的代码

def extractMultiClassCategories(
    input: Int,
    maxFeatureValue: Int): List[Double] = {
    var categories = List[Double]()
    var j = 0
    var bitShiftedInput = input
    while (j < maxFeatureValue) {
      if (bitShiftedInput % 2 != 0) {
        // updating the list of categories.
        categories = j.toDouble :: categories
      }
      // Right shift by one
      bitShiftedInput = bitShiftedInput >> 1
      j += 1
    }
    categories
  }

spark这里是用在多分类的无序特征处理中,输入参数input是1 until 2k1 ,maxFeatureValue是物品种类k,因为这里只需要判断特征值在左半边还是右半边,这里只输出了左半边的表示,也就是1的位置,实际是落入左边的特征值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值