按照维基百科上的解释,数学没有准确的概念,它可以笼统地分为纯数学和应用数学。纯数学研究数量(Quantity)、结构(Structure)、空间(Space)、变化(Change),使用各种符号找寻规律,将各种猜想形式化,并证明其真伪。数学的基础包括了:数理逻辑、集合论、范畴论、计算理论。数量主要就是数字系统的建立,结构涉及组合、数论、图论、群论、代数等,空间涉及几何、差分几何、拓扑等,变化则是微积分、差分等式、分析。而应用数学则是研究数学问题的解答,于是也可以看作是计算数学,涉及了概率论、统计、密码学、优化、数学经济/物理/化学/生物等。
从编程的角度,数学中很多东西的确是没有必要吃苦头去学。在翻译的文章《程序员如何学数学》中,作者戏谑到:“事实上,我觉得你不需要知道任何事情,只要你能保持活着就行了”。个人觉得本文提到的内容是每个程序员应该有的职业素养、科学素养。不管你是在职场做写着你以为普通的代码,还是在学校里做学问,相信用心练习领悟都会受益终身。本文总结了程序员应该关心的数学主要分支和基本的学习方法,但也只能称作数学和数学学习的相关“知识”,真正的修行正如老话所说:“师父领进门,修行在个人”。这些好的书籍、资料顶多只能将你带进数学殿堂的大门内,正式的修行在此才真正开始。
我们经常说编程的本质是计算,即那本经典的算法书的标题:程序=数据结构+算法。那么数学与计算又是什么关系呢?最近写过一篇《X的奇幻之旅》,是一篇同名书《The Joy of X》的读书笔记。这本书中有一章与全书同名。本以为是全书最核心的一章,结果却什么没学到。
最近一直在思考计算在数学中的位置,突然犹如醍醐灌顶。代数乃至数学中最重要的两个活动:一是定义概念后用等式表示属性(变量)间关系,从而深入了解表示这个概念的数学对象;二是有了概念之后,为其设计计算过程,通常用于解决一个问题。两者都需要证明,前者证明等式所表达关系的正确性,而后者则证明计算过程的正确性。计算过程可以使用前者找到的规律来简化。也许作者并不完全表达相同的意思,但确实给予了灵感。
举个例子,我们提出一个关于数论的概念,比如质数,什么样的数是质数。然后我们研究它、找规律,可能得到一些等式,也比如得到一些结论,比如偶数只有2是质数,并加以证明。最后我们设计一个计算过程,比如计算得到一个区间内的所有质数,于是我们设计一个算法和数据结构。并利用前面找到的规律优化,最后证明其正确性。数学、计算、编程,三者和谐而统一。
在上面这一套过程中,计算过程实现后的反复使用,甚至部分推导也许可以用计算机来辅助。但找规律、下结论、设计计算过程、解决问题都是创造性的工作,否则早已被计算机所代替。然而,最展现人类思想伟大的可能要属概念和证明。为什么是这两个呢?
有人说数学是其他学科的基础和指导,比如各种自然科学、经济、工程等等,然而哲学是数学的基础和指导。看起来有点悬,可确实是这样。如果你不相信、不理解无穷的概念,你是不可能建立起微积分的。如果你坚定宇宙是像精密的时钟一样,你是不会相信现代统计学和量子力学的。你要相信,才能创造。这也是最近读了《How Not to Be Wrong: The Power of Mathematical Thinking》的感受。
而证明则是另一种的天才。我们经常习惯去找套路。就像题海战术一样,给不同的解题策略起各种名字。证明也同样有套路,基础就是逻辑和集合论,再加以直接证明、归纳法、反证法等等训练,于是就觉得我们能证明一切。其实,证明或者很多事情有时真的教不了。推荐看看《Proofs from THE BOOK》吧。
数学家一般是如何解决问题的呢?首先,我们会从一些公理(Axiom)和定义(Definition)出发,提出猜想和假设(Conjecture),通过中间过渡的引理(Lemma)和命题(Proposition),推导出定理(Theorem),再由此引申出一些推论(Corollary)。以前一直搞不懂这些概念是什么关系,现在终于理清楚了,如上所述顺序就是:Axiom/Definition/Conjecture => Lemma => Proposition => Theorem => Corollary。
可能大家对这些概念之间的区别不太了解。从定义上说Proposition是不太重要的定理,其实不必较真,Lemma、Proposition、Theorem、Corollary都是相对来说的,可以自己决定,可能一位数学家觉得这是Lemma另一位就觉得这应该是一条Theorem。比如费马大定理(Fermat’s Last Theorem),其实曾经是一条Conjecture,现在却是Taniyama-Shimura的推论,而名字上却叫做Theorem。
看或者写定义和定理,有一点最重要:最需要留意的其实不是主体,而是条件(Condition)和假设(Assumption)。比如最著名的毕达哥拉斯定理,可能大家都知道两边的平方和等于斜边的平方,但其成立的条件呢?这就好比,我们写代码调用了API时,就想着API的功能,却没留意Precondition,结果API没有返回期望的结果。
前面说过写数学的重要性:如果你不能用写的方式解释清楚,那说明你没有理解,写出来才会让你像数学家一样思考。关于如何写数学,第一点就让人很诧异:用自然的语句写出来!这是我们普通人,甚至新手数学家最容易犯错的地方。我们大多数人都以为数学是高度符号化的语言,所以我们用一系列符号来回答解决问题就行了。这样理解不对吗?符号多么简洁、多么高大上,完全用符号来推导不是最好的方式吗?错!符号仅仅是一种速记法,我们想要表述的永远是想法、概念,而没有语句来形成上下文,符号只是一堆没意义的乱码。总而言之,符号只有在语句形成的上下文中才有意义,才能表述概念。
自解释代码(self-explaining code):在谈到代码与注释的比例和关系时,我们经常会说到自解释代码,即在最极端的情况下可以做到没有注释,完全通过方法名、变量名达到解释的目的。以前觉得,这种“完美”的情况也许可行,但不完全适用于我们非英语国家的开发人员。但今天在看《How to Think Like a Mathematician》时突然看到这里,不禁陷入深深的思考。纯数学是描述性语言,描述这个世界背后的规律,而程序、算法、计算则可看作应用数学,是过程化语言。证明是纯数学的核心,那么代码(或者算法)就是计算的核心。如果将符号推导与代码做类比的话,那我们是不是可以将证明中的解释语句比作代码中的注释呢?如果这样比较还说得通的话,那是不是可以这样想:连绝顶聪明的数学家都主张在符号中加一些语句,使整个证明更加清楚,那我们普普通通的程序员不应该清清楚楚地写好注释吗,而不是一味的追求代码的自解释。
第二条原则就是:用最简洁的方式写数学。为了达到这个目的,使用尽可能短的单词和语句,避免使用复杂的词汇、句型、双重否定等,这样才能够避免歧义,最容易理解。同时还要清楚地表达自己,像写议论文一样,清楚地列出argument、conclusion,多使用因果关系的连接词。
干净的代码(Clean Code):从这个角度来说,数学证明与计算编程两者有着殊途同归,都强调了用最简洁的符号来表述。在大叔的《Clean Code》中,有专门的章节来强调函数和类都要尽可能地短小。
作为程序员,最熟悉的莫过于离散数学了。从离散而非连续的角度,学习逻辑、集合论、概率统计等。但离散数学的书看多了有时会产生抑制,就会觉得这些都很基础,大概都了解。想要更好地掌握,唯有超越离散数学,往更深的层次去学习。离散数学中的每一个部分都是一个单独的数学分支,都可以继续深入学习。
之前在翻译的文章《程序员如何学数学》中,对原作者“浅尝辄止”的数学学习方法很感兴趣,于是对各个数学分支简单地调查了一下,对其谈到的程序员值得学习的数学分类又补充了一些:
本文的前半部分只是开胃小菜,真正的修炼从这里才开始。下面就是一些自己精心挑选的参考资料。一部分为科普读物,让你能带着乐趣地对数学的某些方面有一些了解。但千万别以为读了几本科普书,自己无所不能了。看出了乐趣,提高了信心自然好,但要清楚地认识到:任何知识和学科的学习都不简单。所以对每一部分,还需要选一些稍微学术些的课本来深入学习。这一部分是不断更新。
1.《Mathematics: A Very Short Introduction》:牛津出版的一本非常小的口袋书,别看书很小,但内容有口皆碑。
2.《Concepts of Modern Mathematics》:Dover出版,价格公道,作者Ian Stewart是大牛,写过《数学:确定性丧失》和很多数学著作。这本写的也非常深入浅出,不需要什么预备知识就能基本看懂。通过一本书了解数学的发展,而不是我们在学校里学习的传统数学,强力推荐一下!
3.《How to Think Like a Mathematician》:书名有些大,内容可能对于相关专业或有科研背景的人来说太简单,但对于门外汉的程序员来说还是很新奇的,可以学习一下数学方面的人是如何研究问题的。读过之后,再去看编程方面比较严谨的书像《算法导论》,就会有新的发现。
整理了一下对初学者比较友好的数学书,参考了Goodreads上的评分:
下面是专项训练的书,有些分支还没有选好,先列一下选好的部分: