假设我有一个存在总排序的元素数组。冒泡排序距离是在我使用冒泡排序时对数组进行排序所需的交换次数。什么是有效的(可能涉及动态编程)方法来计算此数组的可能排列数,这些排列的气泡排序距离小于或等于某个预先指定的数字?
如果它简化了问题,你可以假设数组的所有元素都是唯一的(没有联系)。
答案 0 :(得分:2)
好的,这是一个解决方案。让我们假设数组的所有元素都是不同的,而且在不失一般性的情况下,我们可以假设它们是{1,...,n}。 (我们总是可以重新标记元素,以便这种情况,并且不会受到任何影响。)
首先,我们可以观察到冒泡排序执行的掉期数量是排列中的倒置次数 a [1..n]:对的数量(i,j)我< j但a [i]> a [j]。 (这不难证明。)
因此,我们希望{1,...,n}的排列数最多为k次倒置。设c(n,k)表示这个数字。 {1,... n}的任何排列都可以被认为是对{1,...,n-1}进行排列并在某处插入{n}。如果将其插入位置i,则会创建n-i个新的反转。所以旧的排列必须最多具有k-(n-i)个反转。这给出了:
c(n,k) = sum_{i s.t. n-i≤k} c(n-1, k-(n-i))
= sum_{i=max(1,n-k) to n} c(n-1, k-n+i)
基本情况:
c(1,0) = 1 (or better, c(0,0)=1)
(注意k至多为n *(n-1)/ 2
更新:上面需要O(n ^ 2k) - 所以达到O(n ^ 4) - 计算c(n,k)的时间,因为每个nk c(n, k)在给定早期计算时需要O(n)时间来计算。我们可以通过使递归更短来改进因子n,从而可以在给定前一个的O(1)时间内计算每个c(n,k)。写j为k-n + i,以便
c(n,k) = sum_{j=max(k-n+1,0) to k} c(n-1, j)
请注意,c(n,k)和c(n,k-1)的大部分和是相同的。具体地,
When k≤n-1, c(n,k) = c(n,k-1) + c(n-1,k)
When k≥n, c(n,k) = c(n,k-1) + c(n-1,k) - c(n-1,k-n)
更新的程序:(我写了一个懒惰的备忘版本;你可以通过动态编程的常用方式自下而上使它更有效率。)
ct = {(0,0): 1}
def c(n,k):
if k<0: return 0
k = min(k, n*(n-1)/2) #Or we could directly return n! if k>=n*(n-1)/2
if (n,k) in ct: return ct[(n,k)]
ct[(n,k)] = c(n,k-1) + c(n-1,k) - c(n-1,k-n)
return ct[(n,k)]
if __name__ == "__main__":
n = input("Size of array: ")
k = input("Bubble-sort distance at most: ")
print c(n,k)
答案 1 :(得分:1)
查看Wagner-Fisher algorithm的编辑距离。您可能正朝着相同的方向前进:构建一个最小交换表,在您的问题中应该是n×n,使用从左上角到右下角构建表的不变关系。