1. 程式人生 > >排列與組合的一些定理

排列與組合的一些定理

事物 規劃 四種 zoom org hellip com vpc assume

一,加法原理與乘法原理

加法原理與乘法原理是排列與組合的基礎。加法原理本質上是分類,乘法原理本質上是分步。

分類,就是把一個集合(某事物)分成互不相交的若幹獨立的部分。比如,概率論中的全概率公式就將事件分成”全劃分“

分類思想可以簡化程序的時間復雜度。比如:最短路徑算法-Dijkstra算法的應用之單詞轉換(詞梯問題)

分步,就是第一步幹嘛,第二步再幹嘛……比如A地到D地,第一步:先到達B地;第二步,再到達C地

二,排列

P(n,r)表示從n個數中選擇r個數的一個全排列

公式:P(n,r)=P(n-1,r)+p(n-1,r-1)*r

上面公式用到了分類思想:對於某個元素而言,要麽選,要麽不選。如果不選它,則在剩下的n-1個元素中選r個進行全排列;如果選了它,則在剩下的n-1個元素中只需要選r-1個元素進行全排列了。但是選了的那個元素,一共有r種位置存放(因為這是排列,同一個元素放在不同的位置 屬於不同的排列)故p(n-1,r-1)*r。

排列一共有三種:①線排列,就是通常的普通排列。

公式:P(n,r)=n!/(n-r)!

②圓排列,相當於排序的數圍成一個圓。比如循環隊列的那種實現方式。舉個圓排列的例子如下:

a b c d四個字母的線排列共有4!=24種。對於其中的一種排列: a b c d ,它對應著四種等價圓排列:

1) a b c d 2) b c d a 3)c d a b 4)d a b c

因此,對於給定n個數的所有圓排列,一共有 p(n,r)/r種。 因為,一個圓排列可以產生r個線排列。

③重排列

對於線排列而言,某個元素選了之後,就不能再選了(一個元素只能選一次)

對於重排列而言,一個元素可以選多次。

集合{∞·b1,∞·b2,....,∞·bn}的一個r排列個數為:nr

即從b1,b2...bn,共n個元素中選出r個,每個元素可選任意多次,一共有種nr排列

還有一種重排列是:每個元素最多只能選K次(某個固定的次數)

{K1·b1,K2·b2,....,Kn·bn}( 元素b1最多只能選K1次)的一個r排列個數為:(K1+K2+....+Kn)!/(K1!K2!...Kn!)

上面表示從b1,b2...bn,共n個元素中選出r個進行排列,但是 bj最多只能選Kj次,一共有(K1+K2+....+Kn)!/(K1!K2!...Kn!)次排列方式。

三,組合

C(n,r)表示從n個數中選擇r個數的一個全組合。

公式:C(n, r)=[n!/(r!)(n-r)!]=P(n,r)/r!

公式:C(n,r)=C(n-1,r)+C(n-1,r-1) 這個公式用到了分類思想。對於n個元素的某個元素,要麽選,要麽不選。

如果選了,只需在 剩下的n-1 個元素中選 r-1個;如果不選,則在剩下的n-1個元素中選r個(因為一共要選r個啊)。

這個公式非常有用,這是楊輝三解公式:其中基準條件C(n,0)=1,C(i,i)=1

C(0,0)

C(1,0) C(1,1)

C(2,0) C(2,1) C(2,2)

C(3,0) C(3,1) C(3,2) C(3,3)

..... ..... .....

如果要求解C(n,r),可以先求解出C(n-1,r) 和 C(n-1,r-1);再運用公式相加即可。很明顯,這是一個與Fib數列類似的遞歸計算。只不過求Fib(n)時,只有一個參數,而這裏有二個參數而已。當然,上面的程序效率是非常低的,因為它重復計算了很多子問題。代碼實現如下:

技術分享圖片
 1 public class YanHuiTriangle {
 2     //compute C(n,r)
 3     public static long c(int n, int r){
 4         if(n == 0 || r == 0)
 5             return 1;//base condition
 6         if(n == r)
 7             return 1;//base condition
 8         else
 9             return c(n-1, r-1) + c(n-1, r);
10     }
11 
12     //compute c(n,r)
13     public static long c_dp(int n ,int r){
14         long[][] dp = new long[n+1][r+1];
15         
16         //init
17         for(int i = 0; i <= n; i++)
18             dp[i][0] = 1;//if(n==0)
19         for(int i = 0; i <= r; i++)
20             dp[0][i] = 1;//if(r==0)
21         for(int i = 0; i <= r; i++)//Assume r < n
22             dp[i][i] = 1;//if(n==r)
23         
24         for(int i = 1; i <= n; i++)
25         {
26             for(int j = 1; j <= r; j++)
27             {
28                 if(j < i)
29                     dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
30                 else if(j > i)//從 i 個元素中選取出 j個元素(j>i 沒有意義)
31                     dp[i][j] = 0;
32             }
33         }
34         return dp[n][r];
35     }
36     
37     public static void main(String[] args) {
38         
39         long start_dp = System.currentTimeMillis();
40         System.out.println(c_dp(50, 10));
41         long end_dp = System.currentTimeMillis();
42         System.out.println("dp use time: " + (end_dp - start_dp) + "ms");
43         
44         long start = System.currentTimeMillis();
45         System.out.println(c(50,10));
46         long end = System.currentTimeMillis();
47         System.out.println("non dp use time: " + (end - start) + "ms");
48     }
49 }
技術分享圖片

技術分享圖片

這個示例完美地證明了DP(動態規劃)實現和遞歸實現的差距。是學習DP的一個好示例。如何將一個程序從遞歸改成DP?看上面的示例就會有啟示了。

其次,這也是0-1背包問題分解的思路,對於某件物品,要麽拿走,要麽不拿走。因此,這個組合公式在DP問題的分析中經常用到。

重組合公式:從集合{∞·b1,∞·b2,....,∞·bn}中選取r個元素,(不考慮次序),一共有多少種組合?

答案是:F(n,r) = C(n+r-1, r)

四,二項式定理

(X+Y)n = C(n,0)X0 Yn + C(n,1)X1 Yn-1 +.....+ C(n,n)Xn Y0

“二項式”定理嘛,就是有兩個項相加求n次冪。。。。。

參考

http://www.cnblogs.com/hapjin/p/5656632.html

排列與組合的一些定理