{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 3.4 while循环"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"循环结构表示程序<font color=Red>__重复执行__</font>某个或某些操作,直到某<font color=Red>__条件为假(或为真)__</font>时才可终止循环。\n",
"在问题求解过程中,很多时候需要重复做一件事情很多次。这时可以选择<font color=Red>__重复使用这些语句__</font>来解决问题,也可以使用<font color=Red>__循环控制结构__</font>来完成。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"人类重复做某件事情次数越多,出错的概率越大,所以数学家们研究了各种等差数列、等比数列的求和公式、求积公式,把重复多次的工作变成一个表达式的求解,以降低出错的概率。\n",
"计算机与人不同,计算机可以快速精准的完成重复性的工作,而循环结构的使用,既可以<font color=Red>__减少代码行数__</font>、又可以<font color=Red>__简化业务逻辑__</font>,提高程序的<font color=Red>__可读性__</font>。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python中控制循环有两种结构:\n",
"\n",
"```python\n",
"1. while 循环\n",
"2. for...in 遍历循环\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"while循环语句一般用于<font color=Red>__循环次数不确定__</font>的情况下,通过判断是否满足某个指定的<font color=Red>__条件__</font>来决定是否进行下一次循环,也称为条件循环语句。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"判断条件的返回值为<font color=Red>__布尔型__</font>,即<font color=Red>__True__</font> 或 <font color=Red>__False__</font>,任何<font color=Red>__非0__</font>或<font color=Red>__非空__</font>的值均为True。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"当判断<font color=Red>__条件表达式__</font>的结果为<font color=Red>__True__</font>时,执行while下面缩进部分语句块中的全部语句,然后再次检查判断条件的值,重复以上操作,直到判断条件的结果为False时结束循环。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在循环体的语句中,一定有一条语句会使循环判断<font color=Red>__条件__</font>的<font color=Red>__结果发生变化__</font>,使之在经过有限次运行后能够变<font color=Red>__“假”__</font>,从而<font color=Red>__结束循环__</font>。或在循环体中设定一个<font color=Red>__边界条件__</font>,当条件满足时,执行<font color=Red>__break语句__</font>直接终止当前循环。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"while循环的语法如下:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"while 判断条件:\n",
" 语句块(使判断条件表达式的值趋向于False)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 实例 4.3 斐波那契数列前n项 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,……\n",
"\n",
"斐波那契数列又称黄金分割数列,这个数列从第3项开始,后一项的值总是与他前面两项的和的值相等。\n",
"\n",
"在数学上,以递推的方法定义:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"F(1)=1\n",
"F(2)=1 \n",
"F(n)=F(n - 1)+F(n - 2) (n ≥ 3,n ∈ N*)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"根据数学上的递推公式,可以先把变量名a,b指向开始两个值 1 和 1,然后构建一个循环。 \n",
"在循环中输出 a 的值,然后计算 a + b 的和,将标签 a 指向对象 b,将标签 b 指向加和产生的新对象上作为新的一项,这个操作可以用<font color=Red>__同步赋值语句__</font>实现:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"a, b = b, a + b\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src = \"images/ch3/8.png\" width = \"480\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src = \"images/ch3/9.gif\">"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a, b = 1, 1 # 初始两项的值\n",
"while a < 1000: # 设定终止条件,当a值大于或等于1000时终止循环\n",
" print(a, end=',') # 输出当前a 值,不换行,用逗号结束输出\n",
" a, b = b, a + b # 将 b 和 a + b 值分别赋值给变量名a和b\n",
"# 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"while循环还有一个重要的应用就是构造<font color=Red>__无限次循环__</font>,在循环体中使用<font color=Red>__if语句__</font>判定是否<font color=Red>__达到结束循环的条件__</font>,当满足条件时,用<font color=Red>__break语句__</font>终止循环。\n",
"\n",
"此时需要注意,在程序执行过程中,<font color=Red>__务必要有__</font>使if 语句中的表达式结果<font color=Red>__趋于False__</font>的语句,以避免程序<font color=Red>__陷入无限循环__</font>。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 实例 4.4 Leibniz公式计算圆周率 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"历史上有许多计算圆周率$pi$的公式,其中,格雷戈里和莱布尼茨发现了下面的公式(可由等比数列求和公式变换并积分获得):"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src = \"images/ch3/10.png\" width = \"480\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"这个公式累加1项是4.00,累加2项是2.67,累加3项是3.47...趋进于圆周率值。编程对其各项进行累加,直到最后一项的绝对值小于$10^{-6}$为止,输出此时的圆周率值。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"公式中每一项的分母数字正好相差2,符号正负交替,可以利用循环结构求解。 \n",
"因循环次数未知,但知道终止条件,所以可以用while加判断条件控制循环,条件为True时进入循环,在循环里使判断条件值发生改变,待判断条件值为False时,不再进入循环。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"pi, i = 0, 1 # 设定pi初值0, i为分母变量名\n",
"sign = 1 # sign用于改变符号\n",
"while 1 / i >= 1e-6: # 条件表达式值为True时进入循环\n",
" pi = pi + sign * 1 / i # 累加,每次循环加上最后一项\n",
" sign = - sign # 每循环一次改变一次符号,实现正负项交替\n",
" i = i + 2 # 下一项分母比当前项大2,此语句也会使1/i值变小\n",
"print(pi * 4) # 乘4得以π的计算值,放循环外只输出一次"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"也可用while加True或一个结果为True的表达式,使循环判断条件永远为True,构建无限次循环。 \n",
"在循环体中,判断循环控制条件1/i < $10^{-6}$的结果为False时,用break<font color=Red>__结束循环__</font>,使循环可以在有限次数内结束。 \n",
"此处brek作用是程序执行到此语句时,提前<font color=Red>__结束循环__</font>。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"pi, i = 0, 1 # 设定pi初值0, i为分母变量名\n",
"sign = 1 # sign用于改变符号\n",
"while True: # 构建无限循环\n",
" if 1 / i < 1e-6: # 最末项大于等于10-6时\n",
" break # 结束循环\n",
" else: # 否则执行累加语句\n",
" pi = pi + sign * 1 / i # 累加,每次循环加上最后一项\n",
" sign = - sign # 每循环一次改变一次符号,实现正负项交替\n",
" i = i + 2 # 下一项分母比当前项大2\n",
"print(pi * 4) # 乘4得以π的计算值,放循环外只输出一次"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"输出: \n",
"3.141590653589692\n",
"\n",
"这个公式简单而优美,但收敛的较慢。 \n",
"增加计算的项数可以提高计算精度,将终止条件设为$10^{-9}$时可以提高3位数的精度,再想提高精度,时间就难以接受了。 \n",
"3.141592651589258 \n"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"编写一个程序,可以重复接收用户输入的学生姓名,当输入的姓名为非空字符串时,输出“欢迎你,***同学!”(***处为用户输入的学生姓名),当用户直接输入回车时(此时input()函数接收到的是空字符串)结束程序。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
" \n"
]
}
],
"source": [
"user_name = input()\n",
"while user_name: # 当输入非空时执行循环\n",
" print(f'欢迎你,{user_name}同学!') # 输出\n",
" user_name = input() # 循环体内的输入语句可重复接收输入"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 可用无限循环方法,用break结束程序\n",
"\n",
"while True:\n",
" user_name = input()\n",
" if user_name != '': # 当user_name为非空字符串时,即输入非空时\n",
" print(f'欢迎你,{user_name}同学!') # 输出\n",
" else:\n",
" break"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"if user_name != '':\n",
"等价于:\n",
"if user_name:\n",
"后者表示当user_name为True时,因为user_name是input()结果,必然是字符串,所以仅当user_name为空字符串时,即直接输入回车时为False,其他情况均为True。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"while True:\n",
" user_name = input()\n",
" if user_name: # 当user_name为非空字符串时,即输入非空时\n",
" print(f'欢迎你,{user_name}同学!') # 输出\n",
" else:\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 可用前面讲过的赋值表达式更简洁的实现\n",
"\n",
"while user_name := input(): # 当输入非空时执行循环\n",
" print(f'欢迎你,{user_name}同学!') # 输出"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" <font face='楷体' color='red' size=5> 练一练 </font>"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"重复接收用户输入,以字符串形式输出,当用户直接回车时(此时input()函数接收到的是空字符串)结束程序。"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"老师在统计学生成绩,需要将试卷上的百分制转为五分制,但不知道学生有多少人。请编写一个成绩转换程序。\n",
"每次输入一个百分制的学生成绩,成绩大于或等于90且小于或等于100的输出为“A”,成绩大于或等于80且小于90的输出为“B”,成绩大于或等于70且小于80的输出为“C”,成绩大于或等于60且小于70的输出为“D”,成绩小于60的输出为“E”。\n",
"输入数据不合法时输出“data error!”。\n",
"老师可重复输入成绩进行转换,直接输入回车时结束程序\n",
"\n",
"输入:\n",
"每次输入一个浮点数,代表百分制成绩;\n",
"重复输入,输入回车时结束程序\n",
"\n",
"输出:\n",
"根据每一次的输入值分别输出A、B、C、D、E中的一个字母或\"data error!\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 补充你的代码\n",
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"测试数据:\n",
"输入:100\n",
"输出:A\n",
"输入:59\n",
"输出:E\n",
"输入:78\n",
"输出:C\n",
"输入:101\n",
"输出:data error!\n",
"输入:81\n",
"输入:B\n",
"输入:回车\n",
"\n",
"结束程序"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4.5 应用之二分法"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。 \n",
"<img src = \"images/ch3/10.gif\"> \n",
"当数据量很大适宜采用该方法。 \n",
"采用二分法查找时,数据需是排好序的。 \n",
"基本思想: \n",
"假设数据是按升序排序的,对于给定值key,从序列的中间位置k开始比较, \n",
"如果当前位置arr[k]值等于key,则查找成功; \n",
"若key小于当前位置值arr[k],则在数列的前半段中查找,arr[low,mid-1]; \n",
"若key大于当前位置值arr[k],则在数列的后半段中继续查找arr[mid+1,high], \n",
"直到找到为止,时间复杂度:O(log(n)) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 二分法求平方根"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"设计一个用二分法计算一个大于或等于 1 的实数 n 的平方根的函数sqrt_binary(n),计算精度控制在计算结果的平方与输入的误差不大于1e-6。 注:初始区间取[0,n]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"num = float(input()) # 输入一个大于1的数\n",
"low, high = 0, num # 初始化二分查找的上下界\n",
"while True: # 开始无限循环\n",
" x = (high + low) / 2 # 计算上下界的中点\n",
" if abs(x ** 2 - num) <= 1e-6: # 如果中点的平方与输入的数的差的绝对值小于等于1e-6\n",
" print(x) # 打印中点的值\n",
" break # 结束循环\n",
" elif x ** 2 - num < 0: # 如果中点的平方小于输入的数\n",
" low = x # 将中点设为新的下界\n",
" else: # 如果中点的平方大于输入的数\n",
" high = x # 将中点设为新的上界\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"\n",
"\n",
"def sqrt_binary(num):\n",
" low, high = 0, num\n",
" while True:\n",
" x = (high + low) / 2\n",
" if abs(x ** 2 - num) <= 1e-6:\n",
" return x\n",
" elif x ** 2 - num < 0:\n",
" low = x\n",
" else:\n",
" high = x\n",
"\n",
"\n",
"num = float(input())\n",
"if num >= 1:\n",
" print(sqrt_binary(num))\n",
" print(math.sqrt(num))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 二分法求函数的零点"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"现有方程:\n",
"$ f(x) = x^5-15x^4+85x^3-225x^2+274x-121p $\n",
"已知f(x)在[1.5,2.4]区间有且只有一个根,用二分法求解该根。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n = int(input()) # 输入一个整数n,表示小数点后的位数\n",
"low, high = 1.5, 2.4 # 初始化二分查找的上下界\n",
"while True: # 开始循环\n",
" x = (low + high) / 2 # 计算上下界的中点\n",
" # 计算多项式的值\n",
" y = x ** 5 - 15 * x ** 4 + 85 * x ** 3 - 225 * x ** 2 + 274 * x - 121\n",
" if abs(y) < 1 * 10 ** -n: # 如果多项式的值的绝对值小于10的-n次方\n",
" print('{:.6f}'.format(x)) # 打印中点的值,保留6位小数\n",
" break # 结束循环\n",
" elif y < 0: # 如果多项式的值小于0\n",
" high = x # 将中点设为新的上界\n",
" else: # 如果多项式的值大于0\n",
" low = x # 将中点设为新的下界\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def f(x):\n",
" return x ** 5 - 15 * x ** 4 + 85 * x ** 3 - 225 * x ** 2 + 274 * x - 121\n",
"\n",
"\n",
"def bisection_method(low, high):\n",
" while True:\n",
" mid = (low + high) / 2\n",
" if abs(f(mid)) < 1 * 10 ** -n:\n",
" return '{:.6f}'.format(mid)\n",
" elif f(mid) < 0:\n",
" high = mid\n",
" else:\n",
" low = mid\n",
"\n",
"\n",
"if __name__ == '__main__':\n",
" n = int(input())\n",
" Low, High = 1.5, 2.4\n",
" print(bisection_method(Low, High))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 二分法猜数游戏"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"写一个1-1024之间的整数,猜测这个数字,每欠猜测后给出“大了”“小了”“猜中”的提示"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"x = random.randint(1, 1024) # 随机产生一个0-1024之间的整数\n",
"for i in range(10): # 允许猜10次\n",
" guess = int(input()) # 输入猜的整数\n",
" if guess == x:\n",
" print('猜中了') # 猜中则终止循环\n",
" break\n",
" elif guess > x:\n",
" print('猜的数大了')\n",
" else:\n",
" print('猜的数小了')\n",
"else:\n",
" print('你用完了十次机会也没猜中哦')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n"
]
}
],
"source": [
"True+1\n",
"print(bool(\"\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}