You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
15 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 加餐1 | 作为一名程序员,数学到底要多好?
你好,我是月影。
刚刚学完了可视化的数学篇,今天咱们放松一下,以我的个人经历来聊一聊,数学对我们程序员的重要性。
作为奇舞团团长和从事前端15年以上的“老人”我为团队面试过许多同学也和许多同学聊过前端或者程序员的职业发展方向。一般来说我面试的时候会要求面试者有一定的数学基础在聊关于职业发展的话题时我也会强调数学对于程序员成长的重要性。甚至在可视化这门课里面我也认为学习可视化的第一步是学好图形学相关的数学知识。
不过行业里也有些不同的声音有些人觉得除了部分特殊的领域大部分普通的编程领域不太需要数学知识。比如说在我们平时的Web开发项目中不论是前端还是后端似乎更多地是和产品与业务逻辑打交道比较少或几乎没有用到数学知识。甚至有些人认为程序员根本用不着学好数学特别是在前端这样偏UI层的领域数学更是没有用武之地。
当然,以上这些认为数学不重要的想法,我都可以理解,曾经我自己也没有意识到数学和编程有什么必然的联系。而且,我当年在学校学习的时候,数学也学得很马虎,基础也不是那么好。不过后来,我个人的一段经历,让我很早就意识到数学对编程的重要性,而这个认知,对我后来的职业发展有着非常重要的影响。所以,我想在这里和你分享一些我个人成长中的经历和收获,希望能对你有些帮助。
## 实习面试的两个问题
2003年因为朋友的推荐我获得了微软亚洲研究院MSRA访问学生的面试机会当时的面试官是浙江大学的刘利刚博士他也是我后来的实习导师。那时他正在MSRA做访问学者。
在这之前,我没有任何面试经验,在学校里面,我的学习成绩也一般,只是对编程比较感兴趣,自己做过一些小项目。我不知道会被面试什么问题,所以也没特意准备。见到了利刚博士之后,他并没有问我任何有关编程的问题,而是问了我两个数学问题,这两个问题让我至今仍记忆犹新。
第一个问题是这样的:
> 已知ABC是三个不同的数字且能使以下等式成立求A、B、C分别是多少。
![](https://static001.geekbang.org/resource/image/f5/3f/f57c583ec3134c974fyy55a08125c23f.jpeg)
求这道题的答案并不是很难,但是花多久的时间能得出答案却是一个问题。我当时回答出这个问题,大概只用了**不到10秒钟**。你可以先试着解一下看看你能在10秒内给出这个问题的答案吗
其实,利刚博士出这道题,主要在考察我的**数感**。啥是数感呢?这不是指一个人具备了多么高深的数学知识,而是指他对数字的一种直觉以及洞察力。
我在解决这个问题的时候,完全是脱口而出答案,我甚至都没有意识到自己是怎么得出来的。但是,当我一下说出答案之后,再回想为什么才反应过来,这道题其实是有规律的。
![](https://static001.geekbang.org/resource/image/84/2f/8432ccabd92ec9yy30bf1c820121252f.jpeg)
你仔细看中间这一列应该能一下子得出A的值是9。然后再看第一列就能得到C的值是4最后B的值自然就是5了。所以答案就是A = 9、B = 5、C = 4。
那面试为啥要考察数感呢?利刚博士是这么给我解释的:数感好表示学习和理解能力强,因为访问学生要做的工作内容就是图形学的基础研究,一些知识肯定是要现学的,这需要有比较强的学习能力和理解力,所以作为数学基础的数感就很重要了。正是通过这个问题,我认识到了数感的重要性。
好了,接下来我们接着来看第二个问题。
> 给你一个天平和一个物体让你设计一些砝码无论这个物体的重量是在1100克之间的任何一个整数克数都能用这些砝码称量出来并且砝码的数量要尽可能少你最少需要几个砝码呢
乍一看,这个问题似乎与计算机和编程完全不沾边,但实际上这个问题涉及基础的**数的进制原理**。为什么说这个天平称重涉及数的进制原理呢?
因为我们知道天平一般来说标准的用法是左边托盘放物体右边托盘放砝码。那如果我们要称重的物体在1100克之间还要设计尽可能少的砝码最优解肯定是称某个克数的砝码组合是唯一的这样最省砝码。
怎么理解呢?我举个例子。
假如说现在我们有3个砝码分别为A砝码1克、B砝码2克、C砝码也是2克显然这3个砝码可以称15克的物体。如果物体是1克的话那么用A砝码就行如果物体是2克的话有两种方法用B砝码或者用C砝码如果物体是3克的话也有两种方法用A+B或A+C砝码如果物体是4克的话用B+C砝码如果物体是5克的话用A+B+C砝码。
但是我们看到在物体是2克和3克的时候分别有两种砝码组合对应的称量方法。如果我们把一种砝码组合作为一种编码再把一种物体克数作为一个状态的话那么重复的编码就只表示同一种状态这就属于浪费。显然更好的解决办法是用最少的编码组合表示尽可能多的状态。甚至我们应该做到一种编码唯一对应一种状态这样才是最优的。
所以呢我们应该把C砝码改为4克这样一来3个砝码就可以称出17克的物体而且没有任何两种编码表示同一种状态这就是最优的。我把具体的称量方法总结出一张表列在了下面。
![](https://static001.geekbang.org/resource/image/0b/b5/0b5fd71ddfafdf0ec00b9c59cc2930b5.jpeg)
看到这里聪明的同学应该已经知道这一题的答案了。实际上我们将砝码被使用记为1将砝码不被使用记为0那这个问题就等价于用多少位二进制数可以表示不大于100的正整数因此答案自然是7位也就是说砝码需要7个重量分别是1克、2克、4克、8克、16克、32克和64克。
所以你看,这个问题表面上是天平问题,实际上牵扯到数的进制表示,或者说是编码,这显然是一个计算机问题。这其实也是利刚博士问我这个问题的真正目的,它同时考查了我关于数学模型的抽象能力,以及对计算机基础知识的理解程度。
顺便再说一下,这个问题如果允许将砝码放在天平左侧托盘中,那么有一个技巧可以让用到的砝码数量更少,你能想到该怎么做吗?如果你想到了,可以在留言里分享你的答案。
## 我的图形学实习经历
回答出这两个问题之后,我通过了面试,来到微软亚洲研究院实习。我的课题是图形学基础研究,恰好是和三角剖分有关,具体来说是简单多边形的相容三角剖分。
什么是简单多边形的相容三角剖分呢?简单来说,就是将两个简单多边形剖分成同样数量的三角形,同时还需要保证每个三角形的顶点能够一一对应。所谓一一对应,就是给两个多边形的顶点进行编号之后,它们中每一对三角形顶点的编号都相同。
如果两个简单多边形的边数相同,在不允许添加内部点的情况下,并不总能构成相容三角剖分。比如下图中的两个六边形,左边三条虚线构成三角形,而右边的三条虚线却相交于一个点,所以对这两个图形来说,如果我们不添加内部点,就不存在相容三角剖分了。
[![](https://static001.geekbang.org/resource/image/3a/fb/3aceba1a5f298aee4a187209e8a10afb.jpg)](https://www.sciencedirect.com/science/article/pii/0925772193900285)
如果我们允许在图形内部添加点进行三角剖分,就可以得到相容三角剖分了,剖分后的效果如下图:
![](https://static001.geekbang.org/resource/image/b5/5a/b596d3964bd48084e9c00f5b1d93c65a.jpg)
而我当时的工作主要是研究如何对多边形进行快速相容三角剖分。之所以研究相容三角剖分,是因为通过相容三角剖分可以生成拓扑结构相同的三角网格,而拓扑结构相同的三角网格是实现物体变形特效的基础。比如说,我们可以用相容三角剖分实现人物的“变脸”特效等等。
[![](https://static001.geekbang.org/resource/image/9d/70/9da642f984cdc8c90a9845f34873cc70.jpg)](https://mappingignorance.org/2018/02/21/triangulations-face-morphing/)
大体上,我的研究就是围绕相容三角剖分涉及的算法,因为它们都比较复杂,这里我就不多说了。接下来,我主要介绍一下我的工作中遇到的问题。因为涉及图形学的基础内容,自然会有一些基础的图形学计算,比如计算点到直线和线段的距离,计算边的切线和法线,判断线段的关系,绘制圆锥曲线等等。
## 我实习的第一个任务
在我刚开始实习的时候,第一个任务就是要计算点到直线和线段的距离。
不过,一开始我在求点到直线的距离的时候,是先写出直线的两点式代数方程,然后求点与直线的垂线方程,接着将两个方程联立求交点,最后再求出点与交点的距离。
这么做当然是可以求出结果来的但是计算过程可以说是相当繁琐了而且它还有缺陷。缺陷究竟是什么呢我们知道用直线方程求垂线的时候要用到点斜式但是点斜式的斜率在直线垂直于X轴时会有斜率计算出来是无穷大的问题。这种特殊情况还需要特殊处理就更增加了我计算的复杂度。
所以,当时我花了一天时间才把“求点到直线和线段距离的问题”用代数方程解决。但是,第二天给利刚博士交差的时候,就被他批评了一顿,他问我为什么要用代数方程去做这个问题,如果用向量来做,根本就是分分钟的事儿。
如果你认真学了数学篇的课程,应该也已经知道,用向量解决这个问题的确非常简单。因为向量叉积的几何意义就是平行四边形的面积,在用向量叉积除以底边就是高,也就是点到向量所在直线的距离了。而我当时并没有想起可以用向量来解决这类问题,所以才走了弯路。
经过这次教训,我深刻意识到**选择正确数学工具,能够把看似非常复杂的问题转化为简简单单问题,从而顺利解决**。这也是为什么数学对于程序员来说非常重要。这个教训也是我在MSRA实习中最重要的收获。
## 两次数学实践
大约4个多月后我就结束了MSRA的实习回到了学校。毕业后我去了一家深圳的软件公司真正地成为了一名程序员。后来更是在机缘巧合下接触到了前端也一直成长到今天。
在成长的过程中,我始终牢记:用数学思想和意识去解决工作中的问题。你别说,还真被我遇到了两个事儿。接下来,我就和你说说我印象最深刻的这两个案例。
第一个案例是我在08年到百度时遇到的。当时某个产品中有一个绘制椭圆的需求负责开发的工程师是使用椭圆的代数方程来计算的。因为代数方程涉及开平方的问题所以开发人员还要根据象限来判断正负号这会非常麻烦。
![](https://static001.geekbang.org/resource/image/ea/54/eafdc7f9f5798c7041c2c5623b23d054.jpeg "椭圆代数方程涉及开根号")
现在你学习了数学篇的课程,应该知道用椭圆的参数方程来解决,根本不会涉及开根号和象限判断问题,操作起来也会简单很多。
![](https://static001.geekbang.org/resource/image/95/7a/95832de155b7bb82f9ed6682fc6de17a.jpeg "椭圆的参数方程")
另一个案例是我在360搜索的一个运营活动中遇到的。当时需要我们实现一个Canvas2D的图片特效效果如下
![](https://static001.geekbang.org/resource/image/96/60/965a77b94f7f9608145e3a0041a88f60.gif)
具体要求是在一张静态图片上,选择若干个运动区域,比如示意图中的猫的耳朵、眼睛、鼻子区域,然后对区域做这样逆时针旋转的运动,最终将它变为动图。
这其实是一个Canvas2d的特效。最初的时候我们的工程师是使用代数方法来解决的具体的方法如下
![](https://static001.geekbang.org/resource/image/e4/1e/e420d7c88ab0f83bab3b198ff2afa81e.jpg)
代数方法虽然能解决问题但是会有4个缺陷
1. 求角需要计算反正切,性能差;
2. 计算中需要开平方,要判断符号;
3. 求反正切有无穷大问题,需要特殊处理;
4. 有重复的计算量,进一步消耗性能。
不过,在我采用了向量法进行优化之后,这个动图的性能提升了数倍。具体算法如下图:
![](https://static001.geekbang.org/resource/image/3f/13/3f6aff92c3e441f6fe689726a9d02f13.jpg)
这两个案例其实共同说明一个道理:**学会选择合适的数学工具,才能用最优的方式轻松解决问题。**
## 小结
以上就是我和数学相关的个人经历了。总的来说,我收获到最重要的经验就是,要学会选择合适的数学工具来解决问题。当你选对工具之后,那些看似复杂的问题,可能会变得无比简单,从而被迅速解决。所以我们要重视数学基础的积累,锻炼自己的数感,拓宽知识面,以数学思维来思考计算机问题。
当然,我也不是说,程序员必须要有多么高深的数学知识。你看我们专栏中需要的数学知识,基本上就是一些高中数学知识和一部分基础的线性代数知识。但是数感、数学思维锻炼还是很重要的,这些锻炼越多,程序员的逻辑能力、抽象能力也会得到提高。更重要的是,通过锻炼我们能形成用数学思维思考问题的习惯,这样才能迅速找到最合适解决某类问题的数学工具,从而提升我们的技术能力。
## 小试牛刀
最后我再留两道思考题,来锻炼一下你的数感和数学思维能力。
1. 我们知道简单多边形和复杂多边形区别是,是否有非相邻的边相交。那为了判断一个多边形是不是简单多边形,我们可以实现一个函数,来判断两个线段是否相交。你能实现这个函数吗?
![](https://static001.geekbang.org/resource/image/90/4c/90d19b7614fd4a83c6eeb8224d4f8d4c.jpg "左侧ab、cd线段相交右侧ab、cd线段不相交")
2. 假如你手里有5个硬币已知随机抛一次之后有的硬币正面朝上有的硬币反面朝上。请问随机抛一次有3个或3个以上硬币正面朝上的概率是多少
欢迎你在留言说说数学对你的影响,也欢迎你和我分享关于数学方面的疑惑,我们一起探讨。