自信息技术新课标以Python语言作为核心编程语言推荐以来,Python编程与算法教学成为落实新课标课程目标的基础。如何在中小学Python编程与算法教学中,落实课标精神,围绕“算法以及基于算法的问题求解”这一核心问题,依据基础教育阶段学生特点把握适当深度和广度,通过算法学习和实践,为深入体会并应用计算思维(信息技术的思想、方法和手段)解决问题提供坚实基础,实现“运用计算思维解决问题”能力培养的课程目标,是广大信息技术学科教师共同关注的话题和主要研究点。

存在的问题和困难

作为普通高中信息技术新课标的重要内容,中小学Python编程与算法在教学中目前存在如下问题和困难:

第一,教学内容选择的困难。高校中计算机及其相关专业关于算法的教学已经形成非常完备的体系,但由于中小学生与大学生在年龄和认知上存在差异,学科基础也不同,所以基础教育阶段的算法及问题求解教学,不能是高校教学内容的简单简化,也不能只将其作为大学算法课程的前序准备而不涉猎本质问题。上述状况使得中小学算法教学内容在深度和广度方面普遍呈“高不成、低不就”的窘态。

第二,教学实践层次的困难。首先,作为计算机科学的重要分支,算法与数学、数据结构、编程语言特性密切关联,已经成为一个的完备的知识体系,单纯讲算法本身很难实施。其次,Python语言封装了很多基础算法的实现,而且涉及交错复杂的概念(如数据类型、抽象数据类型),究竟是讲算法的底层方面还是讲高阶应用难以取舍。再次,如何处理算法的直觉描述(非形式化描述)、流程图、伪代码(半形式化描述)和编程实现(形式化描述)之间的关系,也是教学实践的难点。

第三,案例选择的困难。中小学阶段的算法教学是为编程教学服务的,所以必须服从编程教学的总目标:运用计算思维解决问题。但限于中小学生的认知水平和知识深度,教学案例的求解流程和问题深度很难把握,所以选择适当的案例成为实施Python编程与算法教学的关键。

遵循原则

第一,算法应该源自学生熟悉的应用情境。

第二,在算法的选择上,要把握好高阶算法和低阶算法之间的平衡。

第三,算法的目标、直观思想以及逐步导致形式化描述的演化过程是高中(包括小学、初中)算法教学的重要部分,教学中应避免直接提出一般化、形式化的算法描述。

第四,算法中所涉及的核心思想、形式化或半形式化表示、算法推导的数学及背景知识应在学生的知识范围内,或略微超过学生的知识范围。

第五,算法的编程实现上,原则上不涉及较复杂或较具技巧性编程,较复杂算法可不做实际的编程实现或只做Python-like伪代码实现,但算法作为教学案例,必须先讲清楚其背后的关键思想。

第六,高阶算法所解决的问题应具有典型性、时代性、选择性、普适性和适用性,并且对学生的后续学习或其他学科的学习有启示作用。

第七,在时间和条件都允许的情况下,应对解决同一类问题的、基于不同策略所实现的算法做性能比对,并挑选出较优算法。

实施策略

1.明晰Python编程知识进阶,合理设计教学方法

在Python编程与算法教学中,要始终贯彻“需求导向、问题解决、做中学”的定位,明确学会使用函数比掌握编程技巧更重要。在设计编程案例时,要掌握输入/输出函数,能够灵活运用非数值数据类型,包括字符串、列表和字典;要了解Python语言异常灵活的循环及控制结构;鼓励教师将Python当作实现版的“伪代码”来使用,即使确实需要用伪代码,也建议使用Python-like风格;如果要使用Python的高级特性,编程案例应尽量依托Python语言特点,并以算法实现的需求适当渐次地引入。但笔者建议,非必要尽量不使用Pyt hon的高级特性。

2.熟悉算法策略,找准对应求解问题

算法策略是指在算法设计中所使用的问题求解的策略,是计算思维最直接、最具体的体现形式。算法策略的视角比实现算法时的具体编程实现方法在站位上要更高。

在中小学编程教育中,在算法教学里,常见的算法策略和它们用于处理的任务主要有:迭代(也称为循环)——用于处理重复性的任务;递归——用于完成迭代的一种高效方法;蛮力法——在没有更好的办法而且计算资源(时间和空间)允许的前提下,可以尝试采用的求解问题的方法;回溯——用于测试不可行的选择,目的在于尽可能排除这类选择。

案例解析

算法以及基于算法的问题求解是计算机科学的最重要的组成部分,也是信息技术新课程最基本、最核心的内容,算法教学中教学案例的选择是最重要的关键点。下面,笔者以斐波那契数列的问题解决路径为例,解析在上述原则和策略的指导下,如何实施Python编程和算法教学。

1.构建问题场景

斐波那契数列从0和1开始,之后的斐波那契数列系数就由之前的两数相加。高中数学必修五在讲解数列前n项和的课后资料中提到了斐波那契数列,作为数学知识生活化、发展学生抽象思维能力的拓展。

斐波那契数列(Fibonacci sequence)因为相邻两项的比无限趋近于黄金比等性质,又称黄金分割数列。它是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。假设一对刚出生的小兔子一个月后能长成大兔子,再过一个月便能生下一对小兔子,此后每个月生一对小兔子。如果不发生死亡,一年内逐月的小兔子对数是一组非常特殊的数字:1,1,2,3,5,8,13,21,34,55,89,144,……不难发现,从第三个数起,每个数都是前两个数之和,这个数列称为斐波那契数列。

在现代物理、准晶体结构、化学等领域,斐波那契数列都有直接的应用,斐波那契数列的通项公式表示如下图所示的公式(1)。

在算法教学中,斐波那契数列是最简单的递归定义函数,因此非常适合用来说明实现基本递归算法的方法。

2.简单递归实现算法

斐波那契数列的Python实现所需基本知识包括:使用if 语句实现简单迭代/循环、自定义函数、函数的递归定义。

斐波那契数列本身就是用递归形式定义的,Python是函数式编程语言,支持函数的递归定义,即函数体的内部包含对函数本身的调用。因此,公式(1)可以转换为一个合法定义的Python函数,并尝试让学生输出一些可很快通过人工验证的值(不要太大)(如下图)。

可以注意到,在计算最后一个值时是需要一点时间的。在上述的递归调用中,很多中间值要重复计算。如下图所示,在计算fib_1(4)时,fib_1(2)就重复计算了2次。显然,n的值越大,在计算fi b_1(n)的过程中,需要重复计算fib_1(k)(k<n)的次数就越多。

3.动态规划优化递归算法

为了优化斐波那契数列的递归实现,一个最简单的方法就是在每次计算中,将前面已经计算过的值“记忆”下来,不再重复计算。这种在计算过程中使用记忆的机制可以有效避免重复及浪费资源,是所谓动态规划方法的一个最简单的例子,动态规划就是指资源的动态再分配。动态规划实现需要涉及如下Python知识:Python字典、Python的Dict内建对象(一种特殊的Python字典)、对迭代器使用if语句。下图所示代码是一个带有记忆的版本。

作为对比,计算fib_2(20)会产生fib_2()的39次自调用;而若使用fib_1(),则计算fib_1(20)会产生21891次调用。

在Python中,任意对象只要定义了某种_next _()方法,就是一个迭代器。因此,Python中的容器类数据类型,如列表、元组、字典、集合、字符串等,都可以用于创建迭代器。有了迭代器的概念,迭代的概念就比较容易理解了:迭代就是从迭代器中依次抽取元素的过程。

4.装饰器优化递归算法

Python有一个内建的“装饰器”(decorator),可用于自动记忆任何函数。通过使用这个装饰器,计算斐波那契数列的函数可以更加简化。

以下实现使用Python的高级特性——Python装饰器语句。简单地讲,装饰器就是在不改变原来函数代码的情况下,给其增加一些附加功能,如上面记忆函数中间值的功能。

下图代码实现的函数fib_3()与fib_1()几乎完全相同,但使用了装饰器@functools.1ru_cache(),其中maxsize属性用来指示在函数的最近调用中应该缓冲多少次,设置maxsize=None表示对次数不做限制。

5.使用元组拆包优化递归算法

元组拆包就是将元组中的元素分别赋给变量。利用Python的元组拆包技巧可给出斐波那契函数的一个性能更高的实现(如下图)。

在本例中,语句“ last ,next=next,last+next”实现将last的值更新为next的值,而将next的值更新为“last+next”的值,就是一个元组拆包。使用拆包技巧,计算fib_4()只需循环n-1次!

6.斐波那契数列生成器-循环结构

到目前为止,所写的函数只能产生特定位置上的斐波那契数的值,如果要一次性将到某个值为止的整个斐波那契数列都输出,可以使用一个简单循环(如下图)。

7.寻找更多优化策略

教师可以引导学生继续进行拓展练习,如探索斐波那契数列利用矩阵非递归化优化,也可以探索斐波那契数列与黄金分割的关系,或者使用动态规划优化函数fib _up_to()等,提升学生学习兴趣,培养精益求精探索的创新精神。

本文作者:

于晓雅

北京教育学院人工智能和创客教育研究中心

樊磊

首都师范大学教育学院

文章刊登于《中国信息技术教育》2022年第20期

欢迎订阅

纸质版订阅

电子版订阅