1. 程式人生 > >對酒當歌,人生幾何。譬如朝露,去日苦多

對酒當歌,人生幾何。譬如朝露,去日苦多

今天把常用數學公式用C/C++表達組織一下,以便日後使用。

排列組合數


先來看看排列組合數的公式:
Amn=n(n1)(nm+1)=n!(nm)!
Cmn=Amnm!=n!m!(nm)!=Cnmn

顯然寫程式的話,Amn很容易用一個for迴圈表示,而Cmn=n(n1)(nm+1)1×2m的形式更容易用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的一組隨機全排列,不能指定下限。