经常看到有程序员思维的字眼,所以最近在学习编写一些小程序,感觉一下什么是程序员思维。
因为程序员思要用最简单的方法来达成结果,所以我这里简化成“电脑思维”,用它比较与人脑思维的异同。
一、题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
遇到这样的题目,如果不是很复杂的数,我们可以通过不断地尝试,将结果试出来,
电脑的处理方法是这样的:
import math for i in range(10000): #转化为整型值 x = int(math.sqrt(i + 100)) y = int(math.sqrt(i + 268)) if(x * x == i + 100) and (y * y == i + 268): print i
其实也是通过遍历的方法将结果试了出来,从这两点来说,人脑与电脑没有区别,不过电脑的处理速度N倍于人脑,所以很容易解决此类问题。
二、题目:输入三个整数x,y,z,请把这三个数由小到大输出。
初学编程的人也许是拿X与Y、Z分别比较,可是其实简单的编程思路是这样的:
l = [] for i in range(3): x = int(raw_input('integer:\n')) l.append(x) l.sort() print l
这里,并不需要将三个数每个进行对比,而是将三个数看成一个整体,然后再进行排序。这里其实和人脑思维没有两样,比如人脑要比较4,7,2,三个数,是将4,7,2看作整体来处理的。电脑引入了列表这个概念,将这三个数可以看成整体。
但是如果遇到复杂的数呢?54 85 52 48 33 15 36 97 54,如果要将前面9个数排序,人脑是怎么处理的呢?人脑知道9开头的数肯定是最大的,所以先找9开头的数,再找8开头的数,再找7开头的数。
那电脑如何处理呢?这就要看程序的源码才知道了,我们以Python的sort源码为例:
https://github.com/python/cpython/blob/master/Objects/listobject.c
算法说明,请看https://github.com/python/cpython/blob/master/Objects/listsort.txt
三、高阶函数
比如,我们要将[1,2,3]这个列表中的每个元素加上1,一般的思路是这样的:
a = [1,2,3] r = [] for each in a: r.append(each+1)
如果用map来写,就可以写成:
map(lambda x:x+1,[1,2,3])
要显示的话,前面加上list即可。
list(map(lambda x:x+1,[1,2,3]))
Map函数就是一种整体的思维,将[1,2,3]看作一个整体,而不再是单个的元素,这样就可以大大提高我们的效率,考虑事情的思维层级也会高出一些来。
在Python中,类似map能用到lambda表达式的“高级”函数,有reduce,filter等等。这种能够接爱一个函数作为参数的函数叫做“高阶函数”,是来自函数式编程的思想。
四、编程与数学
要通过程序生成一个上面的图形,如何生成呢?一般人的想法也许是先画一个星号,再画两个星号......
其实要解决这个问题,首先要将这个问题转化成数学问题,以最多5个星号为例:
第一行是2空格+1星号+2空格
第二行是1空格+3星号+1空格
第三行是0空格+5星号+0空格
这样,转化成数学问题之后,我们的解决方法就简单了。
五、题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。
下面是一个学员方法:
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6 765, 10946] [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 , 10946, 17711] 32.69
他首先将分母、分子的fibonacci数列求出来,然后再用列表取值的方法,将两个列表相除,最终相加得到结果,他的结果是正确的,不过写的代码足足有30行,其实这个问题不用10行代码就可以解决(用lamda更简单,可以只要5行):
a = 2.0 b = 1.0 s = 0 for n in range(1,21): s += a / b t = a a = a + b b = t print(t,a,b) print (s)
这里又牵涉到编程思维的问题,在第四点我讲了,要将编程问题转化为数学的问题,这位学员也做到了这一点,将问题转化成了数学的问题。不过他思考的点是将所有的数通过数学方法生成出来,而更简单的方法是应该思考题目中相领两个数的关系,只要知道了他们的关系,其他的重复工作交给程序就可以了,这也是我们要用程序解决问题的原因。
比如上题中:第一个数是2/1,第二个数是3/2,很显然他们的关系就是:a=a+b,b =a,不过编程时,为了不使b=a出现将a的新值赋给b的情况,所以引入了t。
六、求判断一个数是否为质数/素数
编程初学者的思路:将这个数n除以n-1,或者先将偶数排除在外,只处理100以内的奇数。
正确的思路:
一个数若可以进行因数分解,那么分解时得到的两个数一定是一个小于等于sqrt(n),一个大于等于sqrt(n),据此,上述代码中并不需要遍历到n-1,遍历到sqrt(n)即可。
还有一种方法:
首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;
证明:令x≥1,将大于等于5的自然数表示如下:
······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······
可以看到,不在6的倍数两侧,即6x两侧的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们一定不是素数,再除去6x本身,显然,素数要出现只可能出现在6x的相邻两侧。这里有个题外话,关于孪生素数,有兴趣的道友可以再另行了解一下,由于与我们主题无关,暂且跳过。这里要注意的一点是,在6的倍数相邻两侧并不是一定就是质数。
根据以上规律,判断质数可以6个为单元快进,即将方法(2)循环中i++步长加大为6,加快判断速度。
总结:其实有时碰到没有思路的问题,要多用草稿,多比划,就有思路了。