如何理解算法中的渐进符号

如何理解算法中的渐进符号

我们分析一个算法的时候,常常需要用到数学去描述其性能。最常用的的是ø。比如在一段程序中有

for(i = 0,i < n ; i ++)

   for(j = 0; j < n ; j++);

			for (k = 0; k < n ; k ++);

我们会说其最坏情况是n + n²,这时候,实际上我们没有考虑机器因素,我们把每一条指令的执行时间都当做单位1来看,如果我们考虑到机器因素,比如在A机器上,每条指令执行的速度是c1,那么他的最坏情况是( n + n²) ·c1,而在机器B上,速度变成了c2,那么最坏情况变成了( n + n²) ·c2,如果c1是10,而c2是20,那么我们就说:算法在机器A上面的性能比机器B上差?显然不是这样的,机器变差了但算法本身没有变差,所以说在不同的机器上跑同样的算法,最坏情况时间总是不一样的,我们得想方设法去除机器性能的影响,而只是去单单分析算法本身的优劣,以便于在具体量化算法性能的时候又能去除机器因素的影响,因为我们讲算法分析而不是计算机系统分析。于是有人发明了Θ符号。对于这个符号我们来看几个例子,再看定义

比如说 :

ax ² + bx + c = f(x);那么有Θ(x²)= f(x).

ax³ + bx² + cx + d = f(x);那么有Θ(x³) = f(x).

也就是说它相当于取出f(x)的最高次项然后去掉其常数因子,然后放入Θ()中,而放入Θ()中又表示了什么呢?为了弄清楚这个问题,我们来看其定义:

注:在集合表示中“:”符号表示“满足于 …条件”

Θ(g(n)) = {f(n) : 存在某个常数c1,c2,和n0,使得当n ≥ n0时,有 0 ≤ c1·g(n) ≤ f(n) ≤ c2·g(n) }

从后面半句开始,“存在 n > n0,0 ≤ c1·g(n)”这里保证了g(n)是一个增函数。接着,有 c1·g(n) ≤ f(n) ≤ c2·g(n)。在脑海里可以想象出来函数图像 [ c1·g(n), c2·g(n) ]这个区间实际上是摇摆的曲线,如果把g(n)看做是y = x这样的正比例函数,就是一个扇形是吧?一根曲线摇摆着,扫出一个扇形来。而f(n)就是这组成扇形的众多曲线中的一根!这里也就是说,无论g(n)是什么,总能有一个常数因子使得c1·f(n) =g(n)。再返回来原来的题目中去理解:

a x² + bx + c = f(x);那么有Θ(x²)= f(x),这里Θ(x²)表示,在x趋于无穷大这个过程中,有一个常数c使得cn²= ax² + bx +c = f(x),也就是:同!阶!数!(见高数上);这样,我们用Θ()来描述先程序的性能n² + n变成了Θ(n²),这里表明,在n趋于无穷大的过程中,总有一个常数因子使得cn² = n² + n,这个常数因子c在上文中说到,由机器和其它非算法因素来决定。也就是说,我们用Θ(n²)来表示一个算法的性能的时候,我们就完全忽略了机器,只研究算法本身,它好像是说:无论你在什么机器上跑这个算法,只要输入规模足够大,你的性能都是n²的整数倍,至于是多少倍,由机器决定的,反正在同样的机器上,这个倍数是相等了。

你看,Θ(x²)表示的不就是机器无关的,纯算法的性能了吗?原来的问题解决了。不过Θ(x²)符号还使得我们只关注最高次幂,不去关注低次项,因为在n→∞的时候,低次项是没有意义的,因为此时只要n变化一点点,就足以抵消低次项的变化(其实还是同阶数)。所以我们知道,对于一个时间复杂度为Θ(x²)的算法,它的时间性能是优于Θ(x³)的算法的,这里我们没有考虑常数因子,即使Θ(x³)的常数因子是1而Θ(x²)的常数因子是10000000000000也没有关系,因为无论如何,总有一个值n0使得n越过这个点之后Θ(x²)的算法总能打败Θ(x³)的算法,也就是说使用了Θ这类的符号(渐进符号)去比较算法时间性能的时候,是与机器无关的,即使你在超级计算机上跑Θ(x³)算法,只要输入规模和时间足够,跑Θ(x²)算法的老式386机子总能打败超级计算机。类似于Θ符号我们引出了O符号(读作大o)。

O(g(n)) = {f(n) : 存在某个常数c,和n0,使得当n ≥ n0 时,有 0 ≤ f(n) ≤ c·g(n) }

这里我们就只关心最坏情况,我们在谈论一个算法的最坏情况时间复杂度的时候总是喜欢使用O符号,我通过前面的例子来说明这个O符号的作用。

for(i = 0,i < n ; i ++)

   for(j = 0; j < n ; j++);

			for (k = 0;k < n ; k ++);

这时候我们把机器执行的速度看做单位1,于是执行的时间是n² + n,我们会说,上面这个算法最坏情况时间复杂度是O(n²),还是同样的目的,我们不去考虑机器本身的影响,因为机器执行的速度如果是c,那么时间也就是变成了(n² + n) · c,也只是常数因子的问题而已。在输入规模足够大的情况下,我们用O(n²)来忽略机器的影响,只考虑算法本身的最坏情况,告诉别人: 嘿!无论你在什么机器上跑,你的最坏情况都不能比n² 这个值的x倍更坏啦。至于X倍是多少倍,反正我们只是研究算法的,只有做机器的人才知道X是多少倍,而我们比较算法的时候,不会在不同的机器上去测同样的算法然后拿来比较,往往在同样的机器上去考察一个算法,这时常数因子X是相等的,关心它也是多余的了,这就好比我们高考的时候每个人的分数都乘以10分然后再来报考,结果还不是一样?因为考大学是根据排名来录取的,所以对于算法也是一样的,我们只关注:“那个算法才是最好的?我们应该选择哪个算法?”这种比较性的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在JavaScript,diff算法是用于比较两个对象或数据结构之间的差异,并确定如何最高效地将一个对象转换为另一个对象的方法。它通常用于前端开发的UI渲染和虚拟DOM更新。 diff算法的基本思想是将数据结构分解成组件,并逐一比较它们的差异。这个过程通常包括以下几个步骤: 1. 比较顶层组件:首先,算法会比较两个顶层组件(例如两个React组件)的类型和属性。如果它们不同,算法会认为整个组件需要重新创建。 2. 递归比较子组件:如果两个顶层组件相同,算法会递归地比较它们的子组件。这个过程会继续下去,直到所有的子组件都被比较完毕。 3. 标记变化:在比较过程算法会记录下所有的差异,例如新增、删除或修改的组件。这些差异通常以一种称为"补丁"的形式存储起来,用于后续的更新操作。 4. 应用变化:最后,算法会根据记录的差异来应用必要的改变。这可以是通过添加、删除或更新DOM元素来实现。 需要注意的是,diff算法并不是完美的,它有些局限性和性能开销。一些优化技术,如虚拟DOM和批量更新,可以用来提高diff算法的效率和减少不必要的渲染操作。此外,不同的前端框架和库可能采用不同的diff算法实现,因此具体的实现细节可能会有所不同。 总而言之,diff算法是JavaScript用于比较和更新对象或数据结构之间差异的一种常用技术。它在前端开发起着非常重要的作用,帮助我们高效地更新UI,并提升用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值