master
/ 3.3.3 while 循环语句.ipynb

3.3.3 while 循环语句.ipynb @masterview markup · raw · history · blame

Notebook

3.4 while循环

循环结构表示程序__重复执行__某个或某些操作,直到某__条件为假(或为真)__时才可终止循环。 在问题求解过程中,很多时候需要重复做一件事情很多次。这时可以选择__重复使用这些语句__来解决问题,也可以使用__循环控制结构__来完成。

人类重复做某件事情次数越多,出错的概率越大,所以数学家们研究了各种等差数列、等比数列的求和公式、求积公式,把重复多次的工作变成一个表达式的求解,以降低出错的概率。 计算机与人不同,计算机可以快速精准的完成重复性的工作,而循环结构的使用,既可以__减少代码行数__、又可以__简化业务逻辑__,提高程序的__可读性__

Python中控制循环有两种结构:

1. while 循环
2. for...in 遍历循环

while循环语句一般用于__循环次数不确定__的情况下,通过判断是否满足某个指定的__条件__来决定是否进行下一次循环,也称为条件循环语句。

判断条件的返回值为__布尔型__,即__True____False__,任何__非0____非空__的值均为True。

当判断__条件表达式__的结果为__True__时,执行while下面缩进部分语句块中的全部语句,然后再次检查判断条件的值,重复以上操作,直到判断条件的结果为False时结束循环。

在循环体的语句中,一定有一条语句会使循环判断__条件____结果发生变化__,使之在经过有限次运行后能够变__“假”__,从而__结束循环__。或在循环体中设定一个__边界条件__,当条件满足时,执行__break语句__直接终止当前循环。

while循环的语法如下:

while 判断条件
    语句块(使判断条件表达式的值趋向于False)

实例 4.3 斐波那契数列前n项

1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,……

斐波那契数列又称黄金分割数列,这个数列从第3项开始,后一项的值总是与他前面两项的和的值相等。

在数学上,以递推的方法定义:

F(1)=1
F(2)=1 
F(n)=F(n - 1)+F(n - 2)    (n  3n  N*)

根据数学上的递推公式,可以先把变量名a,b指向开始两个值 1 和 1,然后构建一个循环。
在循环中输出 a 的值,然后计算 a + b 的和,将标签 a 指向对象 b,将标签 b 指向加和产生的新对象上作为新的一项,这个操作可以用__同步赋值语句__实现:

a, b = b, a + b

In [ ]:
a, b = 1, 1             # 初始两项的值
while a < 1000:         # 设定终止条件,当a值大于或等于1000时终止循环
    print(a, end=',')   # 输出当前a 值,不换行,用逗号结束输出
    a, b = b, a + b     # 将 b 和 a + b 值分别赋值给变量名a和b
# 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

while循环还有一个重要的应用就是构造__无限次循环__,在循环体中使用__if语句__判定是否__达到结束循环的条件__,当满足条件时,用__break语句__终止循环。

此时需要注意,在程序执行过程中,__务必要有__使if 语句中的表达式结果__趋于False__的语句,以避免程序__陷入无限循环__

实例 4.4 Leibniz公式计算圆周率

历史上有许多计算圆周率$pi$的公式,其中,格雷戈里和莱布尼茨发现了下面的公式(可由等比数列求和公式变换并积分获得):

这个公式累加1项是4.00,累加2项是2.67,累加3项是3.47...趋进于圆周率值。编程对其各项进行累加,直到最后一项的绝对值小于$10^{-6}$为止,输出此时的圆周率值。

公式中每一项的分母数字正好相差2,符号正负交替,可以利用循环结构求解。
因循环次数未知,但知道终止条件,所以可以用while加判断条件控制循环,条件为True时进入循环,在循环里使判断条件值发生改变,待判断条件值为False时,不再进入循环。

In [2]:
pi, i = 0, 1                # 设定pi初值0, i为分母变量名
sign = 1                    # sign用于改变符号
while 1 / i >= 1e-6:        # 条件表达式值为True时进入循环
    pi = pi + sign * 1 / i  # 累加,每次循环加上最后一项
    sign = - sign           # 每循环一次改变一次符号,实现正负项交替
    i = i + 2               # 下一项分母比当前项大2,此语句也会使1/i值变小
print(pi * 4)               # 乘4得以π的计算值,放循环外只输出一次

也可用while加True或一个结果为True的表达式,使循环判断条件永远为True,构建无限次循环。
在循环体中,判断循环控制条件1/i < $10^{-6}$的结果为False时,用break__结束循环__,使循环可以在有限次数内结束。
此处brek作用是程序执行到此语句时,提前__结束循环__

In [1]:
pi, i = 0, 1                    # 设定pi初值0, i为分母变量名
sign = 1                        # sign用于改变符号
while True:                     # 构建无限循环
    if 1 / i < 1e-6:            # 最末项大于等于10-6时
        break                   # 结束循环
    else:                       # 否则执行累加语句
        pi = pi + sign * 1 / i  # 累加,每次循环加上最后一项
        sign = - sign           # 每循环一次改变一次符号,实现正负项交替
        i = i + 2               # 下一项分母比当前项大2
print(pi * 4)                   # 乘4得以π的计算值,放循环外只输出一次

输出:
3.141590653589692

这个公式简单而优美,但收敛的较慢。
增加计算的项数可以提高计算精度,将终止条件设为$10^{-9}$时可以提高3位数的精度,再想提高精度,时间就难以接受了。
3.141592651589258

编写一个程序,可以重复接收用户输入的学生姓名,当输入的姓名为非空字符串时,输出“欢迎你,***同学!”(***处为用户输入的学生姓名),当用户直接输入回车时(此时input()函数接收到的是空字符串)结束程序。
In [7]:
user_name = input()
while user_name:  # 当输入非空时执行循环
    print(f'欢迎你,{user_name}同学!')  # 输出
    user_name = input()  # 循环体内的输入语句可重复接收输入
In [ ]:
# 可用无限循环方法,用break结束程序

while True:
    user_name = input()
    if user_name != '':  # 当user_name为非空字符串时,即输入非空时
        print(f'欢迎你,{user_name}同学!')  # 输出
    else:
        break
if user_name != '': 等价于: if user_name: 后者表示当user_name为True时,因为user_name是input()结果,必然是字符串,所以仅当user_name为空字符串时,即直接输入回车时为False,其他情况均为True。
In [ ]:
while True:
    user_name = input()
    if user_name:  # 当user_name为非空字符串时,即输入非空时
        print(f'欢迎你,{user_name}同学!')  # 输出
    else:
        break
In [ ]:
# 可用前面讲过的赋值表达式更简洁的实现

while user_name := input():            # 当输入非空时执行循环
    print(f'欢迎你,{user_name}同学!')  # 输出

练一练

重复接收用户输入,以字符串形式输出,当用户直接回车时(此时input()函数接收到的是空字符串)结束程序。老师在统计学生成绩,需要将试卷上的百分制转为五分制,但不知道学生有多少人。请编写一个成绩转换程序。 每次输入一个百分制的学生成绩,成绩大于或等于90且小于或等于100的输出为“A”,成绩大于或等于80且小于90的输出为“B”,成绩大于或等于70且小于80的输出为“C”,成绩大于或等于60且小于70的输出为“D”,成绩小于60的输出为“E”。 输入数据不合法时输出“data error!”。 老师可重复输入成绩进行转换,直接输入回车时结束程序‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬ 输入: 每次输入一个浮点数,代表百分制成绩; 重复输入,输入回车时结束程序‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬ 输出: 根据每一次的输入值分别输出A、B、C、D、E中的一个字母或"data error!"
In [ ]:
# 补充你的代码
测试数据: 输入:100 输出:A 输入:59 输出:E 输入:78 输出:C 输入:101 输出:data error! 输入:81 输入:B 输入:回车 结束程序

4.5 应用之二分法

对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。

当数据量很大适宜采用该方法。
采用二分法查找时,数据需是排好序的。
基本思想:
假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较,
如果当前位置arr[k]值等于key,则查找成功;
若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1];
若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high],
直到找到为止,时间复杂度:O(log(n))

二分法求平方根

设计一个用二分法计算一个大于或等于 1 的实数 n 的平方根的函数sqrt_binary(n),计算精度控制在计算结果的平方与输入的误差不大于1e-6。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬ 注:初始区间取[0,n]

In [ ]:
num = float(input())  # 输入一个大于1的数
low, high = 0, num    # 初始化二分查找的上下界
while True:            # 开始无限循环
    x = (high + low) / 2  # 计算上下界的中点
    if abs(x ** 2 - num) <= 1e-6:  # 如果中点的平方与输入的数的差的绝对值小于等于1e-6
        print(x)                   # 打印中点的值
        break                      # 结束循环
    elif x ** 2 - num < 0:         # 如果中点的平方小于输入的数
        low = x                    # 将中点设为新的下界
    else:                          # 如果中点的平方大于输入的数
        high = x                   # 将中点设为新的上界
In [ ]:
import math


def sqrt_binary(num):
    low, high = 0, num
    while True:
        x = (high + low) / 2
        if abs(x ** 2 - num) <= 1e-6:
            return x
        elif x ** 2 - num < 0:
            low = x
        else:
            high = x


num = float(input())
if num >= 1:
    print(sqrt_binary(num))
    print(math.sqrt(num))

二分法求函数的零点

现有方程: $ f(x) = x^5-15x^4+85x^3-225x^2+274x-121p $ 已知f(x)在[1.5,2.4]区间有且只有一个根,用二分法求解该根。

In [ ]:
n = int(input())          # 输入一个整数n,表示小数点后的位数
low, high = 1.5, 2.4      # 初始化二分查找的上下界
while True:               # 开始循环
    x = (low + high) / 2  # 计算上下界的中点
    # 计算多项式的值
    y = x ** 5 - 15 * x ** 4 + 85 * x ** 3 - 225 * x ** 2 + 274 * x - 121
    if abs(y) < 1 * 10 ** -n:      # 如果多项式的值的绝对值小于10的-n次方
        print('{:.6f}'.format(x))  # 打印中点的值,保留6位小数
        break                      # 结束循环
    elif y < 0:                    # 如果多项式的值小于0
        high = x                   # 将中点设为新的上界
    else:                          # 如果多项式的值大于0
        low = x                    # 将中点设为新的下界
In [ ]:
def f(x):
    return x ** 5 - 15 * x ** 4 + 85 * x ** 3 - 225 * x ** 2 + 274 * x - 121


def bisection_method(low, high):
    while True:
        mid = (low + high) / 2
        if abs(f(mid)) < 1 * 10 ** -n:
            return '{:.6f}'.format(mid)
        elif f(mid) < 0:
            high = mid
        else:
            low = mid


if __name__ == '__main__':
    n = int(input())
    Low, High = 1.5, 2.4
    print(bisection_method(Low, High))

二分法猜数游戏

写一个1-1024之间的整数,猜测这个数字,每欠猜测后给出“大了”“小了”“猜中”的提示

In [ ]:
import random

x = random.randint(1, 1024)  # 随机产生一个0-1024之间的整数
for i in range(10):          # 允许猜10次
    guess = int(input())     # 输入猜的整数
    if guess == x:
        print('猜中了')      # 猜中则终止循环
        break
    elif guess > x:
        print('猜的数大了')
    else:
        print('猜的数小了')
else:
    print('你用完了十次机会也没猜中哦')
In [6]:
True+1
print(bool(""))
False
In [ ]: