今天把常用数学公式用C/C++表达组织一下,以便日后使用。
排列组合数
先来看看排列组合数的公式:
Amn=n(n−1)…(n−m+1)=n!(n−m)!
Cmn=Amnm!=n!m!(n−m)!=Cn−mn
显然写程序的话, Amn 很容易用一个for循环表示,而 Cmn=n(n−1)…(n−m+1)1×2…m 的形式更容易用for循环表示,直接看代码:
// 排列组合数
#define uint unsigned int
// 函数名称: Anm
// 函数功能: 从n中取m的全排列数
uint Anm(uint n, uint m)
{
uint sum = 1, i = 1;
for(i=n; i>= (n-m+1); i--)
sum *= i;
return (sum);
}
// 函数名称: Cnm
// 函数功能: 从n中取m的组合数
uint Cnm(uint n, uint m)
{
uint i = 1, sum = 1;
for(i=1; i<=m; i++)
sum = sum * (n+1-i)/i; // 千万不可错写成了 sum *= (n+1-i)/i;
/*
* 千万不可错写成了 sum *= (n+1-i)/i;
* 因为后者会先计算分式,会导致整数互质除不尽的错误
*/
return (sum);
}
随机输出任意一个全排列
// 函数名称: RandArrange
// 函数功能: 在指定范围内输入任意的一个全排列
#include <algorithm>
#include <time.h>
#define uint unsigned int
void RandArrange(uint* uiResTbl, uint rangeMin, rangeMax)
{
// Seed the random-number generator with the current time so that
// the numbers will be different every time we run.
srand((uint)time(NULL));
uint nCnt = rangeMax - rangeMin + 1;
uint uiRand, i, j;
i = j = 0;
memset(uiResTbl, -1, nCnt);
while(i < nCnt)
{
j = 0;
uiRand = (double)rand() / (RAND_MAX + 1) * (rangeMax + 1 - rangeMin) + rangeMin;
while(j < i)
{
if(uiResTbl[j] == uiRand) break;
j++;
}
if(i != j)
continue;
uiResTbl[i] = uiRand;
i++;
}
}
十分值得注意的是,此函数并不稳定,产生满足要求的全排列的时间不定。
随机输出一组全排列的情况在实际的仿真中用得比较多,如从某数据集中随机选取指定数目的样本。(显然这是一种简单的求部分全排列)而MATLAB中有现成的输出随机全排列的函数 randperm
在MATLAB命令窗口中Help randperm函数:
与上面这个 RandArrange函数不同的是,MATLAB中的randperm函数是输出从1到n的一组随机全排列,不能指定下限。