我刚刚阅读this other question about the complexity of next_permutation,虽然我对响应(O(n))感到满意,但似乎算法可能会有一个很好的摊销分析,显示出较低的复杂性。有谁知道这样的分析?
答案 0 :(得分:16)
所以看起来我将回答我自己的问题是肯定的 - 是,next_permutation
在O(1)摊销时间内运行。
在我进行正式证明之前,我们将快速回顾一下算法的工作原理。首先,它从范围的末尾向开始向后扫描,识别在最后一个元素结束的范围中最长的连续减少子序列。例如,在0 3 4 2 1
中,算法会将4 2 1
标识为此子序列。接下来,它查看此子序列之前的元素(在上面的示例中为3),然后查找子序列中的最小元素(在上面的示例中为4)。然后交换这两个元素的位置,然后反转识别的序列。因此,如果我们从0 3 4 2 1
开始,我们会将3和4交换为0 4 3 2 1
,然后将最后三个元素反转为0 4 1 2 3
。
为了表明该算法在摊销的O(1)中运行,我们将使用潜在的方法。将Φ定义为序列末尾最长连续递减子序列长度的三倍。在此分析中,我们假设所有元素都是不同的。鉴于此,让我们考虑一下这个算法的运行时间。假设我们从序列的末尾向后扫描并发现最后的m个元素是递减序列的一部分。这需要m + 1次比较。接下来,我们发现该序列的元素中哪一个比该序列之前的元素大一个。这采用与最小情况时间成比例的时间与减少序列的长度成比例,使用线性扫描进行另外的m次比较。例如,交换元素需要花费1个学分的时间,然后反转序列则需要最多m个操作。因此,该步骤的实际运行时间大约为3m + 1.但是,我们必须考虑潜在的变化。在我们反转这个长度为m的序列之后,我们最终将该范围末尾的最长递减序列的长度减少为长度1,因为在末尾反转递减序列会使该范围的最后一个元素按升序排序。这意味着我们的潜力从Φ= 3m变为Φ'= 3 * 1 = 3.因此,潜在的净下降为3 - 3m,因此我们的净摊销时间为3m + 1 +(3 - 3m)= 4 = O(1)。
在前面的分析中,我做了一个简化的假设,即所有值都是唯一的。据我所知,为了使这个证明有效,这个假设是必要的。我将考虑这一点,看看证据是否可以修改为在元素可以包含重复项的情况下工作,并且一旦我完成了细节,我将发布一个编辑到这个答案。
答案 1 :(得分:14)
我不确定std :: next_permutation的确切实现,但是如果它与在wiki中所描述的Narayana Pandita算法相同:http://en.wikipedia.org/wiki/Permutation#Systematic_generation_of_all_permutations,
假设元素是不同的,看起来像是O(1)摊销! (当然,下面可能有错误)
让我们计算完成互换的总数。
我们得到了递归关系
T(n + 1)=(n + 1)T(n)+Θ(n 2 )
(n + 1)T(n)来自固定第一个元素并对剩余的n进行交换。
Θ(n 2 )来自改变第一个元素。在我们改变第一个元素的时刻,我们做Θ(n)交换。这样做n次,得到Θ(n 2 )。
现在让X(n) = T(n)/n!
然后我们得到
X(n + 1)= X(n)+Θ(n 2 )/(n + 1)!
即存在一些常数C
X(n + 1)< = X(n)+ Cn 2 /(n + 1)!
写下这样的不等式给我们
X(n + 1) - X(n)< = Cn 2 /(n + 1)!
X(n)-X(n-1)< = C(n-1) 2 /(n)!
X(n-1)-X(n-2)< = C(n-2) 2 /(n-1)!
...
X(2) - X(1)< = C1 2 /(1 + 1)!
添加这些内容会给我们X(n+1) - X(1) <= C(\sum j = 1 to n (j^2)/(j+1)!)
。
由于无限系列\sum j = 1 to infinity j^2/(j+1)!
收敛于C',比如说,我们得到X(n+1) - X(1) <= CC'
请记住,X(n)计算所需的交换平均数(T(n)/ n!)
因此,平均交换次数为O(1)。
由于找到要交换的元素与交换数量呈线性关系,即使您考虑其他操作,也会进行O(1)摊销。
答案 2 :(得分:0)
此处n
代表容器中元素的数量,而不是可能排列的总数。该算法必须在每次调用时迭代所有元素的顺序;它需要一对双向迭代器,这意味着要获得一个元素,算法必须首先访问它之前的那个(除非它是第一个或最后一个元素)。双向迭代器允许向后迭代,因此算法可以(实际上必须)执行与元素一样多的交换。我相信标准可以为前向迭代器提供重载,它将以n
交换而不是一半n
交换为代价来支持数据迭代器。但是,唉,它没有。
当然,对于n
可能的排列,算法在O(1)中运行。