程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

排列、组合的计算

排列、组合的计算

  1. 排列

从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的阶乘)。

 

  1. 组合

从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

 

  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;
}

posted on 2013-06-20 21:05  unixfy  阅读(580)  评论(0编辑  收藏  举报

导航