高中的时候,老师给我们出了一道题:
四个圆最多可以把平面分成多少个部分?
我们这些学生拿到题,都开始拼命在纸上画圆,一个,两个,三个……各种可能的稀奇古怪的位置都要考虑到。
等我们费尽力气算出是14个部分之后,老师又问:那么10个圆呢?
这下我们都傻了,10个圆,这要怎么画啊。
不过也有善于小聪明的学生,试着推算:一个圆的话是2,两个圆是4,三个圆是8,可是四个圆怎么不是16呢……看来行不通。
最后,老师告诉我们,大家要学会推论的思考方法:两个圆最多有两个交点,那么假设平面上已经有n个圆,那么当我们画第n+1个圆的时候,它最多和已有的n个圆有2n个不同的交点。这些点把第n+1个圆划分成了2n段弧,而因为每一段弧把其所在的空间分割成了2部分,所以每一段弧意味着增加了一个部分。总结起来就是:当平面上有n个圆的时候,再画一个圆最多可以增加2n个部分。
当老师给我们讲解了推论方法之后,我才明白,原来这个问题这么简单!
当我接触了计算机,接触了编程之后,我还是总会试着用这种数学的方法思考问题;——这是没有任何错误的,但是我最容易犯的错误就是忽视计算机很多时候可以解决我们无法或者难以通过“公式”“推论”所解决的问题:因为计算机拥有无比快速的大脑。
有一道ACM的问题(http://acm.uva.es/p/v1/136.html),描述如下:
************************************************************************
如果一个数的质因子只包含2,3,5,那么它就被称为“Ugly Number”。下面是Ugly Number从小到大排列的序列:
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, ...
(按照习惯,我们这里把1也算作Ugly Number)
那么现在,请你写程序求出第1500个Ugly Number。
************************************************************************
不知道你看到这个题目的时候有什么思路呢?
(如果决定自己想一想,请不要往下看^_^)
直接看结果请看下面……
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
我最开始看到这个题目,开始时脑海中的想法是:必须推算出Ugly Number的公式呀!
但是我左推推,右推推,没有半点头绪。天啊,看来这个题目需要比较高的数学知识!——我想。
好吧,既然公式推不出来,那么就只好一个一个检查了——把每个数都分解因数,
如果它的质因数只包含2,3,5,那么就把它们加入一个队列就是了!
但是仔细考虑一下,就发现这样做太耗时了(ACM竞赛对程序执行时间有限制,例如1秒),又是分解又是判断质数,到了1500难免要超时。
那么,还有没有什么好方法呢?
答案是肯定的。既然从自然数中“筛选”Ugly Number比较困难,反过来我们逐个生成Ugly Number如何?
我们假设一个数n是Ugly Number,那么也就是说
n = 2^x * 3^y * 5^z,其中x, y, z >= 0。
那么很容易看出2n,3n,5n也都是Ugly Number。
既然这样,那么我们就从1开始,不断地乘以2,3,5,把每个得到的数按大小加入到Ugly Number的队列中;并不断对Ugly Number对列中的下一个数进行乘2,3,5操作,这样我们不久可以得到整个Ugly Number的序列了吗?
很简单,不是吗?
(这里要注意一点,就是如何确定这些生成的Ugly Number的顺序问题。——也即,你生成了一个Ugly Number,并把它加入到队尾的时候,你如何保证它和前一个Ugly Number之间没有别的Ugly Number?这个问题就留给喜欢研究的朋友了:P)
看来,算法虽然和数学有着很深的渊源,但是利用算法来解决问题与利用纸笔+数学的头脑来解决问题,有时候方向还是不同的。■