第5章 循环结构程序 如果说分支给了程序选择的能力,那么可以说循环赋予了程序计算的力量。计算机之所以对人类产生了巨大的帮助,一个极其重要的原因是计算机可以高速地做循环,而且是不知疲倦地做循环。 本章介绍循环的基本语法,以及利用循环解决实际问题。 5.1循环与重复计算 “绳锯木断,水滴石穿”实质上是一个量变到质变的过程,也表明了重复蕴含着巨大的力量,只是往往重复的时间很长很长。类似这样的过程对人类而言枯燥且无趣,但通过计算机的高速性可以大大加速整个过程。 Python提供了while和for两种循环控制语句,二者各有所长,都有自己擅长处理的情况。虽然有时二者可以通用,但是遇到具体问题选择合适的循环不仅便于代码实现而且可以降低出错概率。 5.2while循环 while循环非常适合处理未知循环次数的情况。它的语法规则如下: while 条件: 语句块1 else: 语句块2 其中,条件通常称为循环条件,语句块1称为循环体。执行while循环时,首先判断循环条件是否成立,如果成立就执行循环体,否则结束循环。每次循环体完成后会再次判断循环条件是否成立,然后重复上面的过程。 如果循环是正常结束的,会执行语句块2,也就是说,如果循环是非正常结束的,不会执行语句块2。可见语句块2最多被执行一次,至少被执行零次。但是要强调一下,while循环的else部分不是必需的,可以省略。 【例51】利用while循环编写程序计算1~100(含1和100)的所有整数的和。 考虑用一个变量res存储整数和,该变量一开始赋值为0,然后循环100次,依次把1~100累加到变量res中,最后变量res就是所要的结果。具体程序如下: 程序51求1~100的整数的和 1 2 3 4 5 6 7 8 #计算1~100的整数的和 res=0#存放累加和 i=1 #循环控制变量 while i<=100:#循环100次 res+=i #累加 i=i+1 #循环控制变量加1 print("1+2+3+…+100=",res). 运行结果: 1+2+3+…+100= 5050 实际上利用循环求1~100的整数的和是比较低效的,因为1~100的整数构成了一个公差为1的等差数列,所以利用等差数列的求和公式可以无须循环而直接得到结果。此外,基于Python自身附带的函数和数据结构,使用如下这样的一句代码也可以解决该问题。 print("1+2+3+…+100=",sum(range(1,101)))#利用sum()函数求累加和 在用while循环时候,要避免写成死循环。所谓死循环,就是无法结束的循环,主要原因是while后面的条件一直成立。例如例51的程序中如果循环体内忘记把循环控制变量加1,就会变成死循环。当然死循环也不是一无所用的,工业控制领域的很多程序会故意写成死循环,直到设备被关机为止。 【例52】编写程序,让计算机产生多道随机数构成的加法口算题,每个运算数为0~100,让用户输入结果,如果用户做对了,就继续循环,直到用户做错了题目才停止,最后显示用户做对了多少题。 本题需要使用Python内置的random模块来产生随机数,random模块提供了多个产生不同随机数的函数,表51列出了部分常用函数以及说明,这里选用其中的randint()函数产生1~10000的随机整数。 表51random模块中部分常用函数以及说明 函数说明 seed(a=None)初始化随机发生器的种子数,如果a省略则使用当前时间 random()生成一个0~1的随机浮点数 randint(a,b)生成一个a~b的随机整数 uniform(a,b)生成一个a~b的随机浮点数 choice(seq)从序列seq中随机抽取一个元素 shuffle(lst)对列表lst中元素随机排序,返回值是None 为了统计用户做对的题数,可以设置一个初始值为0的计数器变量,做对一题后立刻让该变量的值加1。 此外,本题并不知道用户到底做多少题结束,只要用户输入的数字与两个随机数的和不相等就继续循环,可见是一个未知循环次数的问题。具体程序如下: 程序52加法口算题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #加法口算题 import random opt1=random.randint(1,100) opt2=random.randint(1,100) print("{:2d}+{:2d} = ".format(opt1,opt2),end="") answer=int(input()) count=0 while answer==opt1+opt2: opt1=random.randint(1,100) opt2=random.randint(1,100) print("{:2d}+{:2d} = ".format(opt1,opt2),end="") answer=int(input()) count+=1 print("合计做对了{0}题".format(count)) 运行结果: 41+52 = 93 76+19 = 95 26+61 = 87 64+12 = 76 36+67 = 103 44+47 = 91 75+62 = 137 82+62 = 144 28+38 = 66 77+100 = 177 34+23 = 57 18+17 = 35 1+97 = 98 84+55 = 133 合计做对了13题 在实际应用中,经常使用while循环确保用户的输入一定在一个合法的范围内,从而保证后续处理数据的可靠性。如下代码片段就可以确保用户的输入范围一定为1~60。 num=int(input("请输入一个1~60的整数")) while num<1 or num>60: num=int(input("请输入一个1~60的整数")) print(num) #执行到这里,一定是1~60的整数了 5.3for循环 利用for循环可以把序列中所有的元素按照原始的顺序遍历一遍,因为序列的大小是可知的,所以for循环可以看成是已知循环次数的循环。它的语法规则如下: for 迭代变量 in 序列: 语句块1 else: 语句块2 其中,用到的序列通常是列表、元组、集合和range对象。语句块1是循环体,被执行的次数等同于序列的长度。和while循环类似,只有当for循环正常结束时才会执行语句块2,else部分同样可以省略。 Python有一个内置的range迭代器类,它用于产生指定范围内的可迭代range对象。它经常被用于控制for循环的次数。迭代器类带来的一个优点是如果产生的元素数量巨大,它无须预先占用大量内存,而是每次迭代时才得到下一个元素。 它有两个调用形式,分别如下: range(起始值, 终止值, 步长) #产生起始值到终止值(不含)之间指定步长的迭代器对象 range(终止值) #产生0到终止值(不含)之间的步长为1的迭代器对象 第一个调用形式的步长可以省略,如果省略就以1作为步长。 【例53】利用for循环和range对象编写程序计算1~100(含1和100)的所有整数的和。 利用for循环和range对象编写该程序,因为变量i的值是通过迭代器返回,所以几乎没有机会把它写成死循环。修改后的程序如下: 程序53求1~100的整数的和 1 2 3 4 5 6 #计算1~100的整数的和 res=0 for i in range(1,101): res+=i print("1+2+3+…+100=",res) 运行结果: 1+2+3+…+100= 5050 【例54】小明是一个渔夫,坚持“三天打鱼,两天晒网”,已知每年的1月1日,小明是第一天打鱼,编写程序让用户输入一个年月日,输出当天小明是打鱼还是晒网。 根据题意知道小明的工作是5天一个周期,因此只要算出指定的日期是该年的第多少天,然后用这个天数对5求余,如果余数是1、2或3就是在打鱼,否则就是晒网。 那么如何得到一个日期是该年的第多少天?显然,只要把该日期之前全部月份的天数累加,再加上这个日期的日就可以得到。众所周知,每月的天数除了2月之外的天数都是确定的,可以用一个包含12个整型元素的列表存储每月的天数。其中2月初始化为28,然后根据用户的输入进行处理,如果是闰年则修改2月的天数为29。有了每月天数的列表,那么后续的工作就是顺理成章的事情了。具体程序如下: 程序54三天打鱼两天晒网 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #三天打鱼两天晒网 year,month,day=eval(input("请输入年月日(用逗号分开):")) monthDays=[31,28,31,30,31,30,31,31,30,31,30,31] #存储每月的天数 if year<=0: print("年不合法") elif month>12 or month<1: print("月不合法") elif day<1 or day>monthDays[month-1]: print("日不合法") else: if (year%4==0 and year%100!=0) or year%400==0: monthDays[1]=29 count=0 for i in range(month-1): count+=monthDays[i] count+=day if count%5 in [1,2,3]: print("小明在打鱼") else: print("小明在晒网") 运行结果: 请输入年月日(用逗号分开):2000,8,7 小明在晒网 5.4break、continue和pass 5.4.1break 前面讲到while和for循环在正常循环结束时,会执行else中的语句,言下之意就是循环可能非正常结束。循环的非正常结束主要包括程序出现异常退出和主动跳出两种情况。出现异常退出,这里先不讨论。所谓主动跳出,就是程序中有代码提前结束循环。 Python提供了关键字break用于从循环中主动跳出,循环体中执行到break语句时立刻结束循环,程序执行流程转向循环之后的第一条语句。在while和for循环中都可以使用break语句。 【例55】传说爱因斯坦出了一个爬楼梯的数学题,有一个长长的楼梯,如果每次爬2阶,最后余1阶; 如果每次爬3阶,最后余2阶; 如果每次爬5阶,最后余4阶; 如果每次爬6阶,最后余5阶; 如果每次爬7阶,最后刚好爬完。请问这个楼梯至少多少阶? 基于计算思维来解决本题比较容易想到的方法是枚举测试,也就是从1开始逐步向上试验每个整数,测试每一个整数对2、3、5、6和7分别求余数的结果是否合乎题意。因为是从小到大逐步增加,所以找到的答案一定是最小的解。不做深入数学分析很难确定本题到底循环多少次,因此这可以看成未知循环次数的问题,那么适合用while循环解决。一个简单的实现程序如下。 程序551爱因斯坦楼梯 1 2 3 4 5 6 7 8 9 #爱因斯坦楼梯 steps=0 while True: steps+=1 if steps%2==1 and steps%3==2 and steps%5==4 and steps%6==5 and steps%7==0: print("此楼梯至少长度为",steps) break 运行结果: 此楼梯至少长度为 119 上面的程序中有两个地方需要注意: 第一,while后面的条件写的是常量True,也就是表示循环条件永远成立; 第二,在找到并输出正确的解之后,就无须再循环,因此使用了break从循环中跳出,可见该循环并不是死循环。 现在的程序中每执行一次循环就把变量steps的值加1,而根据题意可知正确的解一定是7的倍数,因此steps可以每次加7从而减少循环的次数。另外,根据题目的第一个条件可知正确的解一定是奇数,读者可自行考虑如何进一步减少循环的次数。 5.4.2continue 关键字continue也是用于改变程序流程,它用于结束本轮循环。在while循环中遇到continue时直接跳转到循环条件判断; 在for循环中遇到continue时直接取得循环下一个迭代的值。 例55的程序假设使用continue,修改后的程序如下。 程序552爱因斯坦楼梯 1 2 3 4 5 6 #爱因斯坦楼梯 steps=0 while True: steps+=1 if steps%2!=1: continue #不满足条件1 7 8 9 10 11 12 if steps%3!=2: continue #不满足条件2 if steps%5!=4: continue #不满足条件3 if steps%6!=5: continue #不满足条件4 if steps%7!=0: continue #不满足条件5 print("此楼梯至少长度为",steps) break 运行结果: 此楼梯至少长度为 119 这种写法相当于用的是排除法,一个整数只要不满足其中一个条件,就直接开始下一轮循环,换下一个整数来试验。 通常可以通过反写前面if的条件来避免使用continue,两种写法的程序在效率上并无大的区别。但是实际应用中,把一些特殊情况列出来用continue处理,代码会具有良好的可读性。 5.4.3pass Python中的pass是空语句,它不做任何事情,一般用作占位语句,主要是为了保持程序结构的完整性。 什么是保持程序结构的完整性呢?例如,有一个for循环,目前还不需要写任何循环体,此时就可以在内部写一个pass语句。这样这个循环就符合语法了,它也确实需要循环指定的次数,只是它的循环体不做任何事情,这样的循环属于空循环。 空循环有时被用于延时,如下代码片段可以输出26个大写字母,且每个字母输出之后会有个短暂的停顿。这个停顿是因为j需要在range返回的迭代器中遍历30万次,显然这个30万次所消耗的时间是与计算机硬件相关的,因此空循环延时难以精确控制消耗的时间。 >>> for i in range(26): print(chr(65+i),end="") for j in range(300000): pass 5.5循环嵌套 如果在一个循环体内包含另外一个循环,那么就称为循环嵌套。其中while和for循环不仅可以自己嵌套,也允许互相嵌套。理论上循环嵌套的层次可以是任意层,但通常嵌套不要超过三层,否则代码可读性和可修改性都较差。 【例56】编写程序找出10~1000的所有完全数。所谓的完全数又称完美数或完备数。完全数是一种特殊的自然数,它的真因子的和等于它自己。 这个题目的基本解决办法是用循环把10~1000的整数全部遍历一遍,遍历时测试每一个整数是否是完全数。 程序56找10~1000的完全数 1 2 3 4 5 6 7 8 9 #找10~1000的完全数 for i in range(10,1001): #遍历求解空间 temp=0 #存储因子的和,初始化为0 for j in range(1,i):#从1循环到i-1 if i%j==0:#判断是否为i的因子 temp+=j if temp==i:#判断i与真因子的和是否相等 print(i) 运行结果: 28 496 【例57】编写程序解决《孙子算经》中的百钱百鸡问题。该问题描述如下: 鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何? 如果用数学方程组来解决该问题。设公鸡是x只,母鸡是y只,小鸡是z只,那么可以得到方程组如下: x+y+z=100 5x+3y+z/3=100 但是该方程组只有两个方程却包含三个未知数,因此不是特别好解。此时可以采用枚举的计算思维进行验证,它的基本思想是根据题目的条件确定答案的范围,并在此范围内对所有可能的情况逐一验证,直到全部情况验证完毕。如果某个情况验证后符合题目的全部条件,则为本问题的一个解; 若全部情况验证后都不符合题目的全部条件,则本题无解。 针对本例而言每种鸡的数量不可能小于0,不可能大于100,这就是大致的范围,具体的条件是百钱和百鸡。 因此可以得到表52,其中第一列是公鸡数量,第二列是母鸡数量,第三列是小鸡数量,第四列表示百钱条件是否成立,第五列表示百鸡条件是否成立。 从表52可以看到,第一行数据三者都是0,百钱和百鸡两个条件都不成立; 当公鸡数量为4、母鸡为18、小鸡为78时,两个条件都成立,因此这就是一组满足条件的解。 表52百钱百鸡的枚举情况列表 公鸡数量/只母鸡数量/只小鸡数量/只是否刚好百钱是否刚好百鸡 000FalseFalse 001FalseFalse 002FalseFalse … 41878TrueTrue … 41977TrueFalse … 100100100FalseFalse 基于以上思想,可以用一个三层循环嵌套来实现验证过程。百钱百鸡问题枚举时有两个验证条件: 一个是否刚好百钱; 另一个是否刚好百鸡。但是实质上还有一个隐藏条件,小鸡数量一定是3的倍数。因此程序中需要一个保证小鸡数量对3求余是否等于0的判断。可以得到如下程序。 程序571百钱百鸡问题 1 2 3 4 5 6 7 8 9 #百钱百鸡问题 for cock in range(0,101): for hen in range(0,101): for chick in range(0,101): if cock*5+hen*3+chick//3==100 and cock+hen+chick==100 and chick%3==0: print("公鸡{0:2d} 母鸡{1:2d} 小鸡 {2:2d}".format(cock,hen,chick)) 运行结果: 公鸡 0 母鸡25 小鸡75 公鸡 4 母鸡18 小鸡78 公鸡 8 母鸡11 小鸡81 公鸡12 母鸡 4 小鸡84 5.6循环优化 例57的程序在执行时,使用了三层循环,其中的if语句会被执行的次数为101*101*101=1030301,大约100万次,那么这个程序是否可以优化呢? 现在再来仔细考虑一下该问题,因为每个公鸡是5钱,所以百钱最多只能买20只公鸡; 同样每个母鸡是3钱,百钱最多只能买33只母鸡; 另外,既然小鸡的数量一定是3的倍数。那么可以直接让小鸡的数量每次加3。因此前面表格52就可以优化成表53,其中的行数显著减少。 表53百钱百鸡优化后的枚举情况列表 公鸡数量/只母鸡数量/只小鸡数量/只是否刚好百钱是否刚好百鸡 000FalseFalse 003FalseFalse 006FalseFalse … 41878TrueTrue … 203399FalseFalse 根据表53很容易写出如下实现程序。 程序572百钱百鸡问题 1 2 3 4 5 6 7 8 #百钱百鸡问题优化1 for cock in range(0,21): for hen in range(0,34): for chick in range(0,101,3): if cock*5+hen*3+chick//3==100 and cock+hen+chick==100: print("公鸡{0:2d} 母鸡{1:2d} 小鸡 {2:2d}".format(cock,hen,chick)) 和之前的代码相比,首先缩小了外面两层循环控制range函数的区间。其次在枚举小鸡数量时使用了range函数的第三个参数。因为chick一定是3的倍数,那么if条件中的判断chick是否是3的倍数的条件被删除了。 在这样优化后,在保证正确性的前提下程序比刚才简洁,此外该程序循环的次数已经变成了21*34*34=24276。从100万次变成2万多次,降低了两个数量级,显然是一个巨大的进步。那么该问题是否可以进一步优化呢?答案是肯定的。 如果在枚举公鸡和母鸡的数量时,小鸡的数量通过100减去公鸡和母鸡的数量得到,就可以避免验证很多数量不是百鸡的情况。那么验证的过程就从前面的一个5列表格转换成了如表54所示的4列表格,其中小鸡的数量是利用100减去公鸡和母鸡的数量计算而来,因此只需要判断是否刚好是百钱。 表54百钱百鸡只枚举公鸡和母鸡的情况列表 公鸡数量/只母鸡数量/只小鸡数量/只是否刚好百钱 00100False 0199False 0298False … 41878True … 203347False 根据表54的枚举方法实现的程序如下。 程序573百钱百鸡问题 1 2 3 4 5 6 7 8 #百钱百鸡问题优化2 for cock in range(0,21): for hen in range(0,34): chick =100-cock-hen if cock*5+hen*3+chick//3==100 and chick%3==0: print("公鸡{0:2d} 母鸡{1:2d} 小鸡 {2:2d}".format(cock,hen,chick)) 此时该程序已经减少了一层循环,中间的循环体判断是否满足百钱的条件,以及小鸡的数量是否是3的倍数,也就是说程序已经从一个三层循环嵌套优化成了两层循环嵌套,此时总循环的次数是21×34=714。 现在已经从最初的循环大约100万次优化到了大约700次,降低了4个数量级。那么该问题是否可以进一步优化呢?答案是肯定的,再仔细看一下刚才的方程组: x+y+z=100① 5x+3y+z/3=100② 将②乘以3再减去①,可以得到 14x+8y=200 一个方程两个未知数,计算思维的枚举法可以再次发挥作用。可以得到如下程序。 程序574百钱百鸡问题 1 2 3 4 5 6 7 8 #百钱百鸡问题优化3 for cock in range(0,21): hen=(200-cock*14)//8 if hen<0: continue chick=100-cock-hen if cock*5+hen*3+chick//3==100 and chick%3==0 : print("公鸡{0:2d} 母鸡{1:2d} 小鸡{2:2d}".format(cock,hen,chick)) 从上面的程序可以看到,现在该问题已经变成了一层循环,合计循环21次,母鸡的数量通过表达式直接计算出来,然后再利用总数100的约束条件得到小鸡的数量,最后用百钱的条件判断是否是满足条件的解。 5.7典型例题 【例58】编写程序让用户输入x和n的值,计算多项式的值,多项式的形式如下: xn +xn-1 +…+x2+x+1 输出结果时保留小数点后2位。 这样的题目实现时主要是找到合适的通项,然后就可以用循环解决。将上述多项式逆序并稍做整理后可以得到如下形式: 1+x1+x2+…+xn-1+xn 基于上述形式可以写出如下程序。 程序58计算多项式的值 1 2 3 4 5 6 7 #计算多项式的值 x=float(input("请输入浮点数x:")) n=int(input("请输入整数n:")) result=1 item=1 8 9 10 11 for i in range(n): item*=x result+=item print("{:.2f}".format(result)) 运行结果: 请输入浮点数x:3.2 请输入整数n:7 49 97.33 【例59】有一个列表lst存储了n个1~100的正整数,编写程序统计出其中给的好数对的个数。好数对的描述如下: 如果一组整数(i,j)满足nums[i]==nums[j]并且i 1)次,那么该数字出现好数对的数量其实是一个组合问题,就是从n个数字中每取出2个就可以产生一个好数对,根据组合数计算公式,该数字产生好数对n*(n-1)/2个。这样的话,只需要统计出列表中每个数字产生的次数,然后对次数大于1的情况利用排列数进行计算并累加就可以得到最终结果。因为列表中的数字都为1~100,所以可以另外用一个列表来存储每个数字出现的次数。基于上述思想可以写出如下代码,现在的代码多用了一个包含100个元素的列表,但是已经把代码变成了一层循环。 程序592统计好数对的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #统计好数对的值 lst=list(map(int,input("请输入多个用空格分开的1~100的整数").split())) temp=[0]*100 for val in lst:#统计每个数字出现的次数 temp[val-1]+=1 count=0 for val in [item for item in temp if item>1]: #只需要处理出现次数大于1的情况 count+= val*(val-1)//2 print("好数对的数量为:",count) 【例510】现有A、B、C、D四个犯罪嫌疑人被警察捉住,已经肯定其中有且只有一个人是罪犯。四人现在分别说了一句话,A说他不是罪犯,B说C是罪犯,C说是D是罪犯,D说C骗人。也已经知道其中有三个人说了真话,一个人说了假话,现在编写程序帮助警察找出罪犯。 本例如果依靠逻辑推理来解决,通常有排除法、推理法、列表法等。例如基于假设和列表可以给出表55。 表55真值表 如果是A如果是B如果是C如果是D A: 不是我FalseTrueTrueTrue B: 是CFalseFalseTrueFalse C: 是DFalseFalseFalseTrue D: C骗人(不是D)TrueTrueTrueFalse 表55的第一列是A、B、C、D的四句结论,第一行是假设A、B、C、D分别是罪犯的情况,然后对每一个单元格根据假设进行判断是否成立。例如: 如果是A,那么和A的结论“不是我”互相矛盾,可以在对应单元格写False,同样B的结论“是C”在此假设下也是不成立的,C的结论“是D”也不成立,只有D的结论“C骗人”成立,因此这一列前三个单元格是Fasle,最后一个单元格是True。用此办法可以为每个单元格做出标记。 因为知道合计三个人说了真话,所以找到标记了三个True一个Fasle的那一列就是本例的解,就表5.4中数据可以看出C是罪犯。 就本例而言,可以把A、B、C、D放在列表中,然后用for循环对其进行枚举,进入循环后首先设置一个计数器变量count=0,然后用四条if语句对应四条结论,从而决定是否要给count加1,每轮循环结束后检查count的值,如果为3,说明成立了三条结论(也就是说三个人说了真话),那么就找到了题目的解。基于上述思想可以编写出如下代码。 程序510谁是罪犯 1 2 3 4 5 6 7 8 9 10 11 12 #谁是罪犯 candidates=['A','B','C','D'] for ch in candidates: count=0 if ch!='A': count+=1 if ch=='C': count+=1 if ch=='D': count+=1 if ch!='D': count+=1 if count==3: print("罪犯是:",ch) break 运行结果: 罪犯是: C 【例511】约瑟夫环报数。N(3≤N≤1000)个人围成一圈,每人顺序编号,然后从1开始依次加1报数,当报数为k(1≤k≤9)的倍数或者末尾是k时,删除此人(后面不再参加报数)。最后留下的是几号? 本例实质是一个稍作变化的约瑟夫环问题,可以编写程序模拟该过程,从而得到最终结果。借助于列表可以方便地解决本问题,首先将1~N的每个整数存储到列表中,然后对列表从左向右遍历,当遍历到最后一个元素时再回到第0个元素,遍历过程中遇到满足删除条件的元素时,将该元素删除,直到列表剩余一个元素为止。基于该思路可以得到如下代码。 程序5111约瑟夫环报数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #约瑟夫环报数 n,k=list(map(int,input("请输入n和k:").split())) num=[0]*1000 count=1 step=0 for i in range(n):#初始化数据 num[i]=i+1 while n>1: if step==n: step=0 continue if count%k==0 or count%10==k: for i in range(step,n-1):#删除 num[i]=num[i+1] n-=1 else: step+=1 count+=1 print("最后留下{0}号".format(num[0])) 运行结果: 请输入n和k:100 5 最后留下47号 当列表较大时列表删除是一个耗时操作,而刚才的解法中需要多次删除元素,为了提高效率,可以实现“假删除”,就本题而言可以把删除元素的值赋为-1,在计数时忽略值为-1的元素。此时可以得到如下程序。 程序5112约瑟夫环报数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #约瑟夫环报数 n,k=list(map(int,input("请输入n和k").split())) num=[0]*1000 count=1 step=0 length=n while length>1: if step==n: step=0 continue if num[step]==1: step+=1 continue elif count%k==0 or count%10==k: num[step]=1 length-=1 count+=1 step+=1 for i in range(n): if num[i]==0: print("最后留下{0}号".format(i+1)) 运行结果: 请输入n和k:32 3 最后留下19号 习题 1. 从键盘输入一个十进制正整数,利用列表和除二取余法,计算出该数字的二进制值。 2. 从键盘输入一个十六进制正整数,计算出该数字的二进制值。 3. 随机生成一个0~100的整数,判断这个数是否等于50,如果不等于则重新随机生成。最后输出一共随机生成了多少次。 4. 从键盘输入一个字母,如果输入的是小写英文字母,则将其转换为大写字母后显示输出; 如果输入的是大写英文字母,则将其转换为小写字母后显示输出; 如果既不是小写英文字母也不是大写英文字母,则原样显示。 5. 给定一个二进制字符串,例如"10100101",计算并输出字符串中0的个数以及所有数字之和。 6. 一副球拍售价15元,球3元,水2元。现在有200元,要求每种商品至少购买一个,有多少种可能正好把这200元花完? 7. 从键盘输入一批学生的成绩(成绩为整数),输入0或负数则输入结束,然后统计并输出优秀(大于或等于90分)、通过(60~59分)和不及格(小于60分)的人数。 8. 用 * 输出一个等腰三角形。提示用户输入一个整数n,代表输出的等边三角形由n行 * 组成。例如,输入n=3。输出: * *** ***** 9. 用 * 输出一个正六边形,输入一个整数n代表输出的正六边形的边的长度(*的数目)。 例如输入n=3。输出: *** **** ***** **** *** 10. 二维列表中有一组人员的姓名和年龄数据,编写程序找到这组数据中年龄最大的人,输出相关人员信息。 11. 计算s=a+aa+aaa+aaaa+…+aa…a的结果,其中a是0~9的数字,有n个数相加(最大的数有n位)。例如2+22+222+2222+22222(此时共有5个数相加)。编写程序,随机生成a,从键盘输入n,将公式进行输出,并输出计算结果。 12. 有一列表,存放有若干同学的姓名,编写程序将这些信息分成两组,元素顺序为偶数的放在一组,奇数的放在另一组,然后将分组的信息进行输出,输出格式如下: 分组1234 奇数组张三王五孙七吴九 偶数组李四赵六周八 13. 假设某人每月计划给公交卡充一些钱用于坐公交,已知当地公交票费用按照表56进行计算。编写一个程序,从键盘输入充值金额,计算并输出充值的金额最多能坐多少次公交。 表56公交费用表 当月累计次数票价 1~102元(原价) 10~20原价9.5折 21~50原价8折 51次以上原价5折 14. 一条地铁线一共有30个站点,这些站点编号为0~29,已知所有相邻站点间的距离列表distance,distance[i]表示编号为i的车站到编号为i+1车站间的距离,单位为km,站距均为1.5~3.0km。假设地铁的车票和距离有关,基础票价为2元(5km内),超过5km后每5km增加1元(不满5km的部分按1元计算)。编写一个程序,计算花n元最多能乘坐多少站。 15. 输出一个乘法表。要求输入一个整数n,输出 n*n 的乘法表,乘法表打印出来为下三角样式,格式工整。例如,输入 n=4。输出: 1234 11 224 3369 4481216 15. 提示用户输入一个整型数n(n代表后续需要输入整型数的数量),将n个整型数加起来并输出,如果输入的是非整型数则提示当前的输入非法,需要重新输入数值,如果输入n=0则代表退出程序,否则继续提示用户输入新的n。 例如: Please input the number of numbers:(假设输入 n=3) Please input number 1: (假设输入 3) Please input number 2: (假设输入 4) Please input number 3: (假设输入 5) 输出: sum = 12 Please input the number of numbers: … Please input the number of numbers:(假设输入 n=0,则退出程序) 17. 提示用户输入一个整数n,然后输出 [1,n)的所有的素数。例如,输入n = 10。输出: 2,3,5,7。 18. 矩阵相加: 提示用户输入一个数字n,为矩阵的行数,再提示用户输入一个数字m,为矩阵的列数,接下来,提示用户输入2*n*m个数字(每次输入一个数字)。输出 C=A+B。 例如,输入: Please input the number of rows:(假设输入 n=2) Please input the number of columns:(假设输入 m=3) Please input A[0,0]: 1 Please input A[0,1]: 1 Please input A[0,2]: 1 Please input A[1,0]: 1 Please input A[1,1]: 1 Please input A[1,2]: 1 Please input B[0,0]: 2 Please input B[0,1]: 2 Please input B[0,2]: 2 Please input B[1,0]: 2 Please input B[1,1]: 2 Please input B[1,2]: 2 输出: C = [[3, 3, 3], [3, 3, 3]] 19. 有一个弹性小球从H(H≥100)m的高度做自由落体运动,每次落地后会反弹,反弹高度为上次下落高度的一半,然后继续下落,如此反复(假设反弹和下落不会停止); 编写程序,计算并输出小球第N次落地时,共经过了多少米,第N次反弹的高度为多少。