1. 程式人生 > >計算組合數的演算法

計算組合數的演算法

概率論是統計分析的基礎,而統計分析在很多領域比如人工智慧,生物資訊學中作為理論基礎,具有廣泛的應用場景。組合數在概率論中常用,本文就對組合數的數值計算給出一個C++求解的演算法。通過實現該演算法,對數值計算上的某些方法加深的認識。
首先,來看一下組合數的公式:
這裡寫圖片描述
通過該公式,首先會想到使用暴力求解法來計算。但階乘的計算,很容易就會造成數值的溢位,因此暴力求解法在數值稍大的情況下就無法適用。
緊接著,想到常用的處理溢位的方法,利用轉換成對數進行運算,使用該種方法可將連乘轉換成連加,溢位問題是解決了,但精度上卻又不能滿足要求。
於是,需要探索其他的方法來解決這一問題。考慮到分子分母均為階乘,如果對分子分母分別分解質因數,然後對共同的質因數進行約分,最終剩下的質因數的乘積就是我們最終要求的結果。使用這一方法的好處,還在於我們可以通過記錄質因數及質因數存在的次數來記錄最終的結果,而不必直接記錄最終的結果,這樣就可以同時解決溢位問題和精度問題。

在本文最後,我附上了具體的C++實現的程式碼。在看程式碼之前,我們先將其中的核心部分進行一下解讀與分析。
1.初始化素數動態陣列(C++的動態陣列可以用STL中的向量來實現)。在判斷某個數是否素數時,我們只需要輪循到該數的平方根即可,這將大大減少內層迴圈的演算法複雜度。
這裡寫圖片描述
2.n的階乘裡面含有素數的次數。因為n!表示n(n-1)(n-2)…1,因此如果n>=x(素數),則必然至少存在n/x次素數x,並且這n/x個數除以素數x之後得到的新的排列中,又會又(n/x)/x次素數x。換句話說,素數x的所有次數,均會包含在第一次迴圈能夠整除x的這些數中,而這些數中有的可能包括多次的素數x。因此,通過getPrimePow方法,則得到了n!裡面含有素數x的所有次數,記為pow返回。
這裡寫圖片描述


3.計算組合數。根據組合數的公式,可以看到,通過計算n!中含有某個素數的次數,m!中含有這個素數的次數和(n-m)!中含有素數的次數,就可以得到最終結果中應該含有幾個該素數。
這裡寫圖片描述
4.將結果儲存在C++ STL中的Map結構中。Map的key使用素數做下標即可,值即為該素數在最終結果中的次數。為防止溢位,最終結果用該種方式儲存即可。

具體的演算法如下:

#ifndef _COMBINE_CALCULATOR_H_
#define _COMBINE_CALCULATOR_H_
#include <vector>
#include <map>
#include <iostream>
using namespace std; class CombineCalculator { public: CombineCalculator(); ~CombineCalculator(); //計算組合數,C(n,m) bool calculateCombine(int n, int m); //列印結果 void printResult(); private: //計算n!中含有素數x的次數 int getPrimePow(int n, int x); //生成素數 void generatePrime(); //存放素數動態陣列 vector<int> primeVec; //結果Map,key為素數,value為該素數的次數 map<int, int> resultMap; const int MAXN = 1000; const int MAXPRIME = 1000; }; #endif
#include "CombineCalculator.h"

CombineCalculator::CombineCalculator()
{
    generatePrime();
}

CombineCalculator::~CombineCalculator()
{

}

void CombineCalculator::generatePrime()
{
    for (int i = 2; i < MAXPRIME; i++)
    {
        bool isPrime = true;
        int s = sqrt(i);
        for (int j = 2; j <= s; j++)
        {
            if (i%j == 0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            primeVec.push_back(i);
        }
    }
    return;
}


int CombineCalculator::getPrimePow(int n, int x)
{
    int pow = 0;
    while (n >= x)
    {
        int temp = n / x;
        pow += temp;
        n = temp;
    }
    return pow;
}


bool CombineCalculator::calculateCombine(int n, int m)
{
    bool result = true;
    if (n > MAXN)
    {
        result = false;

    }
    else
    {
        for (int i = 0; i < primeVec.size(); i++)
        {
            int pow = getPrimePow(n, primeVec[i]) - getPrimePow(m, primeVec[i]) - getPrimePow(n - m, primeVec[i]);
            if (pow != 0)
                resultMap.insert(std::pair<int,int>(primeVec[i], pow));
        }
    }
    return result;
}

void CombineCalculator::printResult()
{
    map<int, int>::const_iterator iter;
    for (iter = resultMap.begin(); iter != resultMap.end(); iter++)
    {
        cout << "Key:" << iter->first << "Value:" << iter->second << endl;
    }
}

相關推薦

計算合數演算法

概率論是統計分析的基礎,而統計分析在很多領域比如人工智慧,生物資訊學中作為理論基礎,具有廣泛的應用場景。組合數在概率論中常用,本文就對組合數的數值計算給出一個C++求解的演算法。通過實現該演算法,對數值計算上的某些方法加深的認識。 首先,來看一下組合數的公式:

計算合數的遞迴演算法

#include#include#define max 100int combinat(int m,int n){    int i,j;    int C[max][max];    if(n==0||n==m)        return 1;    else    { 

演算法競賽4-1 計算合數

編寫函式,引數是兩個非負整數n和m,返回組合數 Cmn=n!m!(n−m)!Cnm=n!m!(n−m)! ,其中,m<=n<=25。例如,n=25,m=12時答案為5200300。

遞歸(計算合數、判斷回文字符串、漢諾塔問題)

文字 bigint 是否 rar blog rgs port 所有 相等 一.使用組合數公式利用n!來計算 1.設計思想 先輸入整數n和k,分別用計算n!的遞歸的方法算出n!,k!和(n-k)!的結果,再計算n!/(k!(n-k)!!。用大數類BigInte

使用計算機計算合數+漢諾塔+判斷回文

span http 問題 out pan line length ssa arr 使用計算機計算組合數 一、實驗設計思想: 定義類A來存放遞推求階乘的方法,類B存放利用楊輝三角求cnk的方法,類C存放遞歸求階乘的方法,A中即1*2*3.。。。較為簡單,B中先定義數組

課程作業03:用遞歸方法計算合數、解決漢諾塔問題、判斷某個字符串是否回文

java class ply math alt static multi 構造 strong 課後作業1:使用計算機計算組合數 (1)使用組合數公式利用n!來計算 程序設計思想: 設計並調用大數求階乘的方法結合組合數公式計算組合數的值。 程序流程圖: 程序源代碼

運用對數函數<計算合數>

class () color 整理 turn 組合 blog amp gpo 先貼上一張組合數的基本公式吧,在這裏我們暫且規定n為下標,m為上標(n≥m) C(n,m) = n! / [ m!(n-m)! ] 以下思路借鑒於某位大神,為了方便自己理解,我稍微做了些整理。

計算合數

代碼 如果 mat sca class return erro blog 原理 #include <stdio.h> #include <math.h> // 請先獨立完成,如果有困難可以翻閱本書代碼倉庫中的答案,但一定要再次獨立完成。 // “抓住

Binomial Showdown(計算合數) POJ

題目:In how many ways can you choose k elements out of n elements, not taking order into account? Write a program to compute this number.Inp

計算合數 (sdut oj)

計算組合數 Time Limit: 1000MS Memory Limit: 32768KB Problem Description 計算組合數。C(n,m),表示從n個數中選擇m個的組合

遞迴遞推之計算合數

題目大概: 按題目給出的公式求組合數。 思路: 用遞迴函式,遞迴求解組合數。 感想: 一般有公式的題大部分要用遞迴。 程式碼: #include <iostream>using names

計算合數 oj

計算組合數 Time Limit: 1000 ms Memory Limit: 32768 KiB Problem Description 計算組合數。C(n,m),表示從n個數中選擇m個的組合數

C語言程式訓練-1586-計算合數

Problem Description 計算組合數。C(n,m),表示從n個數中選擇m個的組合數。 計算公式如下: 若:m=0,C(n,m)=1 否則, 若 n=1,C(n,m)=1 否則,若m=n,C(n,m)=1 否則 C(n,m) = C(n-1,m-1)

CCF NOI1063 計算合數

時間限制: 1000 ms  空間限制: 262144 KB 題目描述   給出兩個非負整數n和m,編寫程式計算組合數C(n,m)的值。 輸入 一行兩個空格隔開的非負整數n,m。 輸出 輸出一個數表示C(n,m)。 樣例輸入 5 2樣例輸出 10 資料範圍限制  

打表計算合數

clas code () init 組合 pan void ini div 打表,C(n,m)=C(n-1,m-1)+C(n-1,m) n<=10000 const int maxn=100; void init(){ int i,j; f

C# 計算排列合數,及列出所有組合形式的演算法

前段時間有同學問到,如何程式設計求排列組合數,以及列出所有排列組合形式的演算法。乘著放假,寫了一種實現的方法!怕時間長了,淹沒在硬盤裡,記錄在此! /// <summary> /// 計算Int32型別的整數的階乘,

合數計算

turn fzu clu for 組合數 題意 pri ++ 問題 - -首先謝大佬 http://blog.csdn.net/acdreamers/article/details/8037918 引用一段方便自己以後查閱 組合數取模就是求的值; (2)和,並且是素數

一道合數行列式的計算

成對 span dot ots class AS 依次 end right 一道行列式計算 2018.04.10 \[ \det A=\left| \begin{matrix} 1& 1& \cdots& 1\

計算一個N個選項中和為X的所有合數

 下面就是一個最近微信上流傳的一個測試: 假如5塊錢可以買一個女朋友,你會買什麼樣的?下面是每項的價格 有錢-4元, 長得好看-3元, 會做飯-3元, 忠誠-3元, 處女-2元, 溫柔-2元, 活潑可愛-2元, 大長腿-1元, 聰明-1元, 胸大-1元 --計算從