第3章〓流程控制 【应知】理解选择和循环的意义和基本实现语句。 【应会】掌握单分支、双分支及多分支选择结构语句的使用方法; 掌握实现无限循环操作的while语句、实现遍历操作的for...in语句、用于提前结束循环的break和continue语句。 【难点】嵌套语句的使用,穷举法和迭代法的使用。 流程控制也称控制流程,是计算机运算领域的专用语,是指程序运行时指令(或程序、子程序、代码段)运行或求值的顺序。流程控制对于任何一门编程语言都是至关重要的,它提供了控制程序执行的方法。Python语言提供了顺序结构、选择结构和循环结构 图3.1顺序结构 流程图 3种流程控制。 3.1顺序结构 顺序结构是程序中最简单的流程控制结构,按照代码出现的先后顺序依次执行。程序中的代码大多是顺序执行的,其结构流程图如图3.1所示。 本章之前编写的代码大多采用顺序结构。 【实例3.1】输出指定格式的日期。 1# 处理日期和时间的模块库 datetime.date是表示日期的类,datetime.datetime是表示日期 2# 时间的类 3import datetime 4# datetime.day.today()用于获取当前的日期,返回格式为YYYY-mm-dd。 5today = datetime.date.today() 6oneday = datetime.timedelta()# datetime.timedelta()表示两个时间之间的时间差 7yesterday = today - oneday 8tomorrow = today + oneday 9print("今天是:"+str(today)) 10# strftime()函数接收时间元组,并返回可读字符串表示的时间,格式由参数format决定。 11print("昨天是:"+yesterday.strftime("%y/%m/%d")) 12print("明天是:"+tomorrow.strftime("%m-%d-%Y")) 其中,%Y: 四位数的年份表示(0000~9999)。 %y: 两位数的年份表示(00~99)。 %m: 两位数的月份表示(01~12)。 %d: 月份内的某一天(1~31)。 运行结果如下: 今天是:2023-09-26 昨天是:23/09/25 明天是:09-27-2023 3.2选择结构 选择结构也称分支结构,用于判断给定条件,再根据判定结果控制程序流程。例如,日常生活中常见的登录即为选择结构,用户先输入用户名和密码,系统在数据库中查找并匹配。如果两者都与数据库中的记录保持一致,则登录成功,可以继续下面的操作; 否则要重新输入或退出系统。Python中常用的分支结构有单分支、双分支和多分支3种类型。 3.2.1单分支结构 单分支结构是指只有一个分支,满足判断条件则执行相应语句。现实生活中的“如果天下雨,地会就湿”对应的就是单分支结构。 图3.2单分支结构流程图 其结构化流程图如图3.2所示。 其语法结构为: if 条件表达式: 语句块 执行过程为: 先判断条件,如果执行结果为真,则执行后续语句块,否则什么也不执行。 说明: (1) “条件表达式”可以是逻辑表达式、条件表达式、算术表达式等任意类型的表达式,只要能判断非零或非空即可。“语句块”可以是一条语句,也可以是多条语句。多条语句时,需保证缩进对齐一致。 (2) “条件表达式”后面一定要加冒号“: ”,这是初学者易犯错的地方。 【实例3.2】根据出生年份判断是否为成年人。 1import datetime 2year = int(input("请输入出生年份:")) 3if datetime.date.today( ).year - year >= 18:# datetime.date.today( ).year表示获取 4# 当前日期的年份 5print("是成年人!") 运行结果如下: 请输入出生年份:2015 请输入出生年份:2005 是成年人! 【实例3.3】两个整数升序排列并输出。 1a, b = input("input a,b:").split(" ")# 一行输入多个数,用空格分开 2print("排序前:" + a + "," + b) 3if int(a) > int(b): 4a, b = b, a# 交换a,b两个数 5print("排序后:" + a + "," + b) 运行结果如下: input a,b:3 2 排序前:3,2 排序后:2,3 说明: 在Python中,可直接使用语句“a, b = b, a”交换两个值,而在其他高级语言中必须引入中间变量实现交换,即“t=a, a=b, b=t”,这正是Python语言的精妙之处。 拓展: 可以尝试实现3个整数升序排列,更多数的排序需采用其他高级数据类型实现。 3.2.2双分支结构 若条件成立时需要执行某些操作,不成立时需执行另一些操作,则需采用双分支结构。例如身份验证时,密码正确可以登录系统,密码错误则要重新输入。其结构化流程图如图3.3所示。 图3.3双分支结构流程图 对应的语法结构为: if 条件表达式: 语句块1 else: 语句块2 其执行过程为: 先判断条件表达式,如果结果为真或非零,则执行语句块1,否则执行语句块2。 注意: (1) 双分支结构中的else语句不能独立存在,即有else,一定有相应的if,但有if,不一定有else。 (2) else后面不需要加也不宜加条件表达式。 【实例3.4】求两个数中的较大者(此例题可使用4种方法实现)。 方法1: 使用单分支结构方法2: 使用双分支结构 1a, b = input("input a,b:").split(" ") 2max = int(a) 3if (int(max) < int(b)): 4max = int(b) 5print("较大的数为:" + str(max)) 1a, b = input("input a,b:").split(" ") 2if (int(a) > int(b)): 3print("较大的数为:" + a) 4else: 5print("较大的数为:" + b) 方法3: 使用三目运算符方法4: 使用内置函数max 1a, b = input("input a,b:").split(" ") 2max = int(a) if int(a) > int(b) else int(b) 3print("较大的数为:" + str(max)) 1a, b = input("input a,b:").split(" ") 2print("较大的数为:" + str(max(a, b))) 三目运算符也称三元运算符,其语法格式为: (True_statements) if (expression) else (False_statements) 运算规则为: 先对逻辑表达式expression求值,如果逻辑表达式返回True,则执行并返回True_statements的值; 如果逻辑表达式返回False,则执行并返回False_statements的值。 很明显,三目运算符是双分支结构的一种紧凑表现形式。“条件为真的语句”和“条件为假的语句”可包含多条,语句格式有两种。 (1) 多条语句以英文逗号隔开,每条语句都会执行,程序返回多条语句的返回值组成的元组。如: >>>a, b = input("input a,b:").split(" ") >>>s = "No cross, no crown.", "a大于b" if a > b else "a小于或等于b" >>>print(s) 当输入10、20时,运行结果为: ('No cross, no crown.', 'a小于或等于b') (2) 多条语句以英文分号隔开,每条语句都会执行,程序只返回第一条语句的值。如: >>>a, b = input("input a,b:").split(" ") >>>s = "No cross, no crown."; "a大于b" if a > b else "a小于或等于b" >>>print(s) 当输入10、20时,运行结果为: No cross, no crown. 另外,三目运算符支持嵌套,通过嵌套三目运算符,可进行更复杂的判断。 3.2.3多分支结构 在很多情况下,供用户选择的操作有多种,例如,根据空气质量指数判断天气状况并提供生活建议,或者根据百分制成绩判断成绩等级等。使用程序语句实现时,就可以使用多分支结构进行处理。其结构化流程图如图3.4所示。 图3.4多分支结构流程图 对应的语法格式为: if 条件表达式1: 语句块1 elif条件表达式2: 语句块2 elif条件表达式3: 语句块3 … else: 语句块n 执行过程为: 先判断条件表达式1,如果结果为真,则执行语句块1; 否则判断条件表达式2,如果结果为真,则执行语句块2……只有在所有表达式都为假的情况下,才执行else后的语句块n。 【实例3.5】计算阶梯电价(阶梯电价是按照用户消费的电量分段定价,用电价格随用电量增加呈阶梯状逐级递增的一种电价定价机制,目的是减少资源浪费,提高能源利用效率。2023年徐州市居民阶梯电价收费规则为: 当每月用电量0~230度时为第一档,电价是0.5283元/度; 当每月用电量231~400度时为第二档,电度单价在一档单价基础上加0.05元/度; 当每月用电量为401度及以上时,电度单价在一档基础上加0.3元/度)。计算结果保留两位小数。 1x = float(input("请输入每月用电量:")) 2if x < 0: 3print("输入错误!") 4else: 5if x <= 230: 6y = 0.5283 * x 7elif x <= 400: 8y = (0.5283+0.05)*x 9else: 10y = (0.5283+0.3)*x 11print("本月电费为%.2f元" % (y)) 运行结果如下: 请输入每月用电量:-1 输入错误!请输入每月用电量:200 本月电费为105.66元请输入每月用电量:400 本月电费为231.32元请输入每月用电量:601 本月电费为497.81元 【实例3.6】根据空气质量指数进行生活建议。 空气质量指数(air quality index,AQI)是根据空气中的各种成分占比,将监测的空气浓度简化为单一概念型数值的形式,将空气污染程度和空气质量状况分级表示,用于反映城市的短期空气质量状况和变化趋势。具体数值及等级如表3.1所示。 表3.1AQI数值、对应等级及生活建议 AQI数值对 应 等 级生 活 建 议 0~50一级 优空气清新,适宜参加户外活动 51~100二级 良可以正常进行户外活动 101~150三级 轻度污染敏感人群减少体力消耗大的户外活动 151~200四级 中度污染对敏感人群影响较大,减少户外活动 201~300五级 重度污染所有人适当减少户外活动 >300六级 严重污染尽量不要留在户外 源代码如下: 1x = int(input("请输入AQI数值:")) 2if x < 0: 3print("输入错误!") 4else: 5if x <= 50: 6s = "一级,优,空气清新,适宜参加户外活动。" 7elif x <= 100: 8s = "二级,良,可以正常进行户外活动。" 9elif x <= 150: 10s = "三级,轻度污染,敏感人群减少体力消耗大的户外活动。" 11elif x <= 200: 12s = "四级,中度污染,对敏感人群影响较大,减少户外活动。" 13elif x <= 300: 14s = "五级,重度污染,所有人适当减少户外活动。" 15else: 16s = "六级,严重污染,尽量不要留在户外。" 17print("空气质量为" + s) 运行结果如下: 请输入AQI数值:200 空气质量为四级,中度污染,对敏感人群影响较大,减少户外活动。 【实例3.7】学期末,李老师要根据学生的百分制总成绩给出对应等级: 成绩90分以上(包含90分)等级为“优秀”,成绩90~75分(包含75分)等级为“良好”,成绩75~60分(包含60分)等级为“及格”,60分以下为“不及格”。其中“Python程序设计”课程的百分制总成绩计算方法为: 总成绩=平时成绩×10%+实验成绩×30%+期末成绩×60%(备注: 平时成绩、实验成绩和期末成绩满分均为100分)。请输入某位学生的平时成绩、实验成绩和期末成绩,输出总成绩及对应等级。 源代码如下: 1usual, expe, final = input("请输入平时成绩、实验成绩和期末成绩(用","分隔):").split(',') 2usual, expe, final = eval(usual), eval(expe), eval(final) 3total = usual * 0.1 + expe * 0.3 + final * 0.6 4print("该生最终成绩为" + str(total), end=',') 5if total > 100 or total < 0: 6print("您的输入有误!") 7elif total >= 90: 8print("优秀") 9elif total >= 75: 10print("良好") 11elif total >= 60: 12print("及格") 13else: 14print("不及格") 运行结果如下: 请输入平时成绩、实验成绩和期末成绩(用","分隔):100,90,90 该生最终成绩为91.0,优秀 3.2.4分支结构的嵌套 分支结构的嵌套是指实际开发过程中,在一个分支结构中嵌套另一个分支结构。基本语法格式如下: if 条件表达式1: 语句块1 if 条件表达式2: 语句块2 else: 语句块3 else: if 条件表达式3: 语句块4 从语法角度讲,选择结构可有多种嵌套形式。程序员可根据需要选择合适的嵌套结构,但一定要注意控制不同级别代码块的缩进量,因为缩进量决定代码块的从属关系。 【实例3.8】分段函数求值 f(x)=xx≤1 2x-11= '0' and i <= '9':#判断i是否为数字字符 6digit = digit + 1 7elif i >= 'A' and i <= 'Z':#判断i是否为大写字母 8upper = upper + 1 9elif i >= 'a' and i < 'z':#判断i是否为小写字母 10lower = lower + 1 11elif i == ' ':#判断i是否为空格字符 12space = space + 1 13else:#其他字符 14other = other + 1 15print("数字字符:%d\n大写字母:%d\n小写字母:%d\n空格字符:%d\n其他字符:%d\n" 16%(digit,upper,lower,space,other)) 运行结果如下: 请输入一个字符串:Life is short, we need Python! 数字字符:0 大写字母:2 小写字母:21 空格字符:5 其他字符:2 其中,实现字符分类的循环体也用内置函数代替,如: 1if i.isdigit( ):#判断i是否为数字字符 2digit = digit + 1 3elif i.isupper( ):#判断i是否为大写字母 4upper = upper + 1 5elif i.islower( ):#判断i是否为小写字母 6lower = lower + 1 7elif i.isspace( ):#判断i是否为空格字符 8space = space + 1 9else: 10other = other + 1 内置函数i.isdigit、i.isupper、i.islower、i.isspace分别用于判断i是否为数字字符、大写字母、小写字母和空格字符。 2. 使用序列索引迭代序列对象 在for...in循环结构中,也可使用序列索引遍历列表,语法格式如下: for index in range(len(list)): 循环体 其中,index为序列的索引项,内置函数range为计数函数,len获取序列长度。 【实例3.13】统计字符串中各类字符的个数(range版)。 1s = input("请输入一个字符串:") 2digit = upper = lower = space = other = 0# 数字字符、大写字母、小写字母、空格和其 3# 他字符的个数 4for i in range(len(s)): 5if s[i].isdigit( ):# 判断i是否为数字字符 6 digit = digit + 1 7elif s[i].isupper( ):# 判断i是否为大写字母 8 upper = upper + 1 9elif s[i].islower( ):# 判断i是否为小写字母 10 lower = lower + 1 11elif s[i].isspace( ):# 判断i是否为空格字符 12 space = space + 1 13else: 14 other = other + 1 15print("数字字符:%d\n大写字母:%d\n小写字母:%d\n空格字符:%d\n其他字符:%d\n" 16%(digit, upper, lower, space, other)) 运行结果如下: 请输入一个字符串:I am a student, I am 20 years old! 数字字符:2 大写字母:2 小写字母:20 空格字符:8 其他字符:2 使用range函数可得到用于迭代的索引列表,使用索引下标“[]”可以方便快捷地访问序列对象。另外,还可使用range函数实现类似Java、C++等传统编程语言的for循环结构,即从循环三要素角度出发设计循环结构,语法格式为: range([start,] end[, step=1]) 其中,range函数会返回一个整数序列,可选项start为序列初值(循环变量初值),end为序列终止值(循环变量终值,且不含end本身),可选项step为步长或增量,默认为1。 【实例3.14】求∑100i=1i(range版)。 1sum = 0 2for i in range(1, 101, 1): 3sum = sum + i 4print("sum=" + str(sum)) 运行结果如下: sum=5050 显然,此时的for...in循环与while循环完全等价。 3. 使用枚举函数迭代序列对象 Python内置函数enumerate用于将一个可遍历的数据对象(列表、元组或字符串)组合为一个索引序列,同时列出数据和下标,一般用于for...in循环中。其语法格式为: for index, iterating_var in enumerate(list, start_index=0): 循环体 其中,index返回索引计数,iterating_var为与索引计数相对应的索引对象成员,list为待遍历的序列对象,start_index为返回的起始索引计数,默认值为0。 【实例3.15】打印学生花名册。 1name_list = ["李白", "孟浩然", "王维", "李绅"]#name_list的数据类型为列表list 2for index, name in enumerate(name_list): 3print(index, name) 运行结果如下: 0 李白 1 孟浩然 2 王维 3 李绅 3.3.3循环嵌套 允许在一个循环结构中嵌入另一个循环结构,称为循环嵌套。在Python中,for...in循环结构和while循环结构都可进行循环嵌套。如: while condition_expression 1: for index in range(len(list)): 循环体 图3.10打印九九乘法表结构流程图 for...in循环结构可嵌入while循环结构,while循环结构也可嵌入for...in循环结构,还可以根据自身需要任意嵌套。 【实例3.16】打印九九乘法表(下三角形)。 算法分析: 从结构看,九九乘法表是二维形式,单重循环无法实现。从内容看,第一个乘数每行一致,第二个乘数同行每列依次加1,故使用两重循环。外循环控制第一个乘数(迭代变量从1到9),内循环控制第二个乘数。因为要求按照下三角形打印,所以内循环迭代变量只能从1到i。结构化流程图如图3.10所示。 源代码如下: 1for i in range(1, 10): 2for j in range(1, i+1): 3 print(str(i) + "*" + str(j) + "=" + str(i * j), end=" ") 4print() #每行末尾换行 运行结果如下: 1*1=1 2*1=2 2*2=4 3*1=3 3*2=6 3*3=9 4*1=4 4*2=8 4*3=12 4*4=16 5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 拓展: 打印上三角九九乘法表、钻石等图形。 【实例3.17】求1!+2!+…+20!。 算法分析: 求n!要用循环结构实现,累加和也要用循环结构实现,故采用双重循环。外循环计算累加和,内循环求n!。结构化流程图如图3.11所示。 图3.11阶乘和结构流程图 源代码如下: 1sum = 0# 累加和 2for i in range(1, 21): #外循环,用于累加和 3fact = 1# 存放n! 4for j in range(1, i + 1): #内循环,用于计算i! 5fact = fact * j 6sum = sum + fact 7print("sum=" + str(sum)) 运行结果如下: sum=2561327494111820313 观察运行过程可以发现,双重循环计算累加和时,每次都是从1开始计算n!。事实上n!=(n-1)!*n,故可使用单重循环实现。优化后的代码如下: 1sum = 0# 累加和 2fact = 1# 存放n! 3for i in range(1, 21): 4fact = fact * i #直接使用n!=(n-1)!*n计算n! 5sum = sum + fact 6print("sum=" + str(sum)) 循环结构中可嵌套另一循环结构,也可嵌套选择结构,反之亦然。 【实例3.18】列出1~200之间的所有素数,要求每行输出10个数(标志变量版)。 算法分析: 素数是只能被1和本身整除的自然数。判断n是否为素数的方法为: 依次除以2~n,如果能整除,则不是素数。这个过程需使用单重循环嵌套选择结构实现。而列出1~200之间的所有素数也需要循环实现,故使用双重循环完成。流程图如图3.12所示。 图3.12实例3.18结构流程图 源代码如下: 1import math# math模型库,sqrt()函数需要使用 2count = 0 #累计素数个数 3for n in range(2, 201): #外循环遍历2~200 4i,flag = 2,True 5while i <= math.sqrt(n) and flag:#内循环判断每个数是否为素数 6if n % i == 0:#如果能够整除,则不是素数,将flag置为False 7flag = False 8i = i + 1 9if flag: #如果是素数,则打印 10count = count + 1 11print(n, end="\t") 12if count % 10 == 0: #控制每行10个 13print() 运行结果如下: 2357111317192329 31374143475359616771 7379838997101103107109113 127131137139149151157163167173 179181191193197199 说明: 本实例中外循环使用for...in结构,内循环使用while结构,并在内循环中嵌入分支结构进行整除判断。引入标志变量flag标志n是否为素数(默认值为True),一旦判断能够整除(即不是素数),则修改flag值为False,内循环条件执行为否,退出内循环。引入计数变量count记录每行打印个数。 3.3.4break和continue 在循环结构中,大多数情况下当循环条件满足时,会一直执行循环体,直至循环条件不满足。但有时需要在某种条件下提前结束循环,实现方法有两种: 一是使用标志变量,如实例3.18中的flag,通过flag值的变化,在循环未正常结束时提前退出循环; 二是使用break实现。 break语句可以终止当前循环。一般与if语句搭配使用,表示在某种条件下提前结束循环。 【实例3.19】break语句示例。 1n = int(input("n:")) 2for i in range(1, 11): 3if i == n: 4break 5print(i, end=",") 运行结果如下: n:5 1,2,3,4,n:20 1,2,3,4,5,6,7,8,9,10, 从运行结果可以看出,当i的迭代次数小于10时,循环会提前结束。 注意: 使用嵌套循环时,break语句只跳出最内层的循环。 【实例3.20】列出1~200的所有素数,要求每行输出10个数(break版)。 1import math# math模型库,sqrt()函数需要使用 2count = 0 3for n in range(2, 201): #外循环遍历数据 4i = 2 5while i <= math.sqrt(n): #内循环判断是否为素数 6if n % i == 0:#能整除,则不是素数,判断结束 7break 8i = i + 1 9if i > math.sqrt(n):#是素数 10count = count + 1 11print(n, end="\t") 12if count % 10 == 0: #控制每行10个 13print() 说明: 结束内循环有两种途径: ①n是素数,正常结束,即所有的n%i!=0,此时i>sqrt(n); ②n%==0,即n不是素数,提前结束循环。所以break版与标志变量版在输出素数时条件正好相反。 有时只在一定条件下不执行本次循环体,而继续执行下一轮循环,此时需使用另一种语句——continue。 continue是另一种提前结束循环的语句,与break不同,continue只结束本次循环,继续后续操作。 【实例3.21】continue语句示例。 1n = int(input("n:")) 2for i in range(1, 11): 3if i == n: 4continue 5print(i, end=",") 运行结果如下: n:5 1,2,3,4,6,7,8,9,10,n:20 1,2,3,4,5,6,7,8,9,10, 从运行结果可以看出,当n<10时,不执行本次循环,而继续执行后续循环。 【实例3.22】break与continue语句的区别示例。 1import random 2n = random.randint(0, 10) 3print("您选择的是", n) 4for i in range(1,11): 5if i == n: 6print(i, "结束了。") 7break 8if i % 3 != 0: 9print(i, "继续!") 10continue 11print('I love Python!') 运行结果如下: 您选择的是 4 1 继续! 2 继续! I love Python! 4 结束了。 # 满足i%3!=0,执行continue,进行下一次循环 # 两个判断条件都不满足 # 满足i==n,执行break,退出循环 此程序段的执行过程也可用图3.13表示。 图3.13break与continue语句的区别示意图 3.3.5穷举与迭代 1. 穷举 穷举法也称枚举法或列举法,是计算机求解问题时常用的算法,用于解决通过公式推导、规则演绎等方法不能解决的问题。其基本思想是: 不重复、不遗漏地列举所有可能的情况,以便从中寻找满足条件的结果。采用穷举法解决实际问题时,主要使用循环结构嵌套选择结构实现——循环结构用于列举所有可能的情况,而选择结构用于判断当前条件是否为所求解,其一般框架为: for 循环变量x的所有可能的值: if x满足指定条件: x即为所求解 图3.14百钱买百鸡结构流程图 【实例3.23】(百钱买百鸡)我国古代数学家张丘建在《算经》一书中提出的数学问题: 鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何? 算法分析: 假设有x只鸡翁(即公鸡),y只鸡母(即母鸡),则鸡雏(小鸡)有(100-x-y)只。根据题意,一只公鸡需要五钱,一只母鸡需要三钱,一只小鸡需要一钱,故可以列方程5*x+3*y+13*(100-x-y)=100。这是一个不定式方程,不能利用普通的公式推导得出结论,最常用的解法是枚举,将所有可能的情况一一列举出来。流程图如图3.14所示。 1for x in range(101): 2for y in range(101): 3if 5 * x + 3 * y + (1/3) * (100 - x - y) == 100: 4print(x, y, (100 - x - y)) 运行结果如下: 0 25 75 4 18 78 8 11 81 12 4 84 当然,此程序可以优化。由题意可知,公鸡最多20只,母鸡最多33只,所以可将程序优化为: 1for x in range(21): 2for y in range(34): 3if 5 * x + 3 * y + (1/3) * (100 - x - y) == 100: 4print(x, y, (100 - x - y)) 2. 迭代 迭代是另一种常用的循环算法,其利用计算机运行速度快,适合重复操作的特点,使计算机对一组语句进行重复操作,且后一次的操作数基于前一次的执行结果。用迭代法求解实际问题时,需要考虑两方面的问题。 (1) 确定迭代变量: 由旧值直接或间接递推而来的变量就是迭代变量。 (2) 建立迭代关系式: 迭代关系式即“循环不变式”,是一个直接或间接由旧值递推出新值的表达式。 【实例3.24】(斐波那契数列)意大利著名的数学家斐波那契在《计算之书》中提出了一个有趣的兔子问题: 一对成年兔子每个月恰好生下一对小兔子(一雌一雄)。年初时,只有一对小兔子。第一个月结束时,它们成长为成年兔子,第二个月结束时,这对成年兔子将生下一对小兔子。这种成长与繁殖的过程会一直持续,并假设生下的小兔子都不会死,那么一年之后共有多少对小兔子?(为清楚描述数列,打印前20项) 算法分析: 斐波那契数列(Fibonacci sequence),又称黄金分割数列、兔子数列。从问题的描述可以发现,年初时只有一对小兔子,第二个月这对小兔子长成中兔子(兔子总数为1对); 第三个月中兔子长成大兔子并生下一对小兔子(兔子总数为2对); 第四个月小兔子长成中兔子,大兔子再生下一对小兔子(兔子总数为1+1+1=3对)……可用表3.2描述这个过程。 表3.2斐波那契数列的变化过程 月数小兔子对数中兔子对数大兔子对数兔子总数 11001 20111 31012 41113 52125 63238 …………… 图3.15实例3.24结构流程图 可以看出,斐波那契数列为1,1,2,3,5,8,…从第三个数开始,后一个数为前两个数之和。可使用迭代法求解,迭代表达式为: F(n)=1n=1‖n=2 F(n-1)+F(n-2)n>2 其中,n为1或2时为迭代出口。 流程图如图3.15所示。 1f1 = f2 = 1# 迭代变量的初值 2print(f1, f2, end=" ")# 先输出前两个数 3for f_index in range(1, 10):# 每次输出两个数,一共输出10个数 4 f1 = f1 + f2# 迭代表达式,后一个数为前两个数的和 5 f2 = f2 + f1 6 print(f1, f2, end=" ") 运行结果如下: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 3.4流程控制综合例子 【实例3.25】设计小型的加减乘除测试程序(由系统随机给出10道加减乘除运算题目,运算数和运算符都由系统随机给出,系统自动给出答题结果和运算时间)。 算法分析: 此实例需要循环与多分支结构嵌套,循环负责控制题目数量,分支结构检测加减乘除并进行相应计算。流程图如图3.16所示。 图3.16实例3.25结构流程图 源代码如下(为简单起见,将操作数规定为不大于10的数): 1import random# 随机函数库 2import time# 时间库 3count, num = 1, 0# 分别表示题目数量,答对的题目数量 4begin_time = time.time()# 取当前系统时间,单位为秒 5while count < 11: 6 op1 = random.randint(1, 11)# 随机生成一个不大于10的整数,作为第一个操作数 7 op2 = random.randint(1, 11)# 随机生成一个不大于10的整数,作为第二个操作数 8 op = random.randint(1, 4)# 随机生成一个不大于4的数,作为操作符 9 if op == 1:# 加法运算 10print("第" + str(count) + "题:" + str(op1) + "+" + str(op2) +"=",end="") 11result = int(input()) 12if result == op1 + op2: 13 print("正确!") 14 num = num + 1# 答对题目数量加1 15else: 16 print("错误!") 17elif op == 2:# 减法运算 18if op1 < op2:# 保证被减数>减数 19 op1, op2 = op2, op1 20print("第" + str(count) + "题:" + str(op1) + "-" + str(op2) +"=",end="") 21result = int(input()) 22if result == op1 - op2: 23 print("正确!") 24 num = num + 1# 答对题目数量加1 25else: 26 print("错误!") 27elif op == 3:# 乘法运算 28print("第" + str(count) + "题:" + str(op1) + "×" + str(op2)+"=",end="") 29result = int(input()) 30if result == op1 * op2: 31 print("正确!") 32 num = num + 1# 答对题目数量加1 33else: 34 print("错误!") 35else: 36while op1 % op2 != 0:# 保证整除 37 op1 = random.randint(1,11)# 随机生成一个不大于10的数,作为第一个操作数 38 op2 = random.randint(1,11)# 随机生成一个不大于10的数,作为第二个操作数 39 if op1 < op2:# 保证被除数>除数 40op1, op2 = op2, op 41if op1 < op2:# 保证被除数>除数 42 op1, op2 = op2, op 43print("第" + str(count) + "题:" + str(op1) + "÷" + str(op2)+"=",end="") 44result = int(input()) 45if result == op1 // op2:# 为简单起见,采用整除 46print("正确!") 47num = num + 1# 答对题目数量加1 48else: 49print("错误!") 50count = count + 1 51end_time = time.time()# 获取系统当前时间 52print("答对" + str(num) + "道题目, 得分" + str(10 * num) + "分,", end=' ') 53print("用时为%.2f秒。" % float(end_time - begin_time)) # 两次时间差为运行时间 运行结果如下: 第1题:7+2=9 正确! 第2题:5×4=20 正确! 第3题:7×2=14 正确! 第4题:10-5=5 正确! 第5题:10+9=19 正确! 第6题:8÷4=2 正确! 第7题:10-6=4 正确! 第8题:10-7=3 正确! 第9题:5+8=13 正确! 第10题:11-2=9 正确! 答对10道题目, 得分100分, 用时为17.33秒。 【实例3.26】模拟“剪刀石头布”五局三胜猜拳游戏: 选手和计算机轮流猜拳五次,三次胜利才算赢。 算法分析: 选手输入选项(“剪刀”“石头”“布”),计算机随机给出选项,按照游戏规则——“布”>“石头”>,“石头”>“剪刀”,“剪刀”>“布”进行评判和计数,一旦一方满足五局三胜,则游戏结束。流程图如图3.17所示。 图3.17五局三胜猜拳游戏结构流程图 源代码如下: 1import random# 随机函数库 2count1 = count2 = 0# count1和count2分别表示选手和计算机获胜的次数 3for i in range(1, 6):# 最多进行五局 4print("第%d局:" % i) 5play = input("选手:")# 选手输入选项并进行分类"剪刀"-->1,"石头"-->2,"布"-->3 6play = 1 if play == "剪刀" else (2 if play == "石头" else 3)# 三目运算符嵌套 7computer = random.randint(1, 3) # 计算机随机生成选项:1.-剪刀,2.-石头,3.-布 8# 三目运算符嵌套输出计算机选项,可读性较差 9print("计算机:剪刀") if computer == 1 else (print("计算机:石头") if computer == 102 else print("计算机:布")) 11if play == computer: # 进行判断并计数 12print("选项一样") 13elif (play == 1 and computer == 2) or (play == 2 and computer == 3) or ( play == 143 and computer == 1): 15# "剪刀"<"石头","石头"<"布","布"<"剪刀" 16print("计算机赢") 17count2 += 1 18else: 19print("选手赢") 20count1 += 1 21if count1 == 3 or count2 == 3:# 三胜退出 22break 23#输出最终结果 24if count1 > count2: 25print("最终选手胜出") 26elif count1 < count2: 27print("最终计算机胜出") 28else: 29print("平局") 运行结果如下: 第1局: 选手:剪刀 计算机:布 选手赢 第2局: 选手:石头 计算机:布 计算机赢 第3局: 选手:剪刀 计算机:石头 计算机赢 第4局: 选手:石头 计算机:石头 选项一样 第5局: 选手:布 计算机:剪刀 计算机赢 最终计算机胜出 【实例3.27】用1、3、5、8几个数字,能组成的互不相同且无重复数字的三位数各是多少(每行输出10个数字)? 总共有多少个?(蓝桥杯全国软件大赛青少年创意编程 Python 组) 算法分析: 使用穷举法解决问题,循环结构列出所有可能,选择结构进行判断。 源代码如下: 1data = [1, 3, 5, 8]# 列表存储数字,列表的内容在后续章节中详细介绍 2count = 0# 满足条件的数的个数 3for i in data:# 穷举法进行判断,循环结构穷举所有可能 4for j in data: 5for k in data: 6if i != j and j != k and k != i:# 选择结构进行判断是否满足给定条件 7count += 1# 个数加1 8print(100 * i + 10 * j + k, end=" ")# 输出数字 9if count % 10 == 0:# 每行10个 10print( ) 11print("\n一共" + str(count) + "个数字互不相同且无重复数字的三位数") 运行结果如下: 135 138 153 158 183 185 315 318 351 358 381 385 513 518 531 538 581 583 813 815 831 835 851 853 一共24个数字互不相同且无重复数字的三位数 3.5天天向上学习打卡系统——踔厉奋发 3.5.1思政导入 1951年国庆节来临之际,中央人民政府政务院邀请全国各地的英模人物进京参加国庆观礼。受邀代表中,有位名叫马毛姐的16岁安徽姑娘,特别引人注目。因为她是年龄最小的代表,受到毛泽东主席的亲切接见。主席不仅关切地询问她念书情况,还送她一本精美的笔记本,并在扉页上题词: “好好学习,天天向上。”随即,这8个字的题词迅速在全国传播开来,成为天下少年共同的读书誓言。 其实,“好好学习,天天向上”来源于中国儒家经典《礼记·大学》。汤之《盘铭》曰: 苟日新,日日新,又日新。原本说的是洗澡问题,如果今日洗去了一身的污垢,以后每天都要把污垢洗干净,如此坚持天天洗。商汤王将这句“苟日新,日日新,又日新”刻在洗澡盆上,说明这不仅仅是洗澡问题,引申为精神上的洗礼、品德上的修炼、思想上的改造。同样地,《庄子·知北游》提出“澡雪而精神”,《礼记·儒行》中也有“澡身而浴德”的说法。 3.5.2案例任务 天天向上学习打卡系统是一个具有日期显示、学习经验值计算和进一步建议功能的模拟系统。在“显示日期”模块中,显示当天的日期和星期。在“计算学习经验值”模块中,设置标准学习时长为8小时,根据用户设定的打卡周期,计算每天的学习经验值并进行累计。在“进一步建议”模块中,根据学习经验值进行学习推荐: 如果每天学习时长少于标准时长的80%,则建议“您的学习时间偏少,需要加强时间利用率,提高学习效率!”如果每天学习时长大于标准时长的120%,则建议“您的学习时间偏多,需要注意休息,加强体育锻炼!” 3.5.3案例分析和实现 根据任务描述,程序实现可分为如下几步。 1. 通过datatime库获取当天日期,并输出相关信息。 2. 根据用户输入的打卡天数n,进行n次循环。在每次循环中,根据用户输入的当天学习时长进行学习经验值的计算(学习经验值=当天学习时长/标准时长),并累加学习时长和学习经验值。 3. 根据学习经验值进行进一步学习时长建议。 流程图如图3.18所示。 图3.18天天向上学习打卡系统结构流程图 源代码如下: 1import datetime#导入日期时间库 2print('-'*11+' 苟日新,日日新,又日新。'+'-'*11) 3print("欢迎您使用天天向上学习打卡系统,标准学习时长为8小时/天。") 4sum,avg=0,0 #分别表示学习总时长和总学习经验值 5flag = False 6today=datetime.date.today()#获取当天日期 7print("今天是"+today.strftime("%Y年%m月%d日"),end=",星期") 8week=today.isoweekday() #计算当天为周几 9if week==1: 10print("一。") 11elif week==2: 12print("二。") 13elif week==3: 14print("三。") 15elif week == 4: 16print("四。") 17elif week==5: 18print("五。") 19elif week==6: 20print("六。") 21else: 22print("日。") 23num=int(input("请输入您需要打卡的天数:")) 24after_nday=today+datetime.timedelta(days=num)#当天日期后num天 25print("需要打卡的时间段为:"+today.strftime("%Y年%m月%d日")+"~"+after_nday. 26strftime("%Y年%m月%d日")) 27for i in range(num): 28 this_day=today+datetime.timedelta(days=i); 29 count=float(input("请输入"+this_day.strftime("%Y年%m月%d日")+"学习时 30 长(小时):")) 31 sum+=count#累计学习时长 32 value=count/8#计算学习经验值 33 print("您今天的学习经验值为%.2f。"%value) 34 avg+=value#累计学习经验值 35print(f"恭喜您,完成这次监督学习!\n您{num:d}天的总学习时长为{sum:.2f}小时,获得总 36学习经验值{avg:.2f}。") 37# 根据学习经验值进行建议 38if avgnum*1.2: 41 print("您的学习时间偏多,需要注意休息,加强体育锻炼!") 42else: 43 print("您的学习时间把握非常好,继续保持!") 运行结果如下: ----------- 苟日新,日日新,又日新。----------- 欢迎您使用天天向上学习打卡系统,标准学习时长为8小时/天。 今天是2023年05月05日,星期五。 请输入您需要打卡的天数:5 需要打卡的时间段为:2023年05月05日~2023年05月10日 请输入2023年05月05日学习时长(小时):5 您今天的学习经验值为0.62。 请输入2023年05月06日学习时长(小时):6 您今天的学习经验值为0.75。 请输入2023年05月07日学习时长(小时):7 您今天的学习经验值为0.88。 请输入2023年05月08日学习时长(小时):8 您今天的学习经验值为1.00。 请输入2023年05月09日学习时长(小时):9 您今天的学习经验值为1.12。 恭喜您,完成这次监督学习! 您5天的总学习时长为35.00小时,获得总学习经验值4.38。 您的学习时间把握非常好,继续保持! 3.5.4总结和启示 本案例模拟天天向上学习打卡系统,使用标准库datetime显示当前日期和相关星期信息,使用循环结构模拟n天的打卡过程,使用选择结构进行进一步学习建议,即综合使用前面两章所学知识进行设计和模拟。通过这个案例,可以很好地理解和掌握数据类型和程序控制结构。当然,该案例实现功能较为简单,但是随着后续知识的讲授和掌握,大家可以使用列表或字典存储学习时长、学习经验值等相关信息,也可以使用文件或数据库存放学习建议等,从而实现更复杂、更真实的打卡系统。 “日新月异,天行健,君子以自强不息。”“好好学习,天天向上”是一种向上、阳光的心态和状态。在这个瞬息万变的时代,科技日新月异,知识更新周期快,需持续学习,与时俱进,自我迭代,使每日的自己优于昨日的自己。蓝图已经绘就,号角已经吹响。我们要踔厉奋发、勇毅前行,努力创造更加灿烂的明天。 3.6本章小结 本章详细介绍了Python的流程控制,主要包括顺序结构、单分支选择结构、双分支选择结构、多分支选择结构、while循环结构、for...in循环结构、break语句和continue语句的概念和用法。在讲解过程中,结合大量实例,生动形象地演示了每种结构和语句的使用。最后结合天天向上学习打卡系统进行思政引导——踔厉奋发。在学习本章内容时,可以模仿实例,梳理算法流程,动手实践,熟练掌握Python流程控制语句的使用。 3.7巩固训练 【训练3.1】编写程序,判断用户输入的年份是否为闰年(判断闰年的条件是: 能被400整除或能被4整除但不能被100整除)。 【训练3.2】已知三角形边长,利用海伦公式求三角形面积和周长(海伦公式: 若三角形的三条边长为a、b、c,则p=(a+b+c)/2,面积area=p*(p-a)*(p-b)*(p-c))。 【训练3.3】某小学的学优生评定标准如下: 语文、数学、英语和科学四科的总分不低于380分,且每科成绩不低于95分。编程判断某位同学是否为学优生。 【训练3.4】(简易版个税计算器)设某公司员工小王每月税前工资为salary,五险一金等扣除为insurance,其他专项扣除为other,请编程计算小王每月应缴纳税额tax和实发工资payroll(结果保留两位小数)。 注: 应缴纳税额=税前收入-5000(起征点)-五险一金扣除-其他扣除 个人所得税=应缴纳税额×适用税率-速算扣除数 实发工资=税前工资-个人所得税-五险一金 税率表如表3.3所示。 表3.3最新居民个人工资、薪金所得税税率表 级数应纳税所得额预扣率(%)速算扣除数 1不超过3000元的部分30 2超过3000元至12000元的部分10210 3超过12000元至25000元的部分201410 4超过25000元至35000元的部分252660 5超过35000元至55000元的部分304410 6超过55000元至80000元的部分357160 7超过80000元的部分4515160 【训练3.5】幸运52猜数游戏(模仿幸运52中猜价钱游戏,编写程序,计算机随机产生一个正整数,让用户猜,并提醒用户猜大了还是猜小了,直到用户猜对为止,计算用户猜对一个数所用的秒数)。 【训练3.6】求出所有的水仙花数(水仙花数是指一个3位数,其每位数字的 3次幂之和等于其本身。例如: 1*1*1+5*5*5+3*3*3=153)。 【训练3.7】模拟打印超市购物小票。输入商品名称、价格、数量,算出应付金额。用户输入大额面值,实现找零和抹零功能,最后打印购物小票。运行结果如图3.19所示。 图3.19打印超市购物小票 【训练3.8】有一个分数数列21,32,53,85,138,…,编程计算此数列的前20项之和(结果保留两位小数)。 【训练3.9】每行10个输出所有的4位“回文数”(“回文数”是一种特殊的数字,从左边读和从右边读的结果是一模一样的)。 【训练3.10】编程实现: 输出1~ 1000中包含3的数字。如果3是连在一起的(如233),则在数字前加&; 如果此数字是质数,则在数字后加上*(例如,3,13*,23*,&33,43*…&233*…)。