排列組合的計算

排列

排列 (permutation) 指的是:有限多個元素以任何順序所做的一種安排,需動用到全部的元素,叫做這些給定元素的一種排列。

例如:有 3 個字母 a, b, c,按照不同的排列順序,可以有幾種排列方式?共有以下情形:abc, acb, bac, bca, cab, cba 這 6 種排列。

我們可以這樣想,在第一個位置有 3 種選擇,第二個位置剩下 2 種選擇,第三個位置只有 1 種選擇。所以共有 3 × 2 × 1 = 6 種排列方式。也就是 3 階乘 (factorial of 3),數學以 3! 來表示。

同理,若有 9 個不同字母,就有 9! 種排列方式。

在 Python 裡,math 函式庫裡,計算階乘的函數是 math.factorial(x),其中 x 需為正整數。

In [1]: import math

In [2]: math.factorial(3)
Out[2]: 6

In [3]: math.factorial(9)
Out[3]: 362880

n 個相異元素中,所抽出的任一組 k 個元素集,若將具有相同的元素但順序不同的排列也視為相異時,共有幾種排列方式,稱之為有幾種排列數

例如從 5 個字母 a, b, c, d, e 中,任取 2 個字母,順序不同也視為不同排列,有幾種排列數?會有:
ab, ac, ad, ae,
ba, bc, bd, be,
ca, cb, cd, ce,
da, db, dc, de,
ea, eb, ec, ed
會有以上 20 種排列數。

我們可以這麼想,任取 2 個字母,取第一個字母時有 5 種選擇,取第二個字母時只有 4 種選擇,所以共有 20 種排列數。

排列數 P_{k}^{n}= \frac{n!}{(n-k)!}

我們可以用 math.perm(n, k) 這個函數來計算排列數:

 In [2]: math.perm(5, 2)
 Out[2]: 20

組合

組合 (combination):從 n 個相異元素中,所抽出的任一組 k 個元素集,稱為一個組合。在組合中,並不考慮元素出現的順序,不同順序也視為同一個組合。

例如從 5 個字母 a, b, c, d, e 中,任取 2 個字母,不考慮順序,有幾種組合?會有:
ab, ac, ad, ae, bc, bd, be, cd, ce, de 這 10 種組合。

組合數:從 n 個相異元去中取出 k 個元素的組合數,因為不同順序也視為同一種組合,所以組合數會是排列數除以 k! 。

組合數 C_{k}^{n}= \frac{n!}{(n-k)! k!}

Python 裡用 math.comb(n, k) 來計算組合數。

 In [3]: math.comb(5, 2)
 Out[3]: 10

itertools 函式庫

這裡我們利用 itertools 這個函式庫,來呈現排列組合。首先匯入這個函式庫:

 In [1]: import itertools

如果我們要把 “abcde" 這個字串做排列,可以用 permutations(n) 這個函數來產生所有排列的 list,我們把這個 list 命名為 perm1:

 In [12]: perm1 = itertools.permutations("abcde")

再利用一個簡單的 for 迴圈,把 5! = 120種排列列印出來:

 In [13]: for i in list(perm1):
     ...:     print(i)
     ...:     
 ('a', 'b', 'c', 'd', 'e')
 ('a', 'b', 'c', 'e', 'd')
 ('a', 'b', 'd', 'c', 'e')
 ('a', 'b', 'd', 'e', 'c')
 ('a', 'b', 'e', 'c', 'd')
 ('a', 'b', 'e', 'd', 'c')
 ('a', 'c', 'b', 'd', 'e')
 ('a', 'c', 'b', 'e', 'd')
 ('a', 'c', 'd', 'b', 'e')
 ('a', 'c', 'd', 'e', 'b')
 ('a', 'c', 'e', 'b', 'd')
 ('a', 'c', 'e', 'd', 'b')
 ('a', 'd', 'b', 'c', 'e')
 ('a', 'd', 'b', 'e', 'c')
 ('a', 'd', 'c', 'b', 'e')
 ('a', 'd', 'c', 'e', 'b')
 ('a', 'd', 'e', 'b', 'c')
 ('a', 'd', 'e', 'c', 'b')
 ('a', 'e', 'b', 'c', 'd')
 ('a', 'e', 'b', 'd', 'c')
 ('a', 'e', 'c', 'b', 'd')
 ('a', 'e', 'c', 'd', 'b')
 ('a', 'e', 'd', 'b', 'c')
 ('a', 'e', 'd', 'c', 'b')
 ('b', 'a', 'c', 'd', 'e')
 ('b', 'a', 'c', 'e', 'd')
 ('b', 'a', 'd', 'c', 'e')
 ('b', 'a', 'd', 'e', 'c')
 ('b', 'a', 'e', 'c', 'd')
 ('b', 'a', 'e', 'd', 'c')
 ('b', 'c', 'a', 'd', 'e')
 ('b', 'c', 'a', 'e', 'd')
 ('b', 'c', 'd', 'a', 'e')
 ('b', 'c', 'd', 'e', 'a')
 ('b', 'c', 'e', 'a', 'd')
 ('b', 'c', 'e', 'd', 'a')
 ('b', 'd', 'a', 'c', 'e')
 ('b', 'd', 'a', 'e', 'c')
 ('b', 'd', 'c', 'a', 'e')
 ('b', 'd', 'c', 'e', 'a')
 ('b', 'd', 'e', 'a', 'c')
 ('b', 'd', 'e', 'c', 'a')
 ('b', 'e', 'a', 'c', 'd')
 ('b', 'e', 'a', 'd', 'c')
 ('b', 'e', 'c', 'a', 'd')
 ('b', 'e', 'c', 'd', 'a')
 ('b', 'e', 'd', 'a', 'c')
 ('b', 'e', 'd', 'c', 'a')
 ('c', 'a', 'b', 'd', 'e')
 ('c', 'a', 'b', 'e', 'd')
 ('c', 'a', 'd', 'b', 'e')
 ('c', 'a', 'd', 'e', 'b')
 ('c', 'a', 'e', 'b', 'd')
 ('c', 'a', 'e', 'd', 'b')
 ('c', 'b', 'a', 'd', 'e')
 ('c', 'b', 'a', 'e', 'd')
 ('c', 'b', 'd', 'a', 'e')
 ('c', 'b', 'd', 'e', 'a')
 ('c', 'b', 'e', 'a', 'd')
 ('c', 'b', 'e', 'd', 'a')
 ('c', 'd', 'a', 'b', 'e')
 ('c', 'd', 'a', 'e', 'b')
 ('c', 'd', 'b', 'a', 'e')
 ('c', 'd', 'b', 'e', 'a')
 ('c', 'd', 'e', 'a', 'b')
 ('c', 'd', 'e', 'b', 'a')
 ('c', 'e', 'a', 'b', 'd')
 ('c', 'e', 'a', 'd', 'b')
 ('c', 'e', 'b', 'a', 'd')
 ('c', 'e', 'b', 'd', 'a')
 ('c', 'e', 'd', 'a', 'b')
 ('c', 'e', 'd', 'b', 'a')
 ('d', 'a', 'b', 'c', 'e')
 ('d', 'a', 'b', 'e', 'c')
 ('d', 'a', 'c', 'b', 'e')
 ('d', 'a', 'c', 'e', 'b')
 ('d', 'a', 'e', 'b', 'c')
 ('d', 'a', 'e', 'c', 'b')
 ('d', 'b', 'a', 'c', 'e')
 ('d', 'b', 'a', 'e', 'c')
 ('d', 'b', 'c', 'a', 'e')
 ('d', 'b', 'c', 'e', 'a')
 ('d', 'b', 'e', 'a', 'c')
 ('d', 'b', 'e', 'c', 'a')
 ('d', 'c', 'a', 'b', 'e')
 ('d', 'c', 'a', 'e', 'b')
 ('d', 'c', 'b', 'a', 'e')
 ('d', 'c', 'b', 'e', 'a')
 ('d', 'c', 'e', 'a', 'b')
 ('d', 'c', 'e', 'b', 'a')
 ('d', 'e', 'a', 'b', 'c')
 ('d', 'e', 'a', 'c', 'b')
 ('d', 'e', 'b', 'a', 'c')
 ('d', 'e', 'b', 'c', 'a')
 ('d', 'e', 'c', 'a', 'b')
 ('d', 'e', 'c', 'b', 'a')
 ('e', 'a', 'b', 'c', 'd')
 ('e', 'a', 'b', 'd', 'c')
 ('e', 'a', 'c', 'b', 'd')
 ('e', 'a', 'c', 'd', 'b')
 ('e', 'a', 'd', 'b', 'c')
 ('e', 'a', 'd', 'c', 'b')
 ('e', 'b', 'a', 'c', 'd')
 ('e', 'b', 'a', 'd', 'c')
 ('e', 'b', 'c', 'a', 'd')
 ('e', 'b', 'c', 'd', 'a')
 ('e', 'b', 'd', 'a', 'c')
 ('e', 'b', 'd', 'c', 'a')
 ('e', 'c', 'a', 'b', 'd')
 ('e', 'c', 'a', 'd', 'b')
 ('e', 'c', 'b', 'a', 'd')
 ('e', 'c', 'b', 'd', 'a')
 ('e', 'c', 'd', 'a', 'b')
 ('e', 'c', 'd', 'b', 'a')
 ('e', 'd', 'a', 'b', 'c')
 ('e', 'd', 'a', 'c', 'b')
 ('e', 'd', 'b', 'a', 'c')
 ('e', 'd', 'b', 'c', 'a')
 ('e', 'd', 'c', 'a', 'b')
 ('e', 'd', 'c', 'b', 'a')

若我們要在 5 個字母中,任取 2 個字母,順序不同視為不同排列,共有 5! / 3! = 20 種排列數。可以用 permutations(n, k) 產生各種排列,再印出:

 In [14]: perm2 = itertools.permutations("abcde", 2)
 
 In [15]: for i in list(perm2):
     ...:     print(i)
     ...:     
 ('a', 'b')
 ('a', 'c')
 ('a', 'd')
 ('a', 'e')
 ('b', 'a')
 ('b', 'c')
 ('b', 'd')
 ('b', 'e')
 ('c', 'a')
 ('c', 'b')
 ('c', 'd')
 ('c', 'e')
 ('d', 'a')
 ('d', 'b')
 ('d', 'c')
 ('d', 'e')
 ('e', 'a')
 ('e', 'b')
 ('e', 'c')
 ('e', 'd')

接著,考慮組合數的問題,若我們要在 5 個字母中,任取 2 個字母,忽略順序,共有幾種組合數?答案是:
C_{2}^{5}= \frac{2!}{(5-2)! 2!} = 10 種組合。
此時可以用 combinations(n, k) 這個函數:

 In [2]: comb1 = itertools.combinations("abcde", 2)

 In [3]: for i in list(comb1):
    ...:     print(i)
    ...:     
 ('a', 'b')
 ('a', 'c')
 ('a', 'd')
 ('a', 'e')
 ('b', 'c')
 ('b', 'd')
 ('b', 'e')
 ('c', 'd')
 ('c', 'e')
 ('d', 'e')

參考閱讀:
https://docs.python.org/3.8/library/math.html

https://docs.python.org/3.8/library/itertools.html

https://docs.python.org/3.8/tutorial/controlflow.html#for-statements

https://www.w3schools.com/python/python_lists.asp