大三生活第二天

  呼呼,这么晚才来更新。

  今天上了很多课,感觉也没浪费到时间,可是就已经这么晚了,可能是没有好好利用上课时间吧,明天开始要好好利用上课时间来做一些学习和计划。今天虽然有很多计划还没有完成。不过博客是一定要更的(一定要坚持下去!└(^o^)┘,而且好像写出来的文章,排版样子什么的不太好,我决定明天早上修饰一下自己的博客,比如安装客户端,安装插件,添加css什么的,尽量弄得美观大方清楚一点。)

  好了,正式进入正题。

--------------------------华丽分割线--------------------------------

  今天学习了算法分析与设计,主要讲了一些算法思想和知识,现在来总结一下。

  编程的灵魂:

     数据结构+算法=程序。

     今天才正式注意到了这点,以前学了很多算法和数据结构,但是理论方面很凌乱,现在算是正确地认识到了何为程序,何为数据结构和算法的作用。简单来说,算法就是解决问题的方法或者过程,如果把问题看成一个函数,算法可以把输入转化为输出。而数据结构是数据的计算机表示和相应的一组操作。这两大内容组合,就形成了我们熟悉的程序。

  算法:

    由刘汝佳、黄亮著作的《算法艺术与信息学竞赛》可知,基本算法可分为四种,枚举、贪心、递归与分治法、递推。

    今天暂时不对此进行详解,目测将于周五晚对此微博进行更新,详细阐述这四种方法并举些许例子来说明。

  时间复杂度和空间复杂度的计算:

    今天主要讲的是这个。本来我对这方面的知识没有学的很详细,主要是以前使用的数据比较小,自己也没有对程序算法优化很有兴趣,导致自身对算法的时间和空间复杂度理解不是很好。但这个方面的知识真的很重要,可以用来评估你的算法写的是否高效,合理,当初我们的专业老师在上数据结构课时花了很多时间来讲复杂度这门知识,可惜我没有认真听,而很多算法书都把关于时间和空间复杂度的计算列入基本纲要,尤见其重要性。现在就来重新认识一下,也算是比较基础的认识吧,以后会逐渐深究,争取写出更优化的程序。

  时间复杂度:

    同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。因此可以这样理解,计算机科学中,算法的时间复杂度作为一个函数,能够定量描述该算法的运行时间。

    一般来说,能够准确地判断两个算法的性能好坏的方法就是各写一个程序来观察它们的运行情况,但是这样异常耗时并且程序并不是算法的直接对应结果,可能会无法避免一些和算法无关,只由程序代码产生的问题。因此,我们可以运用时间复杂度,从理论上来估计和比较算法的运行效率。下面会详细解释时间复杂度的由来。

    在具体了解时间复杂度之前,需要了解一个概念--基本操作。抽象来说,基本操作,就是一个运行时间不依赖于操作数的操作。简单来说,既然我们是利用时间复杂度来估计算法的运行效率的,那我们总得有个估量的依据吧,比如说哪些操作占时最大,哪些操作占时较小可以忽略,我们就把占主要运行时间的操作看作为基本操作,并作为时间复杂度估计的依据。为什么说基本操作是不依赖于操作数的操作?有点混乱吧。这里借鉴了《算法艺术与信息学竞赛》的一个小例子来说明一下。比如两个不大于100的正整数相加,那么运行时间肯定常数,基本操作就是加法操作,操作数就是那两个几乎不影响运行时间的不大于100的正整数,也就是加法操作并不依赖于操作数。但是,如果是两个大小无限制(位数特别大)的两个正整数相加呢?这时候,加法操作就不能看成是基本操作了,因为两个极大数相加,我们计算时是需要将两个极大整数对应的位数相加,因此,这里的加法操作就不能看成是基本操作了,因为操作数(即极大整数)对其操作的影响很大。那么在这种情况下,什么才是基本操作?可能你已经想到了,将对应位数相加的操作,就可以看成是一个基本操作,因为每位位数是最大值为9的操作数,其运行时间不依赖于它的操作数。学习基础算法的时候可能不会发现这个概念的用处,但越往深研究越会发现,在不同时候,可以把不同的操作看成基本操作,而基本操作的选择就反映了你对问题的主要矛盾的认识。这个是非常重要的。

    现在理解一下时间频度,参考百度百科,一个算法花费的时间与算法中语句(基本操作)的执行次数成正比例,因此,一个算法中的语句执行次数可称为语句频度或时间频度。记为T(n)。

    现在我们将输入n称为问题的规模(可理解为问题运行时间的大小),当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。

     一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

    时间频度不同,但时间复杂度可能相同。如:T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。有点绕,有点理论化是吧?回去看高数书应该又可以理解了>_<|||。或者你可以简单得理解为时间复杂度就是时间频度的公式化,简单化,比如n2+3n+4可以简化为n2,因为在n无穷大时,他们趋近于相等。那为什么可以这样简化呢?为什么称之为渐进时间复杂度呢?在这里,我们需要考虑一个问题,因为算法的运行时间可能和计算机的运行速度有关(比如某些操作运行快,某些操作运行慢),所以我们只关心在问题规模扩大时时空开销的增长情况。是的,增长情况,这是特别需要注意的。

    我们再来看两个例子:

    

1 int num = 1;;
2 for (i = 1; i <=n; i++) {
3   num *= i;
4 }
int num = 1;;
for (i = 1; i <= n; i++) {
  for (j = 1; j <= n; j++) {
    num =  num + i +j;  
   }
}

    这两个例子中,变量i,j的自增并不影响算法的运行时间,所以我们把加法和乘法操作看成是基本操作。那么,第一份代码执行了n个操作,第二份代码执行了n2个操作。那么哪个运行速度更快呢?答案是不知道。是的,因为不知道加法操作和乘法操作哪个快(联系前面所说,算法运行时间依赖于计算机的运行速度)。现在假设加法速度是乘法的10倍,那现在谁更快呢?还不一定,假设加法操作时间为t,乘法操作时间为10t,第一份代码运行时间为n*10t,第二份代码运行时间为n2*t,当n小于10时,第二份代码运行较快,当n大于10时,第一代码运行较快。

    但是,这样依赖于n的规模大小(或者依赖于计算机运行速度)来估量算法运行时间,那答案就不唯一不确定了,这样的研究方法很不严谨。所以,在算法分析上来看,我们观察增长情况来估量算法运行时间,会更优。比如,当n规模扩大10倍时,第一份代码运行时间只扩大了10倍,但是第二份代码运行时间会扩大100倍,就这样观察的话,第一份代码运行速度更快更优,并且不依赖于计算机的运行。

    所以,进行算法分析的时候,我们只对增长情况做分析。到这里就比较好理解为什么叫做渐进时间复杂度了吧?就是n趋向于无限大的时候,得出与时间频度T(n)的增长情况趋近的时间复杂度O(f(n)),用来更简单直接得表示算法的运行时间。因为n越大,T(n)和时间复杂度越接近,所以就叫做渐进时间复杂度!(哎哟,解释得好辛苦,不知道是不是越解释越乱了=@~@=)

  ------------------- 停笔先 -----------

  好吧!眨眼之间发现写了好久了,空间复杂度还没写呢 QAQ。但太晚了,周五再更吧。

  努力,晚安。

原文地址:https://www.cnblogs.com/yuanxiaohui-blog/p/4830946.html