用Python学习线性代数和微积分!这本程序员数学指南,教你用代码表达数学思想,适合所有学习者。
原文标题:颠覆传统,程序员学数学的神器竟然是Python!
原文作者:图灵编辑部
冷月清谈:
书中还强调了程序员在学习数学方面的优势:
1. 使用正式语言:程序员习惯于使用精确的语法,这有助于理解数学的严谨性。
2. 构建自己的计算器:程序员可以利用编程语言构建可扩展的计算器,根据学习进度添加新的功能。
3. 用函数建立抽象概念:程序员可以通过函数来理解数学中的抽象概念,并将这些概念转化为代码。
作者认为,Python 作为一种高级编程语言,拥有丰富的数学库和图灵完备性,是学习数学的理想工具。本书通过可复用的 Python 代码来实现每个新的数学概念,帮助读者加深理解并构建自己的数学工具箱。
怜星夜思:
2、对于非计算机专业的学生,学习数学是否也推荐使用Python?相较于传统的学习方法,Python的优势在哪里?
3、书中提到的Jane的学习经历很常见,大家在学习新技术时都会遇到类似的问题。除了学习Python之外,还有什么方法可以避免陷入“深度优先”的技术文献搜索?
原文内容
我把《程序员数学:用Python学透线性代数和微积分》 这本书的重点是放在一个核心技能上——用代码表达数学思想。我认为,即使你不是程序员出身,这也是一个学习数学的好方法。
我上高中的时候,在 TI-84 图形计算器上学会了编程。我当时就产生了一个宏大的想法:可以编写程序来帮我完成数学作业和科学作业,给出正确的答案并输出答题步骤。正如你所预料的那样,这比只帮我做作业要困难得多,但让我产生了一些很有帮助的想法。对于任何一种想通过编程解决的问题,我都必须清楚地了解其输入和输出,以及在解决方案的每个步骤中发生了什么。最后,我确信自己真正地理解了相关的数学资料,并实现了一个程序来证明这一点。
这就是我在本书中想与你分享的经验。本书中的每一章都是围绕一个具体示例程序来组织的,想让它可以运行,就需要把所有的数学知识正确地组合在一起。一旦完成这个过程,你就会充分相信自己已经真正理解了这个概念,并且可以在未来的某个时候再次使用它解决问题。我在本书中加入了大量的练习,来帮助你检查自己对数学知识和代码的理解。我还添加了一些小项目,邀请你对所学知识做出新的尝试。
Jane 想学习数学
我的朋友 Jane 是一名全栈 Web 开发者,在旧金山的一家中型科技公司工作。大学期间,Jane 并没有深入学习计算机科学或任何与数学相关的科目,她的职业生涯是从产品经理开始的。在过去的十年里,她学会了 Python 和 JavaScript 编程,并得以转型到软件工程领域。现在,在新的工作岗位上,她是团队中能力最强的程序员之一,能够搭建数据库、Web 服务和用户界面,为客户提供重要的新功能。显然,她非常聪明!
Jane 意识到,学习数据科学可以帮助她在工作中设计和实现更好的功能,利用数据来改善客户的体验。Jane 在上班的地铁上,经常会阅读一些关于新技术的博客和文章。最近,她着迷于几篇关于“深度学习”的文章。一篇文章谈到谷歌的 AlphaGo,它在深度学习的支持下,在一场棋局中击败了世界排名第一的人类棋手。另一篇文章则展示了由普通图像生成的令人惊叹的印象派画作,它同样使用了深度学习系统。
看完这些文章后,Jane 在无意中得知,她的朋友的朋友 Marcus 在一家大型科技公司找到了一份深度学习的研究工作。据称,Marcus 的年薪和年股票收入总共超过了 40 万美元。考虑到自己职业生涯的下一步,Jane 还有什么理由不去研究这些令人着迷又赚钱的问题呢?
Jane 做了一些研究,在网上找到了一个权威(而且免费)的资源:Goodfellow 等人所著的《深度学习》一书。这本书读起来很像她习惯的技术博客,让她对学习这个主题更加兴奋。但随着她不断阅读,书中的内容越来越难。第 1 章涵盖了需要学习的数学概念,并介绍了很多 Jane 从未见过的术语和符号。她浏览了一下,想直接进入书中的精华部分,但书中的内容依旧越来越难。
Jane 决定暂停对人工智能和深度学习的研究,先去补充一些数学知识。《深度学习》的数学章节列出了一本关于线性代数的参考书,供从未接触过这个领域的学生参考。她找到了这本由 Georgi Shilov 所著的教科书,名为《线性代数》。但是她发现这本教科书有 400 页之多,和《深度学习》同样晦涩难懂。
在花了一个下午的时间阅读关于数域、行列式和余子式等深奥概念的定理之后,她停下来了。她不知道这些概念如何能让她实现一个帮助棋手取得胜利或生成艺术品的程序,也不打算再花几十个小时从这些枯燥的资料中寻找答案。
我和 Jane 见了面,边喝咖啡边叙旧。她告诉我,因为不懂线性代数,所以在阅读真正的人工智能书时很吃力。最近,我听到了很多类似的感叹。
我想读一读关于「新技术」的书,但似乎需要先学一学「数学主题」。
她的做法是值得赞扬的:为想学习的科目和缺失的预备知识寻找最佳资源。虽然动机很合理,但是她发现自己陷入了令人厌恶的“深度优先”技术文献搜索中。
在数学课本中苦苦挣扎
大学水平的数学书,比如 Jane 选择的那本线性代数书,往往是非常公式化的。每一节都是一样的套路:先定义一些新的术语,再用这些术语陈述一些事实(即定理),然后证明这些定理为真。
这听起来是一个很好的、符合逻辑的顺序:介绍所讲的概念,陈述一些可以得出的结论,然后证明这些结论。那为什么读高数课本这么难呢?
问题是,数学知识实际上并不是这样创造出来的。当提出新的数学思想时,在找到正确的定义之前,可能会经过很长一段时间的实验。我想大多数专业数学家会这样描述他们经历的步骤。
-
发明一个游戏。 例如,从“玩”一些数学对象开始。尝试列出所有这些数学对象,在这些对象中找到模式,或者找到具有特定属性的对象。
-
形成一些猜想。 推测一些可以陈述出来的、关于游戏的一般事实,并至少让自己相信它们一定是真的。
-
创造一些精确的语言来描述游戏和你的猜想。 毕竟,在你能表达它们之前,你的猜想不会有任何意义。
-
最后,凭着决心和一些运气,为你的猜想找到一个证明,说明它需要为真的原因。
我们要从这个过程中吸取的主要经验是,应该从全局思考开始,不要拘泥于形式。一旦对数学的工作原理有了大致的了解,词汇和符号就会成为有用的工具,而不会分散你的注意力。数学课本通常按相反的顺序排列,所以我建议把课本作为参考,而不是入门材料。
学习数学的最好方法并不是阅读传统课本,而是探索一些想法,并得出自己的结论。然而,你没有足够的时间重新发明一切。怎样才能达到平衡状态呢?这里给出我的拙见,它指导我编写出了这本新颖的数学书。
用上你训练有素的左脑
本书是为有经验的程序员或在工作中热衷于学习编程的人设计的。为程序员读者写关于数学的内容是极好的,因为如果你会写代码,就已经训练了你的分析性左脑。我认为学习数学的最好方法是借助高级编程语言,并且预测在不远的将来,这将是数学课堂的常态。
对于像你这样的程序员,可以用几种具体的方法来很好地学习数学。我在这里将其列举出来不仅是为了奉承你,也是为了提醒你已经具备了哪些技能,可以在数学学习中利用起来。
当学习编程时,第一个痛苦的教训就是,不能像写简单的英文一样编写代码。如果你给朋友写的纸条有少许拼写或语法错误,他们可能还能理解你想说的内容,但代码中的任何语法错误或拼写错误都会导致程序运行失败。在某些语言中,即使在原本正确的语句末尾漏掉一个分号,也会导致程序无法运行。作为另一个例子,看一下下面这两个语句。
x = 5
5 = x
可以把二者都解读为符号 x 的值是 5。但这在 Python 中并不准确,事实上,只有第一条可以如此解读。Python 语句 x = 5 是一个指令,会把变量 x 的值设置为 5。但是,不能将数字 5 设置为具有 x 的值。这可能看起来有些咬文嚼字,但你需要知道这一点才能写出正确的程序。
另一个困扰新手程序员(有经验的程序员也一样)的问题是引用相等。如果定义了一个新的 Python 类,并创建了它的两个相同的实例,那么它们是不相等的。
>>> class A(): pass
...
>>> A() == A()
False
你可能希望两个相同的表达式是相等的,但这显然不符合 Python 的规则。因为这两个表达式是 A 类的不同实例,所以它们是不相等的。
留意新的数学对象,它们看起来像你所熟知的对象,但行为方式却不一样。例如,如果字母 A 和 B 代表数,那么 A · B = B · A。但是,正如你将在第 5 章中学到的,如果 A 和 B 不是数,情况就不一定是这样。如果 A 和 B 是矩阵,那么积 A · B 和 B · A 就是不同的。事实上,它们之中甚至有可能只有一个是合法的,或者两者都不正确。
写代码的时候,仅仅写出语法正确的语句是不够的。语句所代表的思想需要是有意义的、合法的。如果写数学语句时也同样谨慎,你会更快发现错误。更棒的是,如果用代码写数学语句,计算机会帮你做辅助检查。
计算器在数学课上很常见,因为可以用来检查计算结果。虽然你需要知道在不借助计算器的情况下如何计算 6 乘以 7,但能通过计算器来确认你的答案正确也挺好。一旦掌握了数学概念,计算器还能帮你节省时间。比如在计算三角函数时,你想知道 3.141 59 / 6 是多少,这可以用计算器很容易地算出来,于是你可以把精力花在思考结果的意义上。计算器能做的事情越多,理论上应该越有帮助。
但计算器有时太复杂了,很难用。当我上高中的时候,老师要求买一个图形计算器。我买了一个 TI-84,它有大约 40 个按钮,每个按钮有两三种不同的模式。我只知道如何使用其中的 20 个左右。总之,这个工具使用起来相当烦琐。当我在小学一年级拿到第一台计算器时,情况也是一样的。虽然它只有大约 15 个按钮,但我不知道其中一些是干什么的。如果让我为学生们发明第一台计算器,我会把它做成如图 1-14 所示的样子。
图1-14 帮学生学习计数的计算器
这个计算器只有两个按钮:一个按钮可以将数值重置为 1,另一个按钮可以前进到下一个数。这样“只满足最基本需要”的工具,非常适合帮助孩子学习计数。(这个例子可能看起来很傻,但你确实可以买到这样的计算器!它们通常是机械式的,被当作计数器售卖。)
掌握了如何计数,你就会想练习写整数和做加法了。在这个学习阶段,需要给计算器再加几个按钮(见图 1-15)。
图1-15 能写整数和做加法的计算器
在这个阶段,不需要 −、× 或 ÷ 这样的按钮来妨碍你。当你做减法问题(如 5 − 2)时,仍然可以通过这个计算器检查答案(确认 3 + 2 = 5)。同样,你也可以通过累加来解决乘法问题。摸索完这个计算器后,就可以升级到一个能完成所有算术运算的计算器了。
我认为理想的计算器应该是可扩展的,这意味着可以按需添加更多的功能。例如,可以为你学习的每一个新的数学运算在计算器上添加一个新按钮。当学到代数的时候,也许除了数,还可以让它理解 x 或 y 等符号。当你学了微积分时,可以更进一步,使其理解和处理数学函数。
可以处理多种类型数据的可扩展计算器似乎遥不可及,但这正是高级编程语言提供的能力。Python 不仅自带算术运算、math 模块,还有众多可以随时引入的第三方数学库,让编程环境更加强大。因为 Python 是图灵完备的,所以你(原则上)可以计算任何可以计算的东西,需要的只是一台足够强大的计算机、一个足够巧妙的程序实现,或者两者兼备。
本书用可复用的 Python 代码来实现每个新的数学概念。自己动手实现可以很好地加深你对一个新概念的理解。最终,你的工具箱里会多出一个新工具。在自己尝试之后,如果愿意,也可以随时换上一个经过打磨的主流库。无论采用哪种方式,你构建或导入的新工具都为探索更复杂的概念奠定了基础。
我刚才所说的过程在编程里叫作抽象。例如,当你厌倦了重复计数时,就可以创建针对加法的抽象;当你厌倦了做重复的加法时,就可以创建针对乘法的抽象;以此类推。
在编程里的所有抽象方式中,可以延续到数学中的最重要的一种方式是函数。在 Python 中,函数是一种重复执行任务的方法,可以接收一个或多个输入,产生一个输出。例如:
def greet(name):
print("Hello %s!" % name)
这段代码可以让人用有表现力的简短代码发出多个问候语,比如:
>>> for name in ["John","Paul","George","Ringo"]:
... greet(name)
...
Hello John!
Hello Paul!
Hello George!
Hello Ringo!
这个函数很实用,但它不像数学函数。数学函数总是接收输入值,并总是返回没有副作用的输出值。
在编程中,我们把行为像数学函数的函数称为纯函数。例如,平方函数 f(x) = x2 接收一个数并返回这个数与自己的乘积。执行 f(3) 时,结果是 9。这并不意味着数3现在已经变成了 9,而是意味着 9 是函数 f 在输入为 3 时的相应输出。可以把这个平方函数想象成一台机器,它在输入槽中接收数,在输出槽中产生结果(数),如图 1-16 所示。
图1-16 函数就像一台有输入槽和输出槽的机器
这是一个简单实用的心智模型,整本书中都会用到它。我最喜欢它的一点是,你可以把函数当成一个对象来处理。在数学中,就像在 Python 中一样,函数是可以独立处理的数据,甚至可以将其传递给其他函数。
数学的抽象性会让人望而生畏。但是,就像在任何优秀软件中一样,引入抽象是有原因的:它可以帮助你组织和交流更宏大、更强有力的思想。当你掌握了这些思想并将其转化为代码时,就会开启更多令人兴奋的可能性。
如果你还没有做到这一点,我希望你已经相信,软件开发中有许多令人兴奋的数学应用。作为一名程序员,你已经有了正确的思维方式和工具来学习一些新的数学思想。本书中的思想丰富了我的职业生涯、提升了我的个人素养,希望对你也有所帮助。让我们开始吧!