• 排列、组合的计算


    排列、组合的计算

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

    (完)

     

    文档信息


    ·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
    ·博客地址:http://www.cnblogs.com/unixfy
    ·博客作者:unixfy
    ·作者邮箱:goonyangxiaofang(AT)163.com
    ·如果你觉得本博文的内容对你有价值,欢迎对博主 
    小额赞助支持


     

     

  • 相关阅读:
    IOS系统设置页面跳转
    android常用自动化测试框架
    Gradle学习总结
    软件开发版本管理阶段描述
    Swift学习笔记(10):类和结构体
    Swift学习笔记(9):枚举
    Swift学习笔记(8):闭包
    Swift学习笔记(7):函数
    Swift学习笔记(6):控制流
    JSON序列化器/解析器
  • 原文地址:https://www.cnblogs.com/unixfy/p/3147137.html
Copyright © 2020-2023  润新知