排列组合之线性排列

1、问题
1.1 袋中取球

image
袋子里有4个球,分别编号为{1, 2, 3, 4},依次取出,按照取出的先后从左至右排列,会得到一个不同的数字(如 1 2 3 4,有点像双色球开奖),求输出所有的数字组合。

1.2 不重复的数
有4个数字{0, 1, 2, 3},问用这4个数字能组成多少种不能的4位数(0123也算,因为我们也可以用{1, 2, 3, 4})?

2、排列组合
上述2个问题实际都是一个排列组合的问题,首先我们来计算一共有多少种组合

 

取第1个球

取第2个球

取第3个球

取第4个球

能取球的次数

4

3

2

1

组合数 = 4 * 3 * 2 * 1 = 4!
即n个球的组合总数 = n!
我们可以验证下:
1个球的组合:{0} = 1
2个球的组合:{0, 1}, {1, 0} = 2
3个球的组合:{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0} = 6

3、如何输出线型排列组合
根据我们刚才验证组合的过程,我们发现我们在取第n个球时,我们总是从最小的数字0开始,如果该球已被取过了,则跳过继续遍历下一个数字,不管是取第0个球还是第n个,都是如此,因此我们只要知道第n个球的数字,那么下一个球

的数字都可以用相同的方法求出来,直到只剩一个球为止。
算法公式f(x):
1> 当取第x个球时,假设前x - 1个球已取{a, b, c, ...}个数字,放在容器S中(当然,开始时,容器S是空的)
2> 从{0, ..., n-1}循环
    判断第i个数是否已被取过,即是否在S中
    如果在,则跳过这个数
    如果不在,则取出这个球,并添加到容器S中,表示该球已被占用
    这时,该输出下一个球(x+1)个球了,我们用同样的算法f(x+1)
    当x之后所有的球都被取出后,这时我们要继续循环下一个不在容器中的数字,但要注意,因为第x个球下次循环的数字将要改变了(i→i+1),球i将不再被占用,以供取后续的球之用,所以,我们要讲i这个球从占用容器S中取出
3> 很明显,这是一个递归过程,算法是有有限性的,那么什么时候这个递归停止呢
    我们知道,当取出后一个球(n-1)时,袋子里就没有球了,再取下一个时,本次递归就可以结束了

4. 算法

注意:根据编程习惯,第一个球的编号和首次取球都是以0开始

//每个递归,都遍历一遍 vectorBall 中的空值,然后下次递归,递归完后,将原来设定的值设为0
//nIndex = 取第x个球
void    CPermutation::SetBallNum(int nIndex, std::vector<int>& vectorBall, std::vector<int> vectorBallSet, std::vector<std::vector<int>>& vectorPermutation)
{
    int nEmptyBallNum = 0;
    int nBallNum = vectorBall.size();

    if (nBallNum == nIndex)
    {
        vectorPermutation.push_back(vectorBallSet);
        return;
    }

    for (int i = 0; i < nBallNum; i++)
    {
        //第x个球,最多有 nBallNum - x 个空位
        if (nEmptyBallNum >= (nBallNum - nIndex))
        {
            break;
        }
        //如果找到空值
        if (0 == vectorBall[i])
        {
            vectorBall[i] = 1;
            nEmptyBallNum++;
            vectorBallSet[nIndex] = i;
            SetBallNum(nIndex + 1, vectorBall, vectorBallSet, vectorPermutation);
            vectorBall[i] = 0;        //将尝试完的值设为0,再尝试下一个
        }
    }
}

//有n个编号为{0, n-1}的小球,依次取出,按照取出的先后从左至右排列,会得到一个不同的数字,打出所有的数字组合
//类似问题,用{0, n-1}个数,能组成多个数字不重复的n位数
bool    CPermutation::PermutationBall(int nBallNum /* = 4 */)
{
    printf("Ball num = %d
", nBallNum);

    std::vector<int>    vectorBallSet(nBallNum, 0);
    std::vector<int>    vectorBall(nBallNum, 0);
    std::vector<std::vector<int>> vectorPermutation;
    SetBallNum(0, vectorBall, vectorBallSet, vectorPermutation);
    PrintPermutation(vectorPermutation);

    return    true;
}
原文地址:https://www.cnblogs.com/organic/p/6033440.html