排列与逆序

文章目录

  • 1 逆序
  • 2 定理
    • 算法1:从逆序构建一个排列
    • 算法2:从逆序列构建一个排列
  • 3 奇排列与偶排列
  • 4 交换得到自然顺序
  • 5 参考资料

1 逆序

  逆序的概念大家都不陌生,它在行列式理论中是很重要的。

  设 i 1 , i 2 , ⋯   , i n i_1,i_2,\cdots,i_n i1,i2,,in是集合 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}的一个排列。如果 k < l k\lt l k<l i k > i l i_k\gt i_l ik>il,则称数对 ( i k , i l ) (i_k,i_l) (ik,il)为一个逆序。通俗地讲,一个排列中的逆序对应着不以自然顺序出现的一对数。例如 31524 31524 31524中的逆序有 ( 3 , 1 ) , ( 3 , 1 ) , ( 5 , 2 ) , ( 5 , 4 ) (3,1),(3,1),(5,2),(5,4) (3,1),(3,1),(5,2),(5,4)。显然集合 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}的自然顺序 12 ⋯ n 12\cdots n 12n是唯一没有逆序的排列。

  对于一个排列 i 1 , i 2 , ⋯   , i n i_1,i_2,\cdots,i_n i1,i2,,in,令 a j a_j aj表示逆序对第二个成分为 j j j的逆序对数量,即 a j a_j aj表示在排列中在 j j j前面且大于 j j j的整数的个数。它度量 j j j的无序程度

  逆序列:数值序列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an叫做排列 i 1 , i 2 , ⋯   , i n i_1,i_2,\cdots,i_n i1,i2,,in逆序列。 a 1 + a 2 + ⋯ + a n a_1+a_2+\cdots+a_n a1+a2++an度量一个排列的无序程度。例如 31524 31524 31524的逆序列为 1 , 2 , 0 , 1 , 0 1,2,0,1,0 1,2,0,1,0

  逆序列满足下面的条件:
0 ≤ a 1 ≤ n − 1 0 ≤ a 2 ≤ n − 2 ⋯ 0 ≤ a n − 1 ≤ 1 a n = 0 0\le a_1 \le n-1\\ 0\le a_2 \le n-2\\ \cdots \\ 0\le a_{n-1}\le 1\\ a_n = 0 0a1n10a2n20an11an=0
这个条件的原因很显然,对于每个 k = 1 , 2 , ⋯   , n k=1,2,\cdots, n k=1,2,,n,在集合中存在 n − k n-k nk个大于 k k k的整数。显然满足上述条件的序列个数等于 n ∗ ( n − 1 ) ∗ ⋯ 1 = n ! n*(n-1)*\cdots 1=n! n(n1)1=n!。这个数等于 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}的排列个数。

2 定理

  设 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn是满足下面条件的整数序列:
0 ≤ b 1 ≤ n − 1 0 ≤ b 2 ≤ n − 2 ⋯ 0 ≤ b n − 1 ≤ 1 b n = 0 0\le b_1 \le n-1\\ 0\le b_2 \le n-2\\ \cdots \\ 0\le b_{n-1}\le 1\\ b_n = 0 0b1n10b2n20bn11bn=0
那么,一定存在一个唯一一个 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}排列,它的逆序列是 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn。我们可以通过找到唯一构建逆序列的排列的方法来证明这个定理。

算法1:从逆序构建一个排列

  算法步骤如下:

  • n n n:写出 n n n
  • n − 1 n-1 n1:考虑 b n − 1 b_{n-1} bn1。我们知道 0 ≤ b n − 1 ≤ 1 0\le b_{n-1}\le 1 0bn11。如果 b n − 1 = 0 b_{n-1}=0 bn1=0,则 n − 1 n-1 n1应该放在 n n n的前面。如果 b n − 1 = 1 b_{n-1}=1 bn1=1,则 n − 1 n-1 n1应该放在 n n n的后面。
  • n − 2 n-2 n2:考虑 b n − 2 b_{n-2} bn2。类似地,我们知道 0 ≤ b n − 1 ≤ 2 0\le b_{n-1}\le 2 0bn12。如果 b n − 2 = 0 b_{n-2}=0 bn2=0,则 n − 2 n-2 n2应该放在上一步构建完的两个数的前面。如果 b n − 2 = 1 b_{n-2}=1 bn2=1,则 n − 2 n-2 n2应该放在两个数的中间。如果 b n − 2 = 0 b_{n-2}=0 bn2=0,则 n − 2 n-2 n2应该放在两个数的后面。
  • ⋯ ⋯ \cdots \cdots
  • n − k n-k nk:(一般步骤)我们知道 0 ≤ b n − k ≤ k 0\le b_{n-k}\le k 0bnkk,从步骤 n n n到步骤 n − k + 1 n-k+1 nk+1中, k k k个数 n , n − 1 , ⋯   , n − k + 1 n,n-1,\cdots,n-k+1 n,n1,,nk+1的顺序都已经排好了。如果 b n − k = 0 b_{n-k}=0 bnk=0则放在最前面 … … \dots\dots 如果 b n − k = k b_{n-k}=k bnk=k则放在最后面。
  • ⋯ ⋯ \cdots \cdots
  • 1 1 1:我们必须把 1 1 1放在上一步所构造的序列的第 b 1 b_1 b1个数的后面。

  通过上述步骤构建排列时,步骤 n , n − 1 , n − 2 , ⋯   , 1 n,n-1,n-2,\cdots,1 n,n1,n2,,1根据逆序列 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn唯一确定 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}的一个排列。这个算法的缺点是:排列中的每一个整数的位置不到最后是不得而知的,算法在执行的过程中只做到了这些整数的相对位置保持固定

  例子:构建 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } \{1,2,3,4,5,6,7,8\} {1,2,3,4,5,6,7,8}的一个排列,使其逆序列是 5 , 3 , 4 , 0 , 2 , 1 , 1 , 0 5,3,4,0,2,1,1,0 5,3,4,0,2,1,1,0。根据上述算法:
8 : 8 7 : 87 6 : 867 5 : 8657 4 : 48657 3 : 486537 2 : 4862537 1 : 48625137 \begin{aligned} &8:8\\ &7:87\\ &6:867\\ &5:8657\\ &4:48657\\ &3:486537\\ &2:4862537\\ &1:48625137 \end{aligned} 8:87:876:8675:86574:486573:4865372:48625371:48625137

python实现起来也比较容易:

def generate_algo1(seq):
    permutation = []
    for i in range(len(seq), 0, -1):
        permutation.insert(int(seq[i-1]), i)
    return permutation

seq = input().split()
print(generate_algo1(seq))
输入:5 3 4 0 2 1 1 0
输出:[4, 8, 6, 2, 5, 1, 3, 7]

算法2:从逆序列构建一个排列

  我们从 n n n个空位置上出发,把这些空位置从左到右标记上 1 , 2 , ⋯   , n 1,2,\cdots, n 1,2,,n。算法步骤如下:

  • 1 1 1:因为在这个排列中 1 1 1之前有 b 1 b_1 b1个整数,所以必须把 1 1 1放在位置标签为 b 1 + 1 b_1 + 1 b1+1的位置上。
  • 2 2 2:因为在这个排列中有 b 2 b_2 b2个整数大于 2 2 2且在 2 2 2之前,又因为这些证书还没有被插入进来,所以必须给这些数留出 b 2 b_2 b2个空位置。因此,把 2 2 2放在第 b 2 + 1 b_2+1 b2+1的空位置上。
  • ⋯ ⋯ \cdots \cdots
  • k k k:(一般步骤)因为在这个排列中 k k k前面还应该有 b k b_k bk个整数,这些整数还没有被插进来,因此必须给这些数留出 b k b_k bk个空位置。在本步骤开始时,空位置的个数是 n − ( k − 1 ) = n − k + 1 n-(k-1)=n-k+1 n(k1)=nk+1,把 k k k放在从左边数第 b k + 1 b_k+1 bk+1个空位置上。因为 b k ≤ n − k b_k\le n-k bknk,所以可以确定一个空位置。
  • ⋯ ⋯ \cdots \cdots
  • n n n:把 n n n放在剩下的一个空位置上。

  这种方法中, 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n在排列中的位置是确定的。依旧用上述例子 5 , 3 , 4 , 0 , 2 , 1 , 1 , 0 5,3,4,0,2,1,1,0 5,3,4,0,2,1,1,0来过一遍该算法:
1 : _   _   _   _   _   1   _   _ 2 : _   _   _   2   _   1   _   _ 3 : _   _   _   2   _   1   3   _ 4 : 4   _   _   2   _   1   3   _ 5 : 4   _   _   2   5   1   3   _ 6 : 4   _   6   2   5   1   3   _ 7 : 4   _   6   2   5   1   3   7 8 : 4   8   6   2   5   1   3   7 \begin{aligned} &1:\_ \ \_\ \_\ \_\ \_\ 1\ \_\ \_\\ &2:\_ \ \_\ \_\ 2\ \_\ 1\ \_\ \_\\ &3:\_ \ \_\ \_\ 2\ \_\ 1\ 3\ \_\\ &4:4 \ \_\ \_\ 2\ \_\ 1\ 3\ \_\\ &5:4 \ \_\ \_\ 2\ 5\ 1\ 3\ \_\\ &6:4 \ \_\ 6\ 2\ 5\ 1\ 3\ \_\\ &7:4 \ \_\ 6\ 2\ 5\ 1\ 3\ 7\\ &8:4 \ 8\ 6\ 2\ 5\ 1\ 3\ 7\\ \end{aligned} 1:_ _ _ _ _ 1 _ _2:_ _ _ 2 _ 1 _ _3:_ _ _ 2 _ 1 3 _4:4 _ _ 2 _ 1 3 _5:4 _ _ 2 5 1 3 _6:4 _ 6 2 5 1 3 _7:4 _ 6 2 5 1 3 78:4 8 6 2 5 1 3 7
python实现:

def generate_algo2(seq):
    n = len(seq)
    permutation = [None]*n
    for i in range(1, n+1):
        j, k = 0, int(seq[i-1]) + 1
        while j < n:
            if not permutation[j]:
                k -= 1
                if k == 0:
                    permutation[j] = i
                    break
            j += 1
    return permutation
输入:5 3 4 0 2 1 1 0
输出:[4, 8, 6, 2, 5, 1, 3, 7]

3 奇排列与偶排列

  习惯上,按照某个排列中的逆序个数是偶数还是奇数而把 { 1 , 2 , ⋯   , n } \{1,2,\cdots, n\} {1,2,,n}的这个排列 i 1 i 2 ⋯ i n i_1i_2\cdots i_n i1i2in称为偶排列或奇排列。排列的符号按照排列是偶排列还是奇排列而定义为 + 1 , − 1 +1,-1 +1,1。行列式的定义就包含了这一点, n ∗ n n*n nn的矩阵 A = [ a i j ] ( i = 1 , 2 , ⋯   , n ) A=[a_{ij}](i=1,2,\cdots,n) A=[aij](i=1,2,,n),其行列式为
d e t ( A ) = ∑ ε ( i 1 i 2 ⋯ i n ) a 1 i 1 a 2 i 2 ⋯ a n i n det(A)=\sum \varepsilon(i_1i_2\cdots i_n)a_{1i_1}a_{2i_2}\cdots a_{ni_n} det(A)=ε(i1i2in)a1i1a2i2anin
其中 ε ( i 1 i 2 ⋯ i n ) \varepsilon(i_1i_2\cdots i_n) ε(i1i2in) i 1 i 2 ⋯ i n i_1i_2\cdots i_n i1i2in的符号。

4 交换得到自然顺序

  如果排列 i 1 i 2 ⋯ i n i_1i_2\cdots i_n i1i2in有逆序列 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn k = b 1 + b 2 + ⋯ + b n k=b_1+b_2+\cdots +b_n k=b1+b2++bn逆序数,那么可以通过连续 k k k次交换相邻两个数而把 i 1 i 2 ⋯ i n i_1i_2\cdots i_n i1i2in转化成 12 ⋯ n 12\cdots n 12n。步骤为:

  • 首先连续地把 1 1 1与它左边的 b 1 b_1 b1个整数交换;
  • 再连续地把 2 2 2与它左边大于 2 2 2的整数交换;
  • ⋯ \cdots

  例子:交换 361245 361245 361245 123456 123456 123456
361245 316245 136245 132645 123645 123465 123456 361245\\ 316245\\ 136245\\ 132645\\ 123645\\ 123465\\ 123456 361245316245136245132645123645123465123456

5 参考资料

  • 《组合数学》第五版 P57-P60

你可能感兴趣的:(算法和数据结构,数学,算法,组合数学)