根据程序的运行方式,编写出表现出四种常见的O大 时间复杂度的程序(或函数)。无论采用哪种形式,都需要一个正整数N,您可以假设它小于2 31。
当程序以其原始形式运行时,它应该具有恒定的复杂性。也就是说,复杂度应为Θ(1)或等效地为Θ(1 ^ N)。
当程序反向运行时,它应该具有线性复杂度。也就是说,复杂度应为Θ(N)或等效地为Θ(N ^ 1)。
(这是有道理的,因为N^1
被1^N
逆转。)当程序被加倍,即,级联到本身,并运行它应具有指数复杂性,特别是2 Ñ。也就是说,复杂度应为Θ(2 ^ N)。
(这是有道理的,因为2
在2^N
是双1
中1^N
)。当程序被加倍执行并反转并运行时,它应具有多项式复杂度,尤其是N 2。也就是说,复杂度应为Θ(N ^ 2)。
(这是有道理的,因为N^2
被2^N
逆转。)
这四种情况是您唯一需要处理的情况。
请注意,为精确起见,我使用大theta(Θ)表示法而不是大O,因为您的程序的运行时必须在上下两方面受所需复杂度的限制。否则,仅在O(1)中编写一个函数就可以满足所有四个点。了解这里的细微差别不是太重要。主要是,如果您的程序针对某个常数k执行k * f(N)运算,则很有可能为Θ(f(N))。
例
如果原始程序是
ABCDE
那么运行它应该花费恒定的时间。也就是说,无论输入N是1还是2147483647(2 31 -1)或两者之间的任何值,它都应在大致相同的时间内终止。
程序的反向版本
EDCBA
应该以N为单位花费线性时间。也就是说,终止所花费的时间应与N大致成比例。因此N = 1花费的时间最少,N = 2147483647花费的时间最多。
该程序的两倍版本
ABCDEABCDE
以N表示,应该花费2到N的时间。也就是说,终止所花费的时间应该大致与2 N成正比。因此,如果N = 1在大约一秒钟内终止,则N = 60将花费比宇宙年龄更长的时间。(不,您不必测试。)
程序的加倍和反转版本
EDCBAEDCBA
应该花费以N为单位的平方时间。也就是说,终止所花费的时间应与N * N大致成比例。因此,如果N = 1在大约一秒钟内终止,则N = 60将需要一个小时才能终止。
细节
您需要证明或争论您的程序正在以您所说的复杂性运行。提供一些时序数据是一个好主意,但也要尝试解释为什么理论上复杂性是正确的。
如果在实践中您的程序花费的时间不能完全代表其复杂性(甚至是确定性的),那就很好。例如,输入N + 1有时可能比N运行得快。
您在其中运行程序的环境确实很重要。您可以对流行语言如何永远不会有意浪费时间的算法做出基本假设,例如,如果您知道特定版本的Java实现的是冒泡排序而不是更快的排序算法,那么在进行任何排序时都应考虑到这一点。
对于这里的所有复杂性,假定我们正在谈论的是最坏情况,而不是最佳情况或平均情况。
程序的空间复杂度无关紧要,时间的复杂度无关紧要。
程序可能会输出任何内容。它们取正整数N并具有正确的时间复杂度只是重要的。
允许注释和多行程序。(您可以假定
\r\n
相反是\r\n
为了Windows兼容性。)
大提醒
从最快到最慢(上面的O(1), O(N), O(N^2), O(2^N)
顺序1、2、4、3 )。
较慢的术语始终占主导地位,例如O(2^N + N^2 + N) = O(2^N)
。
O(k*f(N)) = O(f(N))
对于常数k。所以O(2) = O(30) = O(1)
和O(2*N) = O(0.1*N) = O(N)
。
记住O(N^2) != O(N^3)
和O(2^N) != O(3^N)
。
计分
这是普通代码高尔夫。以字节为单位的最短原始程序(恒定时间一)获胜。
n = input(); for i in xrange(n): pass
具有指数复杂性,因为它需要执行2 ** k
步骤,其中k = log_2(n)
输入大小在哪里。您应该弄清楚是否是这种情况,因为它会极大地改变需求。