3.5 练习

仅仅知道了函数的知识是远远不够的,必须要勤练习、多敲代码,才能有体会,才能熟练使用。所以,这里专门设立一节,带领读者做几个练习。

首先声明,以下对每个问题的解决方案,不一定是最优的。如果读者有更好的解决方案,可以分享(到我的网站www.itdiffer.com能够找到各种联系我的方法)。

读者完成下面的练习,请遵守如下规则(全看你的自我控制能力了,自控能力强的胜出,不要欺骗哦,因为上帝在看着你呢):

  • 先根据自己的设想写下代码,然后运行调试,检查得到的结果是否正确。
  • 也给出了参考代码,但是,参考代码并不是最终结果。
  • 可以在上述基础上对代码进行完善。
  • 如果读者愿意,可以将代码提交到github上跟大家分享。

3.5.1 解一元二次方程

解一元二次方程是初中数学中的基本知识,一般来讲解法有公式法、因式分解法等。读者可以根据自己的理解,写一段求解一元二次方程的程序。

最简单的思路就是用公式法求解,这是普适法则。

古巴比伦留下的陶片显示,在大约公元前2000年(2000 BC)古巴比伦的数学家就能解一元二次方程了。在大约公元前480年,中国人已经使用配方法求得了二次方程的正根,但是并没有提出通用的求解方法。公元前300年左右,欧几里得提出了一种更抽象的几何方法求解二次方程。

7世纪印度的婆罗摩笈多(Brahmagupta)是第一位懂得使用代数方程的人,同时容许方程有正负数的根。

11世纪阿拉伯的花拉子密独立地发展了一套公式以求方程的正数解。亚伯拉罕·巴希亚(亦以拉丁文名字萨瓦索达著称)在他的著作《Liber embadorum》中,首次将完整的一元二次方程解法传入欧洲。(源自《维基百科》)

参考代码:

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. """
  4. solving a quadratic equation
  5. """
  6.  
  7. from __future__ import division
  8. import math
  9.  
  10. def quadratic_equation(a,b,c):
  11. delta = b * b 4 *a * c
  12. if delta < 0:
  13. return False
  14. elif delta == 0:
  15. return -(b / (2 * a))
  16. else:
  17. sqrt_delta = math.sqrt(delta)
  18. x1 = (-b + sqrt_delta) / (2 * a)
  19. x2 = (-b - sqrt_delta) / (2 * a)
  20. return x1, x2
  21.  
  22. if __name__ == "__main__":
  23. print "a quadratic equation: x^2 + 2x + 1 = 0"
  24. coefficients = (1, 2, 1)
  25. roots = quadratic_equation(*coefficients)
  26. if roots:
  27. print "the result is:", roots
  28. else:
  29. print "this equation has no solution."

保存为20501.py,并运行之:

  1. $ python 20501.py
  2. a quadratic equation: x^2 + 2x + 1 = 0
  3. the result is: -1.0

能够正常运行,求解方程。

但是,如果再认真思考,会发现上述代码是有很大改进空间的:

1.如果不小心将第一个系数(a)的值输入了0,程序肯定会报错。如何避免之?要记住,任何人的输入都是不可靠的。

2.结果貌似只能是小数,这在某些情况下是近似值,能不能得到以分数形式表示的精确结果呢?

3.复数,Python是可以表示复数的,如果delta<0,是不是写成复数更好。

读者是否还有其他改进呢?你能不能进行改进,然后跟我和其他朋友一起来分享你的成就呢?

至少要完成上述改进,可能需要其他有关Python的知识,甚至于前面没有介绍。这都不要紧,掌握了基本知识之后,在编程的过程中,就要不断发挥google搜索的优势,让它帮助你找寻完成任务的工具。

Python是一个开发的语言,很多大牛人都写了一些工具让别人使用,减轻了后人的劳动负担,这就是所谓的第三方模块。虽然Python中已经有一些“自带电池”,即默认安装,比如上面程序中用到的math,但是我们还嫌不够。于是有很多第三方的模块来专门解决某个问题。比如,这个解方程问题就可以使用SymPy来解决,当然NumPy也是非常强悍的工具。

3.5.2 统计考试成绩

每次考试之后,教师都要统计考试成绩,一般包括:平均分,以及对所有人按成绩从高到低排队,谁成绩最好,谁成绩最差等。下面的任务就是读者转动脑筋,思考如何用程序实现考试成绩统计。为了简化,以字典形式表示考试成绩记录,例如,{"zhangsan":90,"lisi":78,"wangermazi":39},当然,也许不止这三项,每个老师所处理的内容都稍有不同,因此字典里的键值对也不一样。

怎么做?

有几种可能要考虑到:

  • 最高分或者最低分,可能有人并列。
  • 要实现不同长度的字典作为输入值。
  • 输出结果中,除了平均分,其他的都要有姓名和分数两项,否则都匿名了,怎么刺激学渣、表扬学霸呢?不管你是学渣还是学霸,都能学好Python。请思考后敲代码调试你的程序,调试之后再阅读下文。

参考代码:

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. """
  4. 统计考试成绩
  5. """
  6. from __future__ import division
  7.  
  8. def average_score(scores):
  9. """
  10. 统计平均分.
  11. """
  12. score_values = scores.values()
  13. sum_scores = sum(score_values)
  14. average = sum_scores / len(score_values)
  15. return average
  16.  
  17. def sorted_score(scores):
  18. """
  19. 对成绩从高到低排队.
  20. """
  21. score_lst = [(scores[k], k) for k in scores]
  22. sort_lst = sorted(score_lst, reverse=True)
  23. return [(i[1], i[0]) for i in sort_lst]
  24.  
  25. def max_score(scores):
  26. """
  27. 成绩最高的姓名和分数.
  28. """
  29. lst = sorted_score(scores) #引用分数排序的函数sorted_score
  30. max_score = lst[0][1]
  31. return [(i[0], i[1]) for i in lst if i[1] == max_score]
  32.  
  33. def min_score(scores):
  34. """
  35. 成绩最低的姓名和分数.
  36. """
  37. lst = sorted_score(scores)
  38. min_score = lst[len(lst)-1][1]
  39. return [(i[0],i[1]) for i in lst if i[1]==min_score]
  40.  
  41. if __name__ == "__main__":
  42. examine_scores = {"google":98, "facebook":99, "baidu":52, "alibaba":80, "yahoo":49, "IBM":70, "android":76, "apple":99, "amazon":99}
  43.  
  44. ave = average_score(examine_scores)
  45. print "the average score is: ", ave #平均分
  46.  
  47. sor = sorted_score(examine_scores)
  48. print "list of the scores: ",sor #成绩表
  49.  
  50. xueba = max_score(examine_scores)
  51. print "Xueba is: ",xueba #学霸们
  52.  
  53. xuezha = min_score(examine_scores)
  54. print "Xuzha is: ",xuezha #学渣们

保存为20502.py,然后运行:

  1. $ python 20502.py
  2. the average score is: 80.2222222222
  3. list of the scores: [('facebook', 99), ('apple', 99), ('amazon', 99), ('google', 98), ('alibaba', 80), ('android', 76), ('IBM', 70), ('baidu', 52), ('yahoo', 49)]
  4. Xueba is: [('facebook', 99), ('apple', 99), ('amazon', 99)]
  5. Xuzha is: [('yahoo', 49)]

貌似结果还不错。不过,还有改进余地,看看现实就感觉不怎么友好了。能不能优化一下?当然,里面的函数也不一定是最好的方法,你也可以修改优化。

3.5.3 找质数

这是一个比较常见的题目。我们姑且将范围缩小一下,找出100以内的素数吧。

还是按照前面的惯例,读者先做,然后我提供参考代码,最后优化。

质数(Prime number),又称素数,指在大于1的自然数中,除了1和此整数自身外,无法被其他自然数整除的数(也可定义为只有1和本身两个因数的数)。

哥德巴赫猜想是数论中存在最久的未解问题之一。这个猜想最早出现在1742年普鲁士人克里斯蒂安·哥德巴赫与瑞士数学家莱昂哈德·欧拉的通信中。可以用现代的数学语言陈述哥德巴赫猜想为:“任一大于2的偶数,都可表示成两个质数之和。”。哥德巴赫猜想在提出后的很长一段时间内毫无进展,直到20世纪20年代,数学家从组合数学与解析数论两方面分别提出了解决的思路,并在其后的半个世纪里取得了一系列突破。目前最好的结果是陈景润在1973年发表的陈氏定理(也被称为“1+2”)。(源自《维基百科》)

对这个练习,我的思路是先做一个函数,用它来判断某个整数是否是质数,然后循环即可。

参考代码:

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. """
  4. 寻找质数
  5. """
  6.  
  7. import math
  8.  
  9. def is_prime(n):
  10. """
  11. 判断一个数是否是质数
  12. """
  13. if n <= 1:
  14. return False
  15. for i in range(2, int(math.sqrt(n) + 1)):
  16. if n % i == 0:
  17. return False
  18. return True
  19.  
  20. if __name__ == "__main__":
  21. primes = [i for i in range(2,100) if is_prime(i)] #从2开始,1显然不是质数
  22. print primes

代码保存后运行:

  1. $ python 20503.py
  2. [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

打印出了100以内的质数。

你或许也发现了这个程序需要进一步优化的地方,另外,关于判断质数的方法还有好多种,读者可以自己创造或者从网上搜索一些,拓展思路。

3.5.4 编写函数的注意事项

编写函数,在开发实践中是非常必要和常见的,一般情况,你写的函数应该是:

  • 尽量不要使用全局变量。
  • 如果参数是可变类型数据,则在函数内不要修改它。
  • 每个函数的功能和目标要单纯,不要试图一个函数做很多事情。
  • 函数的代码行数尽量少。
  • 函数的独立性越强越好,不要跟其他的外部东西产生关联。