大O记法

大O记法(Big-O Notation)

我们已经见过了很多函数,在比较两个函数时,我们可能会想知道,随着输入值的增长或者减少,两个函数的输出值增长或减少的速度究竟谁快谁慢,哪一个函数最终会远远甩开另一个。

通过绘制函数图,可以获得一些直观的感受:

    x = range(1,7)

    factorial = [np.math.factorial(i) for i in x]
    exponential = [np.e**i for i in x]
    polynomial = [i**3 for i in x]
    logarithmic = [np.log(i) for i in x]

    plt.plot(x,factorial,'black',\
             x,exponential, 'blue',\
             x,polynomial, 'green',\
              x,logarithmic, 'red')

06-01 growth

根据上图,当时:,要想证明的话,可以取极限,例如:(用洛必达法则计算),表明时,虽然分子分母都在趋向无限大,但是分子仍然远远凌驾于分母之上,决定了整个表达式的表现。
类似地我们也可以这样看:,表明分母将会远远凌驾于分子之上。

    from sympy.abc import x
    # sympy中无限infty用oo表示
    print ((sympy.E**x)/(x**3)).limit(x,sympy.oo)
    # result is: oo
    print (sympy.ln(x)/x**3).limit(x,sympy.oo)
    # result is 0

为了描述这种随着输入时,函数的表现,我们如下定义大O记法:
若我们称函数时是,则需要找到一个常数,对于所有足够小的均有
若我们称函数时是,则需要找到一个常数,对于所有足够大的均有

大O记法之所以得此名称,是因为函数的增长速率很多时候被称为函数的阶(Order)

下面举一例:当时,

先来个直观感受:

    xvals = np.linspace(0,100,1000)
    f = x*sympy.sqrt(1+x**2)
    g = 2*x**2
    y1 = [f.evalf(subs={x:xval}) for xval in xvals]
    y2 = [g.evalf(subs={x:xval}) for xval in xvals]
    plt.plot(xvals[:100],y1[:100],'r',xvals[:100],y2[:100],'b')
    plt.plot(xvals,y1,'r',xvals,y2,'b')

06-02 growth

06-03 growth

Sympy可以帮助我们分析一个函数的阶:

    print sympy.O(f, (x, sympy.oo))
    # result is : O(x**2, (x, oo))

计算机学科中使用大O记法,通常是分析当输入数据{% math %}\rightarrow \infty{% endmath %}时程序在时间或空间方面的表现。

然而,从上面的介绍,我们知道这个位置可以是{% math %}0{% endmath %},甚至可以是任何有意义的位置。

    print sympy.order(f, (x, 0))
    # result is : O(x)

误差分析(Error Analysis)

细心的读者可能曾注意到在泰勒级数一节,我们利用Sympy取函数泰勒级数的前几项时,代码是这样的:

    exp = e**x
    sum15 = exp.series(x,0,15).removeO()

其中removeO()的作用是让sympy忽略掉级数展开后的大O表示项,不然的话结果类似如下:

    print exp.series(x, 0, 3)
    # result is : 1 + 1.0*x + 0.5*x**2 + O(x**3)

这表示从泰勒级数的第4项起,剩余所有项在{% math %}x\rightarrow 0{% endmath %}时是{% math %}O(x^3){% endmath %}的。
这表明,当{% math %}x\rightarrow 0{% endmath %}时,用{% math %}1+x+0.5x^2{% endmath %}来近似{% math %}e^x{% endmath %},我们得到的误差的上限将是{% math %}Cx^3{% endmath %},其中{% math %}C{% endmath %}是一个常数。
也就是说大O记法能用来描述我们使用多项式近似时的误差。

另外,大O记法也可以直接参与计算中去,例如我们要计算{% math %}cos(x^2)\sqrt{(x)}{% endmath %}在{% math %}x\rightarrow 0{% endmath %}时阶{% math %}O(x^5){% endmath %}以内的多项式近似,可以这样:

{% math %}cos(x^2)\sqrt{(x)}=(1-\frac{1}{2}x^4+O(x^6))x^{\frac{1}{2}}{% endmath %}
{% math %}\qquad = x^{\frac{1}{2}}- \frac{1}{2}x^{\frac{9}{2}} + O(x^{\frac{13}{2}}){% endmath %}

    print (sympy.cos(x**2)*sympy.sqrt(x)).series(x,0,5)
    # result is : sqrt(x) - x**(9/2)/2 + O(x**5)

results matching ""

    No results matching ""