有不少读者给我来信,咨询的是关于数学和算法对学习计算机的意义。这样的话题,在我的专栏中很多文章里都提到过。在拙作《逆袭大学——传给IT学子正能量》中,在这方面写了不少文字,现将其中的9.5节全文摘录在此文中,以供参考。
更多话题,见《逆袭大学——传给IT学子正能量》全书目录。
9.5 用算法和数学奠定专业基础
一个程序设计的初学者,在刚刚开始学习时,会认为编程中语言是最重要的。没有语言,没有掌握好编程语言,写不出程序来。而后又知道熟练运用语言仅仅是学会了一种表达的方式而已,如同一个三岁的小孩,空有伶牙俐齿,说着幼稚的话。小孩要说出成熟的话来,还需要有思想,会思考。学编程也是一样,学会了语言,还应该学会思考,学会给出问题的解决方案。
经典著作《计算机程序设计艺术》的作者,著名计算机科学家高德纳(Donald Ervin Knuth)曾经说过:“软件设计者需要的最基本的构件是算法、语言和数学。”在专业基本功能方面,围绕算法和数学,积累足够的专业基本功,这是计算机类大学生该有的准备。
算法有大用
很多人在开始一个新的学习历程时,总是关心需要有什么基础。上二年级要先上过一年级,要学会走先得要学会爬。这样一种思维容易理解,体现的是一种对系统学习的期望。实际上,无论要进到哪一个领域学习,总是可以找到一个入口,随时开展体验即可。随着体验的加深,逐渐地将需要学习的其他内容拉进来,一种体系就这样逐渐建立。在编程中,有一定的算法基础是占有优势的。但现实的方法是,先从一种编程语言开始学习,通过学会编写一些不需要复杂算法的程序,对编程有了感性认识,然后再进入算法与编程紧密结合的学习阶段。
从这个意义上讲,编程是不需要基础的。刚开始编写的简单程序,要处理的数据不复杂,构造一些表达式即可以完成。进一步的学习,利用一些很朴素的算法思想就能解决的问题,在很多时候都可以不用意识到算法两字。初学者甚至都先不必关注效率等工程中必须关注的重要指标,能解决问题,能将学习进行下去就行。算法这样一个重要的领域,不应该成为初学者起步的包袱。编程学习中要先放开步子来,利用很自然的思维,解决需要体验的问题,享受由此而来的一切成就感。
编程不需要有基础,这句话是对的。其语境是面向白纸一张似的初学者,从零开始学起,避其高深之处,找到入口得门而入,任何的学习都可以这样开始。编程要以算法为基础,这也是对的。其语境是面向已经具备了一定的程序设计基础的学习者,要得到提高,要将编程从关注语言的级别,提升到真正解决问题能力的级别,这就需要知道算法的重要性。
算法无论是作为思考的中介,还是思考的结果,都是在编程学习起步之后必须正视的问题。由关注语言的学习,转而到对算法的重视,到达的就是一个新的层次。高层次总是在低层次积累到一定程度才能到达。一方面,从低层次起步时,需要安于当前阶段,通过学习和实践,完成该有的积累;另一方面,积累到一定程度时,再行突破,到达另外新的层次。
可以对算法的学习给予超出编一手好程序的期望。递推方法使用“步步为营”的策略,不断利用已有的信息推进求解,最终得到收敛的解;枚举(穷举)方法有个霸气十足的别名——暴力求解,从所有候选答案中去搜索问题的解;递归方法是一种直接或者间接调用自身的办法;分治方法将要解决的问题划分成若干规模较小的同类子问题,将子问题的解逐层合并产生最终的解;贪婪方法从初始解开始就在追求接近给定的目标,以求尽可能快地得到最好的解;试探算法选择一种可能进行试探,发现选择错误就即时回退,然后继续试探;分枝限界法意图找到最优,将不可产生最优解的分支尽可能早地剔除候选队伍。算法思想的光芒普照天下,这样的策略其实并不限于编写程序。学计算机的人是能干大事的,学计算机的人是多面手,他们的底气是掌握这些归为类的方法和策略。
算法学习中会遇到很多问题,结合着数据结构,围绕着抽象数据类型渐次展开。算法研究人员和算法的学习者并不是成天钻在抽象的符号当中工作,经典算法通过是借着经典的具体问题,甚至是一些好玩的故事来体现。查找和排序既是在计算机系统中使用频率最高的问题,又是特别能体现算法思想的问题。背包问题、货郎担问题、流水线调度问题,对应着无数的各式应用。在智能计算中大显身手的模拟退火算法、遗传算法、神经网络等等,来自大自然的智慧,促成了计算的成长。
效率问题是程序设计中的核心问题之一。程序运行要尽可能快,占用内存空间要尽可能小。算法的学习中,到处都体现出了这种“精打细算”的味道。我们能顺畅地欣赏视频,游戏中享受着逼真的感觉,点击“搜索”,瞬间就有搜索结果呈现在眼前,可以体会一下这些应用背后高效率的算法立下的奇功。到了移动计算、物联网的时代,手机、平板电脑这些低速计算设备,以及计算能力更加有限的专用设备显得更加重要,对效率的追求不降反升。在环境保护日益受到重视的今天,计算机设备的耗电量已经成为用电大户,中国所有台式机的用电量,相当于三峡电站发电量的总和,用上更快的算法,意味着计算机运行时间的减少,“绿色计算”成了业界的共识。
随着技术的发展,不断有运行更快、存储量更大的计算机被制造出来,也有了将更多的计算机连接起来组成机群,提供了超级的计算能力。在这样的背景下,有人质疑:“现在的机器设备已经足够快,还用如此费力地用人脑在算法方面有那么多的考虑吗?”他们主张,遇到问题,用暴力求解方法写成程序,交给足够快的超级计算机,一切搞定。甚至我亲自聆听过一位著名教授的言论:“我们不需要在算法方面花那么大的精力,利用机群,增加并行度,是一种更直接和简单的方法。”教授的言论有些道理,但事情并非如此。
举一个实例。在物理学、力学以及信息处理的很多领域,常常需要用到离散傅里叶变换。从算法复杂性度量的角度看,如果直接用公式计算,需要n^2次乘法。如果能够利用指数函数的周期性和其他一些计算技巧,则可以把计算量降低到nlogn次,这种方法叫做快速离散傅里叶变换。算法关注计算规模,当n值很大以后将会体现出高效算法的必要性。如果现在要用计算机对卫星拍摄的照片进行分析处理,对10×10 cm^2的照片每隔1微米进行划分然后进行计算。用传统的傅里叶变换算法,需要计算n^2=1016次,即一亿亿次,如果用百万次级的计算机,需要300年,这样的算法是在实际应用中不能接受的,而使用快速算法,则可以在1小时内获得结果。
在摩尔定律的作用下,计算机的能力在飞快地增长,价格也在不断下降。但是,永远不会有太快的计算机,因为总会产生出新的应用。进入了大数据的时代,需要处理的信息量更是呈指数级的增长,已经给计算、存储的设备提出更多的挑战。基于互联网,现在每人每天都会创造出大量照片、视频、语音、文本等数据,日益先进的存储手段使我们每个人的信息量都在爆炸式的增长,互联网的信息流量和日志容量也在飞快增长。在科学研究方面,随着研究手段的进步,数据量更是达到了前所未有的程度,三维图形、海量数据处理、机器学习、语音识别,都需要极大的计算量。在未来,更需要依靠卓越的算法解决问题,更需要大量的掌握了算法规律和方法的高水平人才承担责任。
面对一块巨石,等待一位大力士将之强力搬开是一种办法。找来一根杠杆,用很小的力量也可以将之撬动。常指望着高性能的计算机求解,依靠的是大力士,而在算法上做文章,用的却是巧劲。我们需要的是智慧的力量,体现出的是智慧的美。当遇到任何大力士都无法撼动的巨石时,我们只有依靠智慧了。
抓住算法,打造核心竞争力
学计算机,要关注算法的学习。除了在课名中就带有“算法”的课程,专业核心课程几乎都是围绕算法展开的,只不过针对的是专门的问题:操作系统关注的是系统资源分配、管理的问题和解决算法;编译原理关注人工语言处理的算法;计算机网络中的重点——协议实际就是算法;计算机图形学关注图形表示的数据结构,以及图形显示、变换等的算法;多媒体技术基础是图像、音频、视频编码、压缩的算法;密码学中是保证信息安全的算法;人工智能中涉及各种决策、模式匹配、优化算法;并行计算是多处理机同时完成一个任务的算法。各种基础的课程,尽管其描述的方式中数学味浓一些,涉及的也是算法。离散数学、可计算理论是算法,线性代数、数值分析、概率论和数理统计中也是算法。认识不到这一点,有些同学就会声称的“操作系统中全是些概念,背一背就过了”、“概率论没有用”之类的断言,真的会将自己学习的方向引入歧途,甚至找不到方向和动力。经历了求职的大学生会发现,技术类岗位的笔试、面试,考到、问到的内容,需要的是算法基础。在企业招人的考试中都是这样,可以知道算法的基础性作用。
学得常用的算法,学会算法分析和设计,这是计算机类专业大学生应该达到的程度。遇到要解决的问题时,为问题建立计算模型,用已有的算法解决问题;进一步,能根据具体问题的实际情境,从多种算法中选择一个最合适算法;更高的要求,解决问题没有现成的算法可以利用,凭借着算法方面的良好素养,能够在已经有的算法基础上进行改进,甚至运用学到的算法策略,设计出新的适用算法,这是充满创造性的工作。在高等教育中专科、本科、硕士、博士的不同层次,都设有计算机专业。不同的学历侧重不同,学历越高,对算法要求有更深入的学习,并且要学习和研究的算法,由一般性算法问题逐渐过渡到专门的算法。
有人说:“本科生用不着学太多的算法。”他马上可以举出的例子就是,现如今有多少码农的工作需要算法?很多人的工作,拖拖控件,写些调用数据库的语句,如此而已。这是业内一部分技术人员的工作场景,但这不是全部,不能成为我们放松学习算法的理由。要在行业的生态系统中生存发展,对大学生的要求不能只看着技术生态链中的一部分。纵观行业中高薪的、核心的技术岗位,岂是只会拖控件就能胜任?万万不可以将刚进入行业时立足的工作形态,当作长期发展的形态。这句话背后的事实是,有些本科生连拖控件的要求都达不到。这句话也还提醒我们,专业基础很重要,但基础不是全部。花一段时间,实践应用开发,学习和体会拖控件之类的其他技能和要求,这也是大学中必要的安排。
很少用到算法的人,有些人是知其有且知其不必用,而有些人是不知其有,进而需要用时当然也不知用,这两方面的差距很分明。实际上,人们之所以说算法很重要,是因为任何的程序,任何的软件,都是由很多的算法和数据结构组成的,大学阶段学习的算法都是基本的算法,是作为每一名专业人员都需要有的公共知识,实在不必因为“很少用到”就生出“放一放”的心思。
这样看来,有的程序员很少用到算法的背后,体现的是软件行业中的分工不同。可以将软件开发工作分为算法密集型、业务逻辑密集型和使用体验密集型三种类型,这三种类型的工作对算法的要求各不想同。算法密集型的如搜索引擎、游戏引擎开发、模式识别等,这些主要的任务都与算法密切相关;业务逻辑密集型,典型的如信息管理系统,其中工作量最大的是实现用户的业务需求,熟悉用户的业务并按要求完成应用更重要;使用体验密集型,如教育、游戏软件开发,更注意友好的界面、方便的操控和愉悦的使用体验。在后两种类型的工作中,会有90%以上的代码只是涉及到业务流程,以及用户界面,的确不需要直接使用什么算法。但是,往往就是余下的10%代码,有相当高的性能要求,所采用的算法决定了系统整体的性能,决定了产品的竞争力。想在软件行业成为有竞争力的从业者,有尽可能好的算法功底,也就成了自然的选择。
数学的思维之用与直接之用
传说中,数学对计算机很重要,但有些程序员也宣称感觉不到数学对他们的意义。其实考察下来,与对算法的态度对比下来,有关用不用数学的观点,也是大致相似的。
有位学生给我来信,他很想知道高数和线性代数对计算机专业有什么作用,要学到什么地步。这个问题在和大学生交往过程中没有少被问到。一些人只是问问而已,还有的是想找个理由不想学那么多数学,当然应该都是遇到了困难。有些计算机类专业的学生,对学数学有情绪。
数学对于计算机学科的重要性常被人谈起。数学与计算之间存在着不可分割的联系:数学起源于对计算的研究,计算作为数学的研究对象已有几千年,数学仍在为计算提供着理论、方法和技术,而计算科学为数学提供了能自动计算的设备,并为更有效地完成任务提供了工程方法和技术手段。计算曾经是数学的一个分支,而当其承担起构造能自动计算和会思维的机器的任务时,逐渐发展出了其特有的成分,并具有了更多工程性质的内容,形成了自己的体系。尽管现在计算与数学已经不再等同看待,但纵观学科间的联系,计算与数学之间的亲缘关系还是最近的。
数学,对于计算机专业人员的作用主要表现在两个方面:
第一方面,是数学的思维之用。数学是科学之母,承载的是一种科学家思考和解决科学问题的具体方式和途径。学数学的过程,就是要获得这种思考和解决问题能力的过程,还可以获得从事IT行业的技术工作中需要的逻辑思维能力和严谨的工作作风。
另一方面,是数学的直接之用。数学可以直接应用于解决一些问题。数学是从各种实际应用需求中抽象出来的理论,之所以要将实际抽象成理论,目的就在于将直击本质的、具有普适性的理论更好地指导实践,将已有的理论,伸入到无限的应用场景中。在计算机工程实践中,要大量用到数学各个分支的知识。例如
· 无论二维平面绘图还是三维动画软件,都需要有几何学的支持;各种图形软件开发中,需要用线性代数中的坐标变换、矩阵运算;
· 游戏开发中图形处理是一个重头戏,此外,还涉及物理方程和公式,想想愤怒的小鸟在被弹射出去的那一刹那,弹力、重力、滚下斜坡,统统要用到;
· 谈及数据压缩与还原、数据加密与解密,以及倍受关注的信息安全,离不开数论、代数编码理论,小波函数等数学分支;
· 在人工智能及其衍生的各个领域,涉及决策、智能的话题,概率、统计不能不提。
这样的例子数不胜数。有很多人感叹,搞计算机,学多少数学都不算多。
尽量学好数学
IT业是一个包容性很强的行业,不少从业人员并非计算机专业毕业,甚至有不少没有接受过大学教育。很多在校大学毕业生很在意这种“抢饭碗”的事情,也为此表示担忧。应该要意识到,一些简单的专业技能、一般的程序设计技术,门槛并不高,掌握起来相对容易,让更多的人有机会从事合适的工作,要能接受这样的事实。而作为计算机类专业的大学生,需要知道自己的竞争优势和不可替代性,就在于在接受专业培养的过程中,具备了专业的思维,积累了扎实的关于计算的基本理论,掌握了许多其他人员并不深究也深究不了的东西,例如,算法、计算机体系结构,等等。用专业的思维分析和解决问题,有能力研制大型系统,从事底层开发,这需要有专业的知识结构和技能作为支撑,其中数学方面的知识,在为专业理论充当支撑。数学专业的毕业生到软件企业中从事软件设计与分析工作的工作要有优势,而计算机类专业的毕业生从程序员起步的居多,原因就在于数学专业的学生在分析推理能力方面,从所受的训练要比计算机类专业的学生多。计算机类专业毕业的学生有自己的特点,倒不用对数学专业的学生有多少羡慕,但从中应该看到数学对计算学科的重要意义。至于和别人抢低端饭碗的专业毕业生,一方面,这只是在刚进入行业的一种方式,另一方面,有些毕业生没有具备专业的核心能力,不得不这样去做。
不过,也有IT行业的专业技术人士说,他们在工作中根本没有感觉到数学的存在。甚至有人认为程序员不需要了解数学,更重要的是要去了解设计模式、面向对象原理、软件工具、界面设计,以及一些其他类似的东西,数学不好也可以做一个很专业的程序员。这样的感觉是对的,这也是在行业内的一种生存方式。
为什么会有这样的感觉?从数学的思维之用看,当训练出来了专业思维,在解决问题时,自然而然就用上了,这是一种最佳的效果。我曾经与我的学生谈离散数学非常重要,其重要程度甚至到了几乎感觉不到其存在的程度。这句话听起来矛盾,我要说的是,要让离散数学的精髓成为你的思维习惯。遇到问题,不用想用离散数学中的哪个定义、定理,但确实就是那样想了,那样做了,这和武术中的“出手不见招”的境界是一样的。
从数学的直接之用看,大学教学需要关注到未来各种可能的用途,要考虑到大多数人的需要。这样一来,对某些人,自然会有一部分用不上。工作中是否要用到大量的数学,和从事的工作领域有很大的关系。例如:
· 投身到科学计算,以及近几年非常热门的数据分析领域,数学知识时时要用;
· 从事游戏开发,涉及图形处理、实体建模、逼真效果展示,数学很重要;
· 从事系统开发中最核心的工作,在算法层面考虑问题,数学基础不好不行;
· 涉及信息安全领域,没有数学功底没法玩。
然而,如果从事的是系统实现环节的工作,如大多数信息系统开发中的实现、测试工作、人机交互界面设计、系统管理等岗位,真的就不需要太多的数学,感觉不到数学的存在也就不足为奇。
另外一个因素是,软件业内的分工进一步细化,很多在开发中需要用到的底层处理由成熟的软件组件提供,用到的数学就“藏”在这些组件中,不需要开发人员考虑了。例如:
· 搜索引擎构造中要用到复杂的算法,而大多数的开发直接引入类库,如开源的Lucence,调用函数就行;
· 游戏开发中要到各种计算模型,足以将目前游戏开发队伍中一大半人淘汰,但使用功能强大的游戏开发引擎,如2D的Cocos和3D的Unity,难度、成本降低,开发周期缩短,开发质量提高,何乐而不为?
由此可见,数学对于计算机类专业很重要,但也不是重要到每个人都必须要精学多学的那种程度。行业中有分工,每个层次、每个领域需要的能力结构和要求也各不相同,实在也没有必要千人一面地做出硬性的要求,要求大家都向数学专业的学生靠拢。另外,在有限的大学时间内,仅培养出突出的数学能力是不够的,诚如上述所提到的纯粹工程的能力,也是不可或缺的部分。在这一方面,计算机类专业的培养方案还是考虑了这样的需求。其中,对计算机科学技术专业,在数学及其他的理论方面的要求相对要高,而对于软件工程、网络工程等专业,工程能力要求相对更重要。
这不能成为一些同学放弃培养方案内正常的数学课要求的理由。数学的思维之用和直接之用并不是能够严格划分开来的,数学之用的无限可能也不是哪位老师能随意讲清。在大学这样一个重基础的学习阶段,还是要有些耐心,完成该有的积累。我们应该看出,达到培养目标的毕业生,在各种岗位上的游刃有余,数学和算法能力强的,将拥有更多的选择机会。即使是在利用Lucence和Unity开发探索引擎和游戏,如果能对其内部的机制多一引起了解,你的层次将不只是“使用”,而可以是“驾驭”,意味着工作质量的提高,意味着你的个人价值和潜力。而如果过早地放弃了对专业核心能力的追求,在岗位选择以及发展空间上面临的限制,也就显而易见了。除非是因为各种原因真的失去了机会,我们再作退而求其次的打算。
=================== 迂者 贺利坚 CSDN博客专栏================= |== IT学子成长指导专栏 专栏文章的分类目录(不定期更新) ==| |== C++ 课堂在线专栏 贺利坚课程教学链接(分课程年级) ==| |== 我写的书——《逆袭大学——传给IT学子的正能量》 ==| ===== 为IT菜鸟起飞铺跑道,和学生一起享受快乐和激情的大学 ===== |