第 3 章 一些简单的数值程序
我们已经学习了一些Python语言基础结构,现在使用这些结构编写一些简单的程序。通过这种方式,我们再顺便介绍几种语言结构和算法技术。
3.1 穷举法
当一个整数存在整数立方根时,图3-1中的代码会对其进行输出。如果输入不是一个完全立方数,则输出一个消息进行说明。
#寻找完全立方数的立方根
x = int(input('Enter an integer: '))
ans = 0
while ans**3 < abs(x):
ans = ans + 1
if ans**3 != abs(x):
print(x, 'is not a perfect cube')
else:
if x < 0:
ans = -ans
print('Cube root of', x,'is', ans)
图3-1 使用穷举法求立方根
那么,对于何种x值,程序能正常结束呢?答案是“所有整数”。证明方法非常简单。
表达式ans**3的值从0开始,并随着每次循环逐渐变大;
当这个值达到或超过abs(x)时,循环结束;
因为abs(x)的值总为正,所以循环结束前进行的迭代次数必然是有限的。
编写循环时,应该使用一个合适的递减函数。这个函数具有如下属性:
它可以将一组程序变量映射为一个整数;
进入循环时,它的值是非负的;
当它的值≤0时,循环结束;
每次循环它的值都会减小。
图3-1的while循环中,递减函数是什么呢?是abs(x) - ans**3。
下面,我们制造一些错误,看看会发生什么。首先,将语句ans = 0注释掉。Python解释器会输出一条错误消息:
NameError: name 'ans' is not defined
因为解释器将ans绑定到任何对象之前,都要先找到与ans绑定的值。下面,我们还原ans的初始化语句,将语句ans = ans + 1替换为ans = ans,再试着求8的立方根。如果你厌倦了漫长的等待,可以按ctrl+c(同时按住ctrl键和c键),这样就可以回到shell的用户提示符。
下面,在循环开始部分添加一条语句:
print('Value of the decrementing function abs(x) - ans**3 is',
abs(x) - ans**3)
然后重新运行程序。这次程序会一次又一次地输出:
Value of the decrementing function abs(x) - ans**3 is 8
程序会永远运行下去,因为循环体没有减少ans**3和abs(x)之间的差距。遇到这种程序不会正常结束的情况时,经验丰富的程序员经常会插入一些print语句,就像这次一样,测试递减函数是否真的递减。
这个程序使用的算法技术称为穷举法,是猜测与检验算法的一个变种。我们枚举所有可能性,直至得到正确答案或者尝试完所有值。乍看上去,这是一种极其愚蠢的解决方法。但令人惊奇的是,穷举法经常是解决问题的最实用的方法。它实现起来特别容易,并且易于理解。还有,在很多情况下,它的运行速度也足够快。请一定记得将你添加的print语句删除或者注释掉,并插入语句ans = ans + 1,然后试着求出1 957 816 251的立方根。程序几乎瞬间结束。然后,再试试7 406 961 012 236 344 616。
眼见为实,即使需要几百万次猜测,也不是什么问题。现代计算机的速度真是太快了,它执行一条指令只需1纳秒——10亿分之1秒。要想描述它有多快还真有些困难,光传输1英尺(约0.3米)只需要1纳秒多一点。另外一种形容方式是,在你的声音传输100英尺的时间内,现代计算机可以执行几百万条指令。
只是为了好玩,试着运行以下代码:
maxVal = int(input('Enter a postive integer: '))
i = 0
while i < maxVal:
i = i + 1
print(i)
看看你需要输入一个多大的整数,才能感受到在输出结果之前有个明显的时间间隔。
实际练习:编写一个程序,要求用户输入一个整数,然后输出两个整数root和pwr,满足0 < pwr < 6,并且root**pwr等于用户输入的整数。如果不存在这样一对整数,则输出一条消息进行说明。
3.2 for循环
迄今为止,我们使用的while循环是高度程式化的,即都按照一个整数序列进行迭代。Python提供了一种语言机制简化使用这种迭代方式的程序,这就是for循环。
for语句的一般形式如下(回忆一下,斜体文本是对程序中该处代码的一种描述,并不是实际的代码):
for variable in sequence:
code block
for后面的变量被绑定到序列中的第一个值,并执行下面的代码块。然后变量被赋给序列中的第二个值,再次执行代码块。该过程一直继续,直到穷尽这个序列或者执行到代码块中的break语句。
绑定到变量的序列值通常使用内置函数range生成,它会返回一系列整数。range函数接受3个整数参数:start、stop和step。生成一个数列:start、start + step、start