金凡 陈凯
记事本里的大兔子和小兔子
假设有一对小兔子,它们用一个月时间长大变成大兔子,然后再过一个月,生下一对小兔子,生下的一对小兔子也用一个月时间长大,再过一个月后又生下一对小兔子,并且假设之后每对大兔子每个月都要生一对小兔子,那么——这里并不是问几个月后总共有几对兔子,那其实就是求斐波那契数列了,这里的问题是,如果生下的每对小兔子都在大兔子的右侧成长并且绝不随便和大兔子交换位置——经过特定时间后,大兔子和小兔子的序列会是怎样的?顺便说一下,假设有谁特别纠结于兔子近亲繁殖的伦理问题,也可以把问题情境中的兔子改成能够单姓繁殖的大理石纹螯虾。
这个问题在纸上就能演算出来,假设一对小兔子用符号a表示,一对大兔子用符号b表示(把现实中的事物用符号来表示,是为了其后计算上的方便,如何合理设定符号,其实和计算思维技能有关),一开始的时候,人们看到的就是a,一个月后a变成了b,再一个月后,b生下a,因为a总是在b的右侧成长,所以人们看到的就是ba,再下一个月,因为a变成b,b变成ba,所以人们看到的就是bab,以此类推,再往后一个月,看到的就是babba,再往后是bababbab。可以发现,从第二个月开始,序列的开头是不变化的,而尾巴在不断地变长:
a
b
ba
bab
babba
babbabab
在记事本中,可以通过不停地进行三步“全部替换”功能来自动生成序列:先把a全部替换成t,再把b全部替换成ba,最后把t全部替换成a就可以了。不用动太多脑筋,反复做这三步“全部替换”就能使得符号序列越来越长。这就体现出用迭代来实现自动化奥妙了。
画图软件里的兔子序列
类似的迭代思想可以用到其他地方,例如,可以用画图软件来生成兔子序列,方法很有趣。
首先,画一个深色的框,如图1。
然后,随便画条竖线,把框一分为二(分的位置不用太在意),在右侧部分填上比较浅的颜色,如图2。
接着,在深色框内再画竖线,这条竖线要比刚才的稍微长一些,然后同样在右侧部分填上比较浅的颜色,如图3。
再接着,把当前第二短的竖线的右侧框的颜色变成深色,如图4。
这个工作可以继续下去,注意分割的竖线越来越长,每一次新分割出来的框,都将右侧填上浅色,然后不要忘记,将当前第二长的竖线的右侧框填上深色,反复进行,很快就能得到兔子序列了,如上页图5。设深色为b,浅色为a,从左往右读,就是兔子序列。
电子表格中的兔子序列
在电子表格中,可以有很多种方法来生成兔子序列,这里仍然借用在画图软件中所使用的方法,将迭代过程变得更自动一些。
第一步:在第一行第一列的单元格中写入b,然后在右侧单元格中写入公式“=b”,用拖曳功能,在第一行填充若干b,如图6。
第二步:在中间某处相邻的两个单元格中连续填入1和a,于是右侧部分就自动全部变成a,如图7。
第三步:在铺满b的序列中间相邻两个单元格中填入2和a,于是2的右侧部分变成a,如图8。
第四步:找到比当前序列中最大的数字小1的数字,在这里正是2减1得到1,在这个数字右侧铺满a的序列最左侧单元格中填入b,于是跟随其后的符号都变成了b,如图9。
第五步:从第三步开始重复此过程,但填写的数字要逐渐变大。于是就逐渐生成兔子序列了。注意在读数据的时候,连续的符号要当成一个符号来读。这个过程做熟练以后,可以用相当快的速度生成兔子序列。
在Scratch里用兔子序列来画树
如果将小兔子a看成细树枝,大兔子b看成粗树枝,那么就可以应用兔子序列模拟一棵树的自动生长过程了(如上页图10)。
首先,在Scratch中添加tree链表,用来存放兔子序列。最初该链表中只有1项,存放着字符a,也就是树的第一层。然后,制作“生长树”“替换”和“分叉”三个模块(如上页图11)。当主程序运行一次“生长树”时,将三次执行“替换”模块,完成前面所提到的三步“全部替换”。最后,利用“分叉”模块将tree链表中所有值为“ba”的项分解为两项。反复运行“生长树”便可生成更长的兔子序列。例如,运行四次“生长树”后,tree链表就增加到5项,它们分别为babba。
接着,添加一个小圆点角色并制作一个“画树枝”的模块,让小圆点以x1、y1为起始位置画线段(如上页图12)。该模块将会遍历当前的tree链表,遇到a,则画长度为len的垂直线段,遇到b,則分叉画两条斜线段。画好线段后,将小圆点当前的位置分别存入x、y链表,它们将成为下一轮画树枝的初始位置。
再接着,制作“初始化”的积木,将程序中涉及到的链表设置为初始值,并设置画笔大小和颜色,清空舞台上所有画线。最后,编写主程序,根据用户输入的高度h来画树(如上页图13)。在画完第一层之后,后续的h-1层就是一个重复迭代的“画树枝”过程。其中每一层树枝的长度和树的高度h以及当前层数i存在一定关系,这个关系式可以自己进行调整。每画完一层,就会再运行“生长树”模块,使树的链表即兔子序列再变长,也就意味着树又长高了一层。
当然还可以继续优化程序,让线段变得有粗细之分,并使角度发生随机变化(如图14),甚至可以让每个节点是否继续生长也有随机变化的可能,让某些节点长出树叶、花果。通过不断地优化程序,可以使得这个程序所画的树更像大自然的树。
为了给大家更多思考和想象的空间,本文没有列出所有代码,需要教师自己研究补充完整。因为代码量比较大,在实际上课时,教师可将讲课重点放在实现迭代过程的实现思路上,部分代码可以直接提供给学生,如怎么分解符号串、以何种形式画树枝等,不必过多纠缠于代码的实现细节。
面对同样的兔子序列,可以使用各种软件玩出很多花样,哪怕只是简单的记事本或画图软件。因为计算思维是一种建立在计算机科学概念基础上的思维方式,它并不局限于某类或某种软件,甚至它也不局限于计算机,关键在于我们思维的方式:能否分解问题,通过抽象化、符号化,运用迭代、递归等方法将其转化成为可以解决的问题。其实,在工作、生活和学习中,许多地方都隐含着用计算思维解决问题的策略,等待着大家去探索挖掘。