一种遍历数组内所有可能的索引序列的算法。
单个循环的时间复杂度是线性的,两个嵌套循环的时间是二次O(n ^ 2)。但是,如果嵌套另一个循环并遍历这两个索引之间分隔的所有索引怎么办?时间复杂度是否上升到三次O(n ^ 3)?当N变得很大时,似乎没有足够的迭代来考虑复杂度三次方,但似乎是二次O(n ^ 2)
这里是考虑N =数组长度的算法
for(int i=0; i < N; i++)
{
for(int j=i; j < N; j++)
{
for(int start=i; start <= j; start++)
{
//statement
}
}
}
这是N = 7(一直持续到i = 7)时迭代的简单视图:
以此类推。
我们应该将时间复杂度视为二次,三次还是不同的大小复杂度?
答案 0 :(得分:7)
基本
for (int i = 0; i < N; i++) {
for (int j = i; j < N; j++) {
// something
}
}
我们执行something
n * (n+1) / 2
次=> O(n^2)
。关于原因:这是
的简化形式
sum (sum 1 from y=x to n) from x=1 to n
。
对于您的新案例,我们有一个类似的公式:
sum (sum (sum 1 from z=x to y) from y=x to n) from x=1 to n
。结果为n * (n + 1) * (n + 2) / 6
=> O(n^3)
=>时间复杂度为三次。
在两个公式中的1
是您输入something
成本的地方。特别是在您进一步扩展公式的地方。
请注意,所有索引可能相距一个,我没有特别注意<
与<=
等,
答案 1 :(得分:2)
简短回答O(choose(N+k, N))
与O(choose(N+k, k))
相同。
这是如何到达那里的长答案。
您的基本问题版本正确。使用k
嵌套循环,随着O(N^k)
趋于无穷大,您的复杂度将为N
。但是,随着k
和N
的变化,行为会更加复杂。
让我们考虑相反的极端。假设N
是固定的,并且k
变化。
如果N
为0,则您的时间是常数,因为最外面的循环在第一次迭代时失败。如果N = 1
则您的时间为O(k)
,因为您经历了所有嵌套层次只有一种选择,每次都只有一种选择。如果N = 2
发生了一些更有趣的事情,您将一遍又一遍地进行嵌套,这需要时间O(k^N)
。通常,在固定N
的情况下,时间为O(k^N)
,其中k
的一个因素是遍历嵌套所花费的时间,而O(k^(N-1))
则是由于顺序前进。这是意外的对称!
现在,如果k
和N
都很大怎么办?那的时间复杂度是多少?好吧,这可以给您直觉。
我们能否描述到达最内层循环的所有时间?是!
考虑k+N-1
个插槽,其中k
个被“进入了另一个循环” ,其中N-1
个被了“使索引增加了1”。 我声明以下内容:
1 < N
实际上需要我们在独特工作中获得的更多收益,那么现在,这看起来像是一团糟,但是有一个技巧可以使它出乎意料地简化。
诀窍是这个。假设我们采用了其中一种模式,并在最后的“再输入一个循环” 条目的最后一段中的某个位置插入了一个额外的“将索引提高1” 。有多少种方法可以做到这一点?答案是,我们可以将最后一个条目插入到最后一段的任意两个位置之间,包括开始和结尾,并且有比该条目更多的方法。换句话说,完成此迭代的方式数量与该迭代要完成的独特工作数量相匹配!
的意思是总功与O(choose(N+k, N))
也是O(choose(N+k, k))
成正比。
值得一提的是,从正态近似到二项式,如果为N = k
,则证明它是O(2^(N+k)/sqrt(N+k))
,其增长速度确实快于多项式。如果您需要更一般或更精确的近似值,可以对choose(N+k, N) = (N+k)! / ( N! k! )
中的阶乘使用Stirling's approximation。