APP下载

以培养计算思维为导向的《计算机科学导论》实践教学案例设计

2019-12-27袁红娟

软件导刊(教育技术) 2019年7期
关键词:字符串计算机科学字符

袁红娟,朱 晔,郦 丽

(泰州学院 计算机科学与技术学院,江苏 泰州 225300)

0 引言

计算思维是运用计算机科学的基础概念进行问题求解、系统设计,以及人类行为理解等涵盖计算机科学之广度的一系列思维活动[1]。计算思维概念由美国卡内基·卡梅隆大学计算机科学系主任周以真教授于2006年首次提出。

“通过中美高水平大学课堂教学的对比,可以看到一流大学教育的目的:不只是学知识、学能力,而且是学会一种思维方式,培养学生批判性独立思考的能力,并为终身学习打下基础”。《计算机科学导论》课程是计算机专业本科生的入门课程,肩负着引导大一新生正确认知和学习计算机科学知识的重任。除了理论知识的学习,在实践教学设计中,重点培养学生独立的计算思维。

2018年,在广州大学举办的“第十一届中国大学教学论坛”上,吴岩司长提出:“课程是教育最微观问题,解决的是教育最根本问题。各高校要全面梳理各门课程的教学内容,淘汰‘水课’,打造‘金课’,合理提升学业挑战度,拓展课程深度,切实提高课程教学质量”。上述提议,强化了人才培养的核心地位,一流本科教育的基础地位,要求建设中国大学“金课”。

泰州学院计算机科学与技术学院,以《计算机科学导论》作为打造“金课”课程教学改革的先锋,相继申报了校级重点教改课题、校级精品在线开放课程建设项目。课程实践教学设计中,摒弃原先办公软件的学习,引入Raptor流程图软件进行算法设计与验证,提高学生分析问题、抽象建模、解决问题的能力。《计算机科学导论》在线课程进行课程教学视频、题库资源建设的同时,与同期教学的《Python程序设计》课程联动,进行互补教学。通过《计算机科学导论》课程学习并深入理解算法思想,以Raptor工具实现算法抽象、建模、转换,最后用Python语言将算法设计并实现,将学生的计算思维培养贯穿于整个教学过程,让学生获得独立解决问题的综合能力。

1 实践教学内容设计

为培养学生的计算思维,体现“金课”高阶性、创新性、挑战度等特点,《计算机科学导论》在实践教学中合理提升实验挑战度,拓展知识的深度,提高教学质量。实践教学一共设置8个实验,内容如下:

实验1:顺序结构程序设计,掌握Raptor算术、逻辑、关系运算符的使用,掌握常量、变量及常用函数的使用。

实验2:选择结构程序设计,掌握决策表达式的使用,掌握单分支、双分支、多分支的使用。

实验3:循环结构程序设计,掌握循环控制的基本结构,掌握决策语句的3种测试方式,掌握循环结构的嵌套。

实验4:数组,掌握一维数组、二维数组、字符数组的创建和使用。

实验5:子程序和子图,掌握子程序的创建、参数的设置、传递;掌握子图的创建,理解变量的生命周期。

实验6:递归和迭代,掌握递归的方法和思维,掌握迭代的方法和思维。

实验7:查找,掌握顺序查找、二分查找、分块查找等的方法和使用。

实验8:排序,掌握插入排序、冒泡排序、选择排序等的方法和使用。

实验1-4是基本的设计性实验,让学生使用Raptor工具对问题进行抽象和建模,用流程图解决简单问题;实验5-8是综合性实验,由基础到应用,层层递进,将知识、能力、素质有机融合,培养学生解决复杂问题的综合能力和高级思维。

2 实践教学方式创新

为提高教学效果,泰州学院大力推动在线开放课程的建设,打造线上金课、线下金课、线上线下混合式金课等。2018年,《计算机科学导论》被立项为校级在线开放课程。课程教学中,利用现有的在线教学资源,实现线上、线下混合式教学改革。在实践教学设计中,体现高阶性、创新性、挑战度的特点,合理提升实验难度,并及时反馈、总结实验效果,提高实践教学含金量。

例如:实验7递归,首先介绍常规的递归思想,列举典型案例——阶乘、汉诺塔游戏和斐波那契数列,然后结合“蓝桥杯”程序设计大赛,布置出具有挑战性的拓展实验作业,全面锻炼学生思维能力。具体的教学过程如下:

2.1 实践教学案例引入

如果用a b c d这4个字母组成一个串,有4!=24种,如果把它们排个序,每个串都对应一个序号:

abcd 0

abdc 1

acbd 2

acdb 3

adbc 4

adcb 5

bacd 6

badc 7

bcad 8

bcda 9

bdac 10

bdca 11

cabd 12

cadb 13

cbad 14

cbda 15

cdab 16

cdba 17

现在有不多于10个两两不同的小写字母,给出它们组成的串,求出该串在所有排列中的序号。

样例输入:bdca

样例输出:11

要求学生基于递归或递推思想,用Raptor工具抽象建模,以流程图方式解决上述问题,并用Python语言编程实现。

2.2 实践教学案例原理

该实践教学案例反映的是康托展开式,该公式实现了由字符c1到cn组成的全排列序列到其编号之间的一种映射,常用于构建哈希表时的空间压缩[4]。

康托公式:X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+…+a2*1!+a1*0!

由c1到cn这n个字符组成的全排列,共n!个,按每个全排列组成的字符从小到大进行排列,并对每个序列进行编号,记为X(从0开始)。

比如说a到d组成的全排列中,abcd对应编号0,abdc对应编号1。

对于ai的理解:

对a到d的全排列中,考察cbad,则

a4={c在集合(c,b,a,d)中是第几大的元素}=2

a3={b在集合(b,a,d)中是第几大的元素}=1

a2={a在集合(a,d)中是第几大的元素}=0

a1={d在集合(d)中是第几大的元素}=0

康托公式中的每一项依次对应全排列序列中的每个元素,并按上述规则映射,则X=2*3!+1*2!+0*1!+0*0!=14,即cbad对应的编号为14。

教学要求:分别用递推和递归方法解决该问题。

3 递推算法实现

递推算法以初始值为基础,用相同的运算规律,逐次重复运算,直至运算结束。从“起点”重复相同的方法直至到达一定“边界”,用循环可以实现[2]。其思想是把一个复杂庞大的计算过程转化为简单过程的多次重复。

3.1 问题分析

若字母长度为3,排列及序号如下:

abc 0

bcb 1

bac 2

bca 3

cab 4

cba 5

字符串s,用n=len(s)计算字符串s的长度。定义计算字符串s序号的递推函数func(s)。

若s=“cba”。

用i逐个遍历s字符串的索引,循环:

当i=0时,首字母c在字符串“cba”中排列为2,序号变化幅度为:2!,因此得到首字母c在字符串“cba”中的排列序号:2*2!,即4。

当i=1时,字母b在剩余字符串“ba”中的排列为1,序号变化幅度为:1!,因此得到字母b在字符串“ba”中的排列序号为:1*1!,即1。

当i=2时,字母a在剩余字符串“a”中的排列为0,序号变化幅度为:0!,因此得到字母a在字符串“a”中的排列序号为0*0!,即0。

直至i>=len(s)结束。

综合以上得到字符串s=“cba”的排列序号为:4+1+0=5。

算法中要多次调用阶乘函数,比如计算n!,用fac(n)表示,下文均如此调用。

正确计算出当前字母在剩余字符串中排列位置,即阶乘的系数值,首先将字符串s,自索引i开始截取剩余的字符串,放置到t中;然后对字符串t升序排列;最后查找出字母s[i]在字符串t中索引值m,m即为阶乘的系数值。

3.2 递推函数伪代码

func(s)

输入:字符串s

输出:字符串s的排列序号

计算字符串s的长度n=len(s)

字符序号初始值sum=0

遍历字符串s的每个索引i:

用t截取字符串s从索引i开始的剩余字符串

对t升序排列

找出s[i]在t中的索引值m

计算当前字符s[i]在排列中的序号sum=sum+m*fac(n-(i+1))

返回排列序号sum

3.3 python语言实现递推函数

def func(s): #定义函数fun(s),计算字符串s的排列序号

n=len(s) #n计算字符串s的长度

sum=0 #sum计算字符串的排列序号

for i in range(n): #遍历字符串s的各索引

t=s[i:] #用t截取索引i开始的剩余字符串

t=sorted(t) #对字符串t进行升序排列

m=t.index(s[i]) #找出字符s[i]在t中的索引值

sum=sum+m*fac(n-(i+1)) #计算当前s[0:i+1]在字符排列中的序号

return sum #返回字符排列序号

4 递归算法实现

在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法[3]。递归是一种奇妙的思维方式,主要分为两个阶段:

(1)递推归纳。把问题转化为比原问题规模小的同类问题。如计算n!,可以递推归纳到计算n*(n-1)!;计算(n-1)!,可以递推归纳到计算(n-1)*(n-1)!……。

(2)递归终止。当问题小到一定规模时,结束递归,逐层返回。如当递推归纳到计算1!,得到1!=1或0!=1,则开始逐层返回。

因此,计算n!的函数,python代码如下:

def fac(n):

if n==1 or n==0:

return 1

else:

return n*fac(n-1)

4.1 问题分析

若字母长度为3,排列及序号如下:

abc 0

bcb 1

bac 2

bca 3

cab 4

cba 5

若字母长度为2,排列及序号如下:

ab 0

ba 1

若字母长度为1,排列及序号如下:

a 0

字符串s,用n=len(s)计算字符串s的长度。用递归函数fun(s)来计算字符串s的排列序号。

通过观察,可以归纳得出:

当字符串长度n=1时,返回字符串排列序号为0。

当字符串长度n>1时,字符串的排列序号由绝对序号加上相对序号求得。绝对序号,即以首字母开头的最小字符串的排列序号;相对序号,是首字母相同时,长度为n-1的后续字符串的排列序号。

例如,长度为3的字符串“cba”中:

以‘c’开头的最小字符串“cab”,序号为4,即2*2!。

去除首字母‘c’,剩余字符串“ba”,以‘b’开头的最小字符串“ba”,序号为1,即1*1!。

去除首字母‘b’,剩余字符串“a”,以‘a’开头的最小字符串“a”,序号为0。这些可以看作各长度字符串在排列中的绝对序号。

在首字母相同的字符串中,排列序号的变化,则依据长度为n-1的后续字符串的排列,可称之为相对序号,用fun(s[1:])递归调用计算。

然后将绝对序号+相对序号,作为字符串的排列序号返回。

根据递推归纳,计算如下:

计算“cba”的排列序号,fun(“cba”)=2*2!+fun(“ba”)

计算“ba”的排列序号,fun(“ba”)=1*1!+fun(“a”)

计算“a”的排列序号,fun(“a”)=0

当字符串长度为1时,fun(“a”)=0,递归终止,然后逐层返回,依次得到fun(“ba”)=1,fun(“cba”)=5。

这里需要考虑首字母不同时,长度为n的最小字符串,其排列序号以(n-1)!幅度变化。正确计算出变化幅度,即(n-1)!的系数值,可以先将字符串s升序排列后的结果放在t中,然后查找出首字母s[0]在字符串t中索引值m,将m作为阶乘的系数值。

4.2 递归函数伪代码

fun(s)

输入:字符串s

输出:字符串s的排列序号

计算字符串s的长度n=len(s)

若n==1,则返回0

否则:

将s升序排列,存储与t中

找出s[0]在t中的索引值,放在m中

返回m*fac(n-1)+fun(s[1:])

4.3 Python语言实现递归函数

def fun(s): #定义函数fun(s),计算s字符串排列序号

n=len(s) #n计算字符串s的长度

if n==1: #若长度为1,返回排列序号0

return 0

t=sorted(s) #将字符串s升序排列到t中

m=t.index(s[0]) #计算字符串s的首字母在字符串t中的索引m

return m*fac(n-1)+fun(s[1:]) #递归调用fun()函数,计算出排列序号

5 总结及拓展

上述递推和递归算法,均实现了计算字符串排列序号的功能。用Python程序实现算法的过程中,很方便地调用了Python的字符串排序函数、查找指定字符索引值的函数,以及字符串切片的使用,体现了《计算机科学导论》课程重点培养计算思维的特点。在后续《C程序设计》及《数据结构》课程学习中,要进一步着眼于降低算法的时间、空间复杂度,提高程序运行的效率,逐步优化程序代码。

针对上述例子,还可以进一步拓展思维。比如,给出排列序号61,要求计算出字符串的排列组合。

由于康托展开是一个全排列到一个自然数的双射,因此是可逆的。由上述递推算法的计算过程,可以很容易地将计算结果逆推回来,具体过程如下:

用61/4!=2余13,说明比首位小的字符有2个,所以首位为c。

用13/3!=2余1,说明除去(c)后,比第二位字符小的字符有2个,所以第二位为d。

用1/2!=0余1,说明除去(c,d)后,比第三位字符小的字符有0个,所以第三位为a。

用1/1!=1余0,说明除去(c,d,a)后,比第四位字符小的字符有1个,所以第四位为e。

说明除去(c,d,a,e)后,最后一位自然就是剩下的字符b。

通过以上分析,排列序号为61时,所求排列组合为cdaeb。

康托展开的逆运算,可以作为课后拓展实验,布置给学生,让学生深入理解该公式的原理并加以应用。

本实践案例具有先进性和互动性,学习结果具有探究性和个性化,体现了“金课”的创新性。实践案例同时具备前沿性和时代性,具有一定难度,需要通过认真思考才能解决,体现了“金课”的挑战度。同时,实践案例分别使用递归、递推思想,来解决现实问题,需要学生综合运用问题分析、抽象建模的能力,并使用Raptor工具及Python语言实现该问题,培养学生解决复杂问题的综合能力和高级思维,体现了“金课”的高阶性。

猜你喜欢

字符串计算机科学字符
基于文本挖掘的语词典研究
论高级用字阶段汉字系统选择字符的几个原则
字符代表几
一种USB接口字符液晶控制器设计
图片轻松变身ASCⅡ艺术画
试论计算机科学与技术的现代化运用
探讨计算机科学与技术跨越式发展
新英镑
SQL server 2008中的常见的字符串处理函数
倍增法之后缀数组解决重复子串的问题