计算给定递归函数的精确运行时间(时间复杂度)
Posted
技术标签:
【中文标题】计算给定递归函数的精确运行时间(时间复杂度)【英文标题】:Calculate exact runtime (time complexity) given recursive functions 【发布时间】:2022-01-08 03:26:46 【问题描述】:考虑这个计算double x
的幂的代码:
public static double F1 (double x, int k)
if (k==1) return x; // O(1)
return x * F1(x, k-1); // O(k)
我的结论是
if (k==1) return x;
中的操作数:2 个操作,if
-statement 和 return
-statement。因此,T(1) = 2
return x * F1(x, k-1);
中的操作数:4 次操作,return
-statement = 1,*
-operator = 1,F1(x, k-1);
= 2。所以等式的第一部分 = 4
我们在x * F1(x, k-1),
中有一个递归调用,所以x = 1
。
我们在每个递归调用中将问题减少 1,因此 y = k-1
。所以方程的第二部分 = T(k-1)
将所有这些放在一起,我们得到:
4 + T(k-1), T(1) = 2
但是我如何从这里开始找到确切的运行时?
我试图查看this 问题的解释,但它侧重于如何计算 Big-O 表示法,而不是精确时间复杂度。如何继续找到确切的时间复杂度?
这里的答案应该是:
Exact: 4k-2
Tilde: 4k
Big-O: O(k)
但我不知道他们做了什么。
【问题讨论】:
我不知道你所说的“波浪号”是什么意思。你能提供解释的引文吗? (请注意,不管它是什么意思,它与[tilde]
逻辑标签的主题无关......所以我把它删除了。)
【参考方案1】:
对于您执行的第一个k-1
步骤:
-
比较
k==1
减法k-1
产品x * ...
return
指令
在您执行的最后一步中:
-
比较
k==1
return
指令
所以你有4*(k-1)+2 = 4k-2
的总体说明。
编辑:正如@rzwitserloot 正确指出的那样,您搜索的数量不是很重要,但这取决于代码的编译和执行方式。上面我只是想弄清楚你的老师所说的“精确时间复杂度”是什么意思。
【讨论】:
在字节码级别,这些数学都没有结果。即使您尝试计算字节码步数,每条字节码指令的执行时间也会非常变化。整个分析完全没有意义。【参考方案2】:但是我如何从这里开始找到确切的运行时?
您将迄今为止所做的所有事情都扔进垃圾箱,然后启动 JMH,稍后再查看更多信息。
完全不可能根据这样的学术分析来确定确切的运行时间。确切的运行时间取决于您的音乐播放器中正在播放的歌曲、您的操作系统是否忙于进行磁盘清理、向网络时间服务器发送 ping、哪些页面碰巧在片上缓存上、您的代码的 CPU 核心最终被运行,以及月相。
让我尽可能清楚地说:像4k - 2
这样的东西完全不相关且被误导 - 这不是计算机的工作方式。您不能说具有“精确运行时间”4k - 2
的算法会比6k + 2
算法更快。它同样可能更慢:它的预测能力为零。这是一个完全没有意义的“计算”。这不代表任何意思。 big-O 表示法存在是有原因的:这确实意味着无论硬件是否变幻莫测:给定 2 种算法,其中一种算法比另一种具有“更好”的 big-O 表示法,那么存在一些输入大小使得更好的算法将是更快,不管硬件问题。这可能是一个非常大的数字,而 big-O 不会告诉您发生这种情况的数字是多少。
big-O 表示法的意义在于,它以数学上的确定性规定了如果您更改算法输入的大小,最终会发生什么,非常宽泛。这就是为什么在显示大 O 表示法时要删除所有常量和除最大因子之外的所有内容。
画一张图;在 X 轴上,有“输入大小”,即O(k)
中的“k”。在 Y 轴上,有执行时间(或者,如果您愿意,最大内存负载)。然后,弥补一些输入大小并运行您的算法几次。平均结果,并在该图上放置一个点。例如,如果您在 k=5 的输入上运行算法,平均需要 27 毫秒,请在 x=5, y=27 上放置一个点。
继续。很多点。最终这些点形成了一个图形。该图将在 x=0 点附近遍布整个地方。就好像一个喜欢随机性的醉汉正在向棋盘扔飞镖一样。
但是,最终(当“最终”出现时无法确定,因为这又取决于很多操作系统的东西,不要费心去预测这些东西),它会开始看起来像一个可识别的形状。我们用简单的公式定义这些形状。例如,如果它最终(足够靠右)合并成看起来像绘制y=x^2
所得到的东西,那么我们称之为O(x^2)
。
现在,y=5x^2
看起来与 y=x^2
完全一样。就此而言,y=158*x^2 + 25000x + 2134931239
,如果您在该曲线上向右看足够远,看起来与y=x^2
完全一样。因此,为什么O(158x^2+20x)
完全没有抓住重点,因此是不正确的。 O 的意义只是告诉你它“向右足够远”会是什么样子。
这给我们留下了正好 2 个有用的性能指标:
O(k)
符号。您在此处正确确定:此算法具有 O(k)
运行时。
时间报告。 没有意义试图通过查看代码来解决这个问题,您需要运行代码。反复地,周围有各种保护措施,以确保热点优化不会完全消除您的代码,重新运行很多次以获得良好的平均值,并确保我们通过了 JVM 的 JIT 步骤。您使用 JMH 来执行此操作,并注意 JMH 的结果自然取决于您运行它的硬件,这是因为程序的性能特征可能因硬件而异。
【讨论】:
以上是关于计算给定递归函数的精确运行时间(时间复杂度)的主要内容,如果未能解决你的问题,请参考以下文章