排列、组合的计算
排列、组合的计算
- 排列
从N中选择出M个元素,且保持选择的有序,每选择出M个元素后,将其看做是一组样本,那么样本总数为A(N, M)。
A(N, M) = N * N-1 * … * N-M+1
如果M=0,定义A(N, 0) = 1;
如果M=N,则A(N, N) = N * N-1 * N-2 * … * 3 * 2 * 1 = N!(N的阶乘)。
- 组合
从N中选择出M个元素,如果不考虑顺序,则样本总数为C(N, M)。
C(N, M) = (N * N-1 * … * N-M+1) / (M *M-1 * M-2 * … * 3 * 2 * 1)
如果M=0,定义C(N, 0) = 1;
如果M=N,则C(N, M) = C(N, N) = 1。
在计算组合的时候有一特性:C(N, M) = C(N, N-M),可以利用这一特性进行优化。
另外为了防止溢出改为:C(N, M) = N/M * N-1/M-1 * … * N-M+2/2 * N-M+1/1。
- 计算
// 程序 #include <iostream> #include <cmath> using namespace std; int fact(int n) { assert(n >= 0); if (n == 0 || n == 1) { return 1; } else { int ret = 1; for (int i = 2; i <= n; ++i) { ret *= i; } return ret; } } int perm(int n, int m) { assert(n >= 0 && m >= 0 && n >= m); if (m == 0) // 这种情况也涵盖了 n=0 && m=0 的情况 { return 1; } else { int ret = 1; for (int i = m; i >= n-m+1; --i) { ret *= i; } return ret; } } int comb(int n, int m) { assert(n >= 0 && m >= 0 && n >= m); // 优化 C(N, M) = C(N, N-M) // 目的:减少不必要的操作 if (m > n/2) { m = n - m; } if (m == 0) // 包括了 n=0 { return 1; } // return perm(n, m) / fact(m); // 优化 // 防止溢出 double ret = 1.0; for (int i = 0; i < m; ++i) { // ret *= (n-i)/(m-i); 这样做事错误的,因为整数相乘导致小数点后丢失 ret *= static_cast<double>(n-i) / (m-i); } return static_cast<int>(ret); } // 应用 // 从3组N个玻璃球里,选择3*M个玻璃球,求3组分别M的概率 double crystal_ball(int n, int m, int a = 3) { // cout << comb(n, m) << endl; // cout << comb(n*a, m*a) << endl; return static_cast<double>(pow(static_cast<double>(comb(n, m)), a)) / comb(n*a, m*a); } int main() { cout << crystal_ball(5, 4) << endl; cout << crystal_ball(6, 4) << endl; cout << crystal_ball(8, 4) << endl; system("PAUSE"); return 0; }
(完)
文档信息
·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
·博客地址:http://www.cnblogs.com/unixfy
·博客作者:unixfy
·作者邮箱:goonyangxiaofang(AT)163.com
·如果你觉得本博文的内容对你有价值,欢迎对博主 小额赞助支持