程序员最大的遗憾:在大学时忽略了数学

阅读数:4963 2019 年 9 月 9 日 07:07

程序员最大的遗憾:在大学时忽略了数学

数学重要吗?

数学比你想象的更重要,也比你想象的不那么重要……

你可以忽略数学,你可能只想成为一名高薪的程序员。编程是一个十分广泛的领域,你可以选择你想要关注的领域(其中一些不需要数学知识),并且仍然能够取得成功。但从另外一个角度来看:

  1. 数学是用来解决特殊问题的工具;
  2. 编程就是在做数学。

我想给你们举两个例子,希望能够激励你们给自己一个学习数学的机会。为此,我挑了一些简短(平均 10 分钟)但可能会让你们感到兴奋的视频,用它们来解释这些概念。

但首先请允许我说一些与数学无关的东西……

如果你不想学,就不会去学

在我上大学的时候,我真的不明白学习数学有什么意义。大学的数学和我们在高中学的数学有很大的不同,而我真正想学的是编程。数学似乎只是我获得学位的必经之路,只是为了考试,对我的编程生涯似乎没有任何价值。我固执地认为事实就是这样,所以拒绝在数学上投入很多时间,这导致我第一年被分到一个表现不佳的小组。我太骄傲(应该说是愚蠢)了,但考试有 40% 与数学证明有关,我也只是勉强通过考试。第二年我仍然没有改变之前的看法,到最后不得不去参加补考。在补考的时候,我终于妥协了,然后用暑假的大部分时间来学习数学。花了一个暑假,我还是没有学透数学,仍然不明白为什么它这么重要。你可能会认为我会从这次巨大的失败中学到一些教训,但实际上我仍然被固执所蒙蔽。我只想说,如果数学只是一门选修课时,我是不会选它的!但在事后看来,这是我最大的遗憾之一:五年后,我发现我最感兴趣的问题都与数学有着密切的联系。

在递交硕士论文的那一周,我终于拿起一本计算机方面的书。这时,我终于意识到数学和计算机科学以一种非常有趣的方式联系在一起。从那以后,在宝贵的课余时间,我一直在努力赶上那些上课认真听讲的学生。这是一个持续了十多年的过程,而我本可以在大学四年里把这些事情搞定。这十年我本可以把时间花在学习新东西上,就像我的同龄人一样,但我的数学还没有达到研究生水平。有时候我真的觉得我浪费了自己的生命,但与其纠结于此,不如让我们来看一些例子,看看数学对于编程来说有多重要。

数学是程序员的得力工具

游戏和电影中的图形编程涉及到物理知识,但精确的物理模拟是十分昂贵的,所以我们通常使用数学的数值方法来替代,例如使用 Verlet 积分来近似模拟物理:

https://youtu.be/2TIEfgC3tAo

你可能认为谷歌的搜索算法只是简单地计算网页上的单词数量,然后显示具有相关性单词数量最高的页面,如果真是这样,那排名也太容易了,只需要重复地计算单词数量,并不包含任何数学问题。但实际上,对搜索页面进行排名是一个大难题。PageRank 算法考虑到网页之间的链接数,将它们放在矩阵中,然后使用线性代数的特征向量近似算法来计算排名:

https://youtu.be/F5fcEtqysGs

在学习过程中,我发现人工智能(或者说机器学习的子领域)非常有趣。在舞蹈游戏中跟踪手势,在 Netflix 上寻找你可能喜欢的电影,识别当前正在播放的歌曲,等等。如果你想要参与构建这些系统中的任何一个部分,至少需要对微积分、概率论和线性代数有很好的理解。

https://youtu.be/8onB7rPG4Pk

我认为这些例子已经足够了,它们都把数学作为解决特殊问题的工具。现在我想谈谈不那么明显的问题,并解释为什么说数学是就是编程。

编程就是一个吃掉数学的过程

抽象是开胃菜

抽象是编程极其重要的组成部分,我们通过抽象把一个复杂的问题分解成更小的部分。当我们发现了一些模式或者想要隐藏一些复杂性,就会进行抽象,例如使用抽象类或接口。我们甚至为如何抽象和抽象这些模式而创建更高层次的模式。抽象的方式是非常重要的,因为抽象可能会让人感到非常困惑,也可能非常有用。我们如何才能发现最有用的抽象?

尽管计算机只存在了几十年,但计算和计算引擎的设计问题已经存在了几百年。这个事实确实令人感到惊讶,但与数学相比,它仍然只是一个非常年轻的领域,因为数学已经存在了数千年。也就是说,数学拥有更多的时间用来解决某些问题。我们不妨看看是否有可以窃取的想法——事实上,如果不这么,就显得我们有点傲慢!

抽象代数是数学的一个子领域,下面是对抽象代数的一些介绍:

https://youtu.be/QudbrUcVPxk

群是加法和乘法的的抽象,也是幺半群(Monoid)的超类。如果一个组移除了元素需要逆运算的属性,就只剩下幺半群。幺半群是一组元素,包括恒等元素及其关联操作。下面的抽象不仅适用于加法,而且还可以:

幺半群 恒等元素 运算
整数加法 0 +
整数乘法 1 x
字符串串联 空字符串 concat
扩展列表 空列表 extend
并集 空集合 union
布尔类型任意 false or
布尔类型所有 true and
图像重叠 透明图像 重叠

这个抽象很有用,现在我们就可以编写适用于任何幺半群函数的实现。例如:

  • 一个简单的函数 mconcat,将一系列幺半群元素组合成一个单独的元素,这个时候对整数列表求就变得与重叠图像列表一样。
  • 一个复杂的函数 foldMap,可以递归地遍历树,或者:
    • 返回值为 true 的元素;
    • 找出是否有任何元素大于 5;
    • 将可折叠结构转换为集合。

我们可以进一步抽象,不仅可以将这个函数用于树,也可以用于任何可折叠容器,换句话说,就是任何可以转换为列表的容器。

在设计程序库时,幺半群也是很有用的。如果有一个二元函数,比如乘法,它接受两个相同类型的参数,并返回一个相同类型的结果,这个时候最好要考虑一下哪些是恒等元素。如果找到了,就等于找到了一个非常直观的方法来使用二元函数。你的函数甚至可以处理空列表,因为你有一个合理的带有数学属性的默认值,这将简化用户代码。

在这,我们使用抽象来创建实现,但其实抽象的作用不仅限于此,抽象的作用是解释或发现连接。

组合是主菜

分而治之是人类解决复杂问题的一种非常流行的方法。把问题分解成更小的部分,解决这些小问题,然后把这些解决方案组合成更大问题的解决方案。你能想到另一种更通用的方法吗?

在编程中,我们把一个问题分解成几个较小的函数来解决较小的问题,然后把它们组合成越来越大的函数,最终解决较大的问题。将函数组合在一起的最佳方法是什么?我想知道数学当中是否有我们可以借鉴的想法?

范畴理论是数学的另一个子领域,我喜欢把它叫作抽象的抽象代数,但实际上,它是有关组合的数学。我们可以从中找到很多有用的想法,比如:

  • 函子(Functor),我们可以使用映射函数将函数应用于容器中的每个元素,比如 Python 的 List、Java 的 Stream API 和 Optional,甚至是 Haskell 的函数。
  • 单子(Monad)是 Python List 遍历、C# LINQ、Scala 解析器组合符、Haskell IO 和并发性的基础。我们可以找出任何一种编程语言的的单子。
  • F 代数,我们通过递归进行抽象。

要理解这些抽象的概念可能需要一段时间,所以你开始得越早越好。下面是一个简短的视频,试图解释单子是什么:

https://youtu.be/Nq-q2USYetQ

数学证明是甜点

你们可能还记得我之前说过数学证明是大学数学中最无聊的部分。但我想告诉你的是:类型可以被看作命题,程序可以被看作证明:

https://youtu.be/SknxggwRPzU

以下是一些可证明的属性:

  • x + y = y + x
  • P & (Q | R) = (P & Q) | (P & R)
  • length(filter(predicate, list)) <= length(list)
  • C 编译器生成的可执行代码与 C 程序的语义完全相同,请参见 CompCert( http://compcert.inria.fr/compcert-C.html

现在,我们知道我们可以用编程来证明一些东西,而我发现证明是编程中最有趣的事情。我们因此可以编写更安全、错误更少的程序,因为我们可以证明属性,而不只是测试它们。

但是等等,如果你能用编程来证明一些东西,不是也能通过写程序来为数学做出贡献吗?是的,数学证明不仅对你们来说是难题,对于数学家来说也是难题。数学家总是在证明过程中中制造错误——而这些错误几十年来都没有被发现。同伦类型理论研究的是不同类型的等式,它以单价观点颠覆了数学的基础,认为所谓的数学基础是有缺陷的。是的,即使数学也不是完美的,还有很多需要我们去提升的空间。

我还只是这方面的新手,不过我发现这非常有趣,并且迫不及待地想要学习更多。我可以推荐读者阅读一下 Little Typer( https://mitpress.mit.edu/books/little-typer ),它为有递归经验的程序员提供了如何使用依赖类型来进行证明的入门知识。

寻找灵感,打好基础

这似乎是我以前一直在试图不惜一切代价避免的事情,结果却变成了我最喜欢做的事情。所以,不要犯我曾经犯过的错误:充分学好你的数学必修课,否则你以后会后悔的。

如果你的教材不能激励你去探索这一领域的知识,那么可以转向其他的在线资源,比如 YouTube、Coursera 或 Edx,或者试着找一本更好的书。有些人能够将难以理解的概念解释清楚,并激励你——你只需要花点时间去把这些人找出来。你还需要进行练习,就像练习编程一样。

还有一些时候,我也感到很挣扎,因为有些新的概念需要以之前的东西为基础,但我却缺失了这些东西。请不要羞于回头重新去学习之前错过的东西。在数学中,很多东西都是建立在彼此的基础之上,没有坚实的基础,很难取得进步。花点时间回头学习之前错过的东西总是值得的,试着正确地把握这些关键概念,而不是盲目地往前走,让自己陷入一片混乱之中。

我有一个待办事项列表,我用它来弥补之前错过的东西。这只是一个漫长而愉快的旅程的开始。数学是一门庞大而令人兴奋的学科,几乎所有的东西都是相互联系的。我们可以从更大的角度来看待数学:

https://youtu.be/OmJ-4B-mS-Y

原文链接:
https://awalterschulze.github.io/blog/post/neglecting-math-at-university/

评论

发布