5.1--5.1.1 Inversions 反序(上)

本文介绍了排列中的反序对概念,定义为在排列中i<j且ai>aj的元素对。反序表记录了每个元素右边大于它的元素个数,其元素之和等于反序对数量。排列的反序数可通过反序表唯一确定,且与排列的逆具有相同的反序数。通过对排列进行特定操作,可以证明反序数增减的性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从5.1的标题COMBINATORIAL PROPERTIES OF PERMUTATIONS来看,这一节应该会涉及比较多的组合数学的东西,这一节也属于比较有意思的一节。


反序和反序对

Inversions 反序,这是什么概念呢?对于排序,当然,排列是重要的研究对象,那么反序应该也是针对排列而言的。

Let (ai a2 ... an) be a permutation(排列) of the set {1,2,..., n}. If i < j and ai > aj, 
the pair (ai,aj) is called an inversion(反序对) of the permutation; 

从该定义来看,反序对就是违背了排序的一对元素,唯一没有反序对的排列就(1,2,...,n)。

事实上,在学线性代数的时候,求n*n行列式的时候,我们曾经用过反序的概念,G. Cramer 在1950提出的公式


上面的求和是对(1,2,...,n)的所有排列求和,而inv(a1a2...an)是该排列的反序对个数,我们简单叫做反序数。



反序表和反序数

The inversion table(反序表) b1b2 ... bn of the permutation a1 a2 ... an is obtained by 
letting bj be the number of elements to the left of j that are greater than j. (bj等于大于j且在j右边的元素个数的总和)
In other words, bj is the number of inversions whose second component is j. 
例子:591826473 的反序表为 2 3 6 4 0 2 2 10,
since 5 and 9 are to the left of 1; 5, 9, 8 are to the left of 2; etc. 

当然从这个定义看出反序表主要是对自然数的排列进行定义的。这也可以扩展,但是讨论问题而言就变得简单了。大多数也需要对一个排列的序号进行研究。

那么反序表的所有元素加起来就是排列的反序数了。而且反序表满足0 <= b1 <= n - 1, 0<=b2<=n-2, ..., 0 <= b(n-i) <= 1, bn = 0. 


反序表和其排列有着一一对应的关系,从排列到反序表,我们已经知道是唯一的,那么如何从反序表到排列呢,只要我们先把最大值写下,然后从大到小通过插值的方法就可以完成其排列了,因为bn一定为0,这样就可以唯一确定一个排列,这个性质很重要,对解决问题的转化也很有帮助。

例如计算排列个数时,我们以前常用的是一种策略,选第一个有n种,选第二个有n-1,所以是n的阶乘,那么从数学来看,反序表更直接,因为反序表的每个元素是独立的,算出来也是n的阶乘。

另一个例子,在第一卷的1.2.10中,曾经计算过排列的局部最大值的问题,就是计算有多少个元素大于他们所有的后继的问题。那么这个数就是反序表中所有有反序极大值的元素的个数总和,这个极大值指的是bj=n-j的时候。这是因为,当bj=n-j时,那么说明大于j的数全部在j的左边,那么右边的数一定是小于j的,所以该数也是具有局部最大值的。那么当我们求一个排列的平均局部最大值个数的时候,用反序表可以简单解决这个问题,因为反序表每个元素都是独立的,所以,b1有1/n概率是n-1,类似的,可以得到,该平均值是1/n+1/(n-1)+..+1=Hn。


在原排列中随意交换两个相邻元素,反序数总是加一或者减一,这个也很容易证明,因为只对相邻的那两个元素的反序对有影响,要不就是从反序对变成不是反序对,要不就是从不是反序对变成反序对。对其他元素并没有影响。

文中还给出了阿基米德的均匀多面体的图,加深直观理解,也颇有趣味。越来越兴奋的发现在下面展示。


排列的逆和反序表

排列的逆,在第一卷的时候也提到过,定义为a‘j = k 当 ak = j 。那么
591826473 ___ 123456789 
123456789        359716842 

一个排列的逆和该排列有着相同的反序数。Rotha给出了比较有趣的证明。

We construct an n x n chessboard having a dot in column j of row i whenever ai = j. Then we put × 's in all squares that have dots lying both below (in the same column) and to their right (in the same row). 

下面是591826473的构造图:


文中说容易看出构造图中的× 的个数就是反序数。这里我们可以知道,在黑点左边的格子数就是该元素的比它小的元素的总数。而除去一些在它前面,又比它小,就可以得到在它后面,又比它小的数,这个数就就构成数对反序对,所有的× 的个数也就是反序对的总和,即反序数。

那么我们现在把图转置一下,交换行列,(相当于把图按照图的y轴向面向你的方向转180°,然后把得到的图再按图的中心旋转90°),那么得到的就是该排列的逆的图了。经过转置,图中的× 的个数是不变的,所以就可以得到证明。





### 计算排列逆序数的算法 #### 归并排序基础上计算逆序数的方法 在二路归并排序的过程中可以顺便统计逆序数。当处理两个子数组A和B时,如果来自右半部分(即B)的一个元素被先放入临时数组,则说明此元素与左半部分剩余未处理的所有元素构成逆序对[^1]。 对于给定序列`arr=[2, 6, 3, 4, 5, 1]`,其逆序数可以通过修改后的归并排序来获得: ```python def merge_count_split_inv(arr_left, arr_right): result = [] count = 0 i, j = 0, 0 while i < len(arr_left) and j < len(arr_right): if arr_left[i] <= arr_right[j]: result.append(arr_left[i]) i += 1 else: result.append(arr_right[j]) count += (len(arr_left)-i) j += 1 result.extend(arr_left[i:]) result.extend(arr_right[j:]) return result, count def sort_and_count_inversions(array): length = len(array) if length <= 1 or not array: return array, 0 mid_point = length // 2 sorted_first_half, first_half_invs = sort_and_count_inversions(array[:mid_point]) sorted_second_half, second_half_invs = sort_and_count_inversions(array[mid_point:]) merged_array, split_invs = merge_count_split_inv(sorted_first_half, sorted_second_half) total_inversions = first_half_invs + second_half_invs + split_invs return merged_array, total_inversions if __name__ == "__main__": test_arr = [2, 6, 3, 4, 5, 1] _, inversions_num = sort_and_count_inversions(test_arr) print(f"The number of inversions is {inversions_num}.") ``` 上述代码实现了基于归并排序思想的逆序数计数函数。通过递归地分割输入列表直到单个元素,之后逐步合并这些片段的同时累加各层产生的逆序数量,最终得到整个列表中的总逆序数目[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值