福利)"/>
一小时学会Python数字计算(文末福利)
本文摘自人民邮电出版社《Python程序设计(第3版)》书
学习目标
-
理解数据类型的概念。
-
熟悉Python中的基本数值数据类型。
-
理解数字在计算机上如何表示的基本原理。
-
能够使用Python的math库。
-
理解累积器程序模式。
-
能够阅读和编写处理数值数据的程序。
1.1 数值数据类型
计算机刚开发出来时,它们主要被视为数字处理器,现在这仍然是一个重要的应用。如你所见,涉及数学公式的问题很容易转化为Python程序。在本文中,我们将仔细观察一些程序,它们的目的是执行数值计算。
计算机程序存储和操作的信息通常称为“数据”。不同种类的数据以不同的方式存储和操作。请考虑这个计算零钱的程序:
# change.py
# A program to calculate the value of some change in dollars
def main():
print("Change Counter")
print()
print("Please enter the count of each coin type.")
quarters = eval(input("Quarters: "))
dimes = eval(input("Dimes: "))
nickels = eval(input("Nickels: "))
pennies = eval(input("Pennies: "))
total = quarters * .25 + dimes * .10 + nickels * .05 + pennies * .01
print()
print("The total value of your change is", total)
main()
下面是输出示例:
Change Counter Please enter the count of each coin type.
Quarters: 5
Dimes: 3
Nickels: 4
Pennies: 6
The total value of your change is 1.81
这个程序实际上操作两种不同的数字。用户输入的值(5,3,4,6)是整数,它们没有任何小数部分。硬币的值(.25,.10,.05,.01)是分数的十进制表示。在计算机内部,整数和具有小数部分的数字以不同的方式存储。从技术上讲,这是两种不同的“数据类型”。
对象的数据类型决定了它可以具有的值以及可以对它执行的操作。整数用“integer”数据类型(简写为“int”)表示。int类型的值可以是正数或负数。可以具有小数部分的数字表示为“floating-point(浮点)”(或“float”)值。那么我们如何判断一个数值是int还是float呢?不包含小数点的数值字面量生成一个int值,但是具有小数点的字面量由float表示(即使小数部分为0)。
Python提供了一个特殊函数,名为type,它告诉我们任何值的数据类型(或“class”)。下面是与Python解释器的交互,显示int和float字面量之间的区别:
>>> type(3)
<class 'int'>
>>> type(3.14)
<class 'float'>
>>> type(3.0)
<class 'float'>
>>> myInt = -32
>>> type(myInt)
<class 'int'>
>>> myFloat = 32.0
>>> type(myFloat)
<class 'float'>
你可能希望知道,为什么有两种不同的数据类型。一个原因涉及程序风格。表示计数的值不能为小数,例如,我们不能有3.12个季度。使用int值告诉读者程序的值不能是一个分数。另一个原因涉及各种操作的效率。对于int,执行计算机运算的基础算法更简单,因此可以更快,而float值所需的算法更通用。当然,在现代处理器上,浮点运算的硬件实现是高度优化的,可能与int运算一样快。
int和float之间的另一个区别是,float类型只能表示对实数的近似。我们会看到,存储值的精度(或准确度)存在限制。由于浮点值不精确,而int总是精确的,所以一般的经验法则应该是:如果不需要小数值,就用int。
值的数据类型决定了可以使用的操作。如你所见,Python支持对数值的一般数学运算。表1.1总结了这些操作。实际上,这个表有些误导。由于这两种类型具有不同的底层表示,所以它们各自具有不同的一组操作。例如,我只列出了一个加法操作,但请记住,对float值执行加法时,计算机硬件执行浮点加法,而对int值,计算机执行整数加法。Python基于操作数选择合适的底层操作(int或float)。
表1.1 Python内置的数值操作
操作符 | 操作 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 浮点除 |
** | 指数 |
abs() | 绝对值 |
// | 整数除 |
% | 取余 |
请考虑以下Python交互:
>>> 3 + 4
7
>>> 3.0 + 4.0
7.0
>>> 3 * 4
12
>>> 3.0 * 4.0
12.0
>>> 4 ** 3
64
>>> 4.0 ** 3
64.0
>>> 4.0 ** 3.0
64.0
>>> abs(5)
5
>>> abs(-3.5)
3.5
>>>
在大多数情况下,对float的操作产生float,对int的操作产生int。大多数时候,我们甚至不必担心正在执行什么类型的操作。例如,整数加法与浮点加法产生的结果几乎相同,我们可以相信Python会做正确的事情。
然而,在除法时,事情就比较有趣了。如表所列,Python(版本3.0)提供了两种不同的运算符。通常的符号(/)用于“常规”除法,双斜线(//)用于表示整数除法。找到它们之间差异的最佳方法就是试一下。
>>> 10 / 3
3.3333333333333335
>>> 10.0 / 3.0
3.3333333333333335
>>> 10 / 5
2.0
>>> 10 // 3
3
>>> 10.0 // 3.0
3.0
>>> 10 % 3
1
>>> 10.0 % 3.0
1.0
请注意,“/”操作符总是返回一个浮点数。常规除法通常产生分数结果,即使操作数可能是int。Python通过返回一个浮点数来满足这个要求。10/3的结果最后有一个5,你是否感到惊讶?请记住,浮点值总是近似值。该值与Python将表示为浮点数时得到的近似值相同。
要获得返回整数结果的除法,可以使用整数除法运算“//”。整数除法总是产生一个整数。 把整数除法看作gozinta(进入或整除)。表达式10 // 3得到3,因为3 进入 10共计3次(余数为1)。虽然整数除法的结果总是一个整数,但结果的数据类型取决于操作数的数据类型。浮点整数整除浮点数得到一个浮点数,它的分数分量为0。最后两个交互展示了余数运算%。请再次注意,结果的数据类型取决于操作数的类型。
由于数学背景不同,你可能没用过整数除法或余数运算。要记住的是,这两个操作是密切相关的。整数除法告诉你一个数字进入另一个数字的次数,剩余部分告诉你剩下多少。数学上你可以写为a = (a//b)(b) + (a%b)。
作为示例应用程序,假设我们以美分来计算零钱(而不是美元)。如果我有383美分,那么我可以通过计算383 // 100 = 3找到完整美元的数量,剩余的零钱是383%100 = 83。因此,我肯定共有3美元和83美分的零钱。
顺便说一句,虽然Python(版本3.0)将常规除法和整数除法作为两个独立的运算符,但是许多其他计算机语言(和早期的Python版本)只是使用“/”来表示这两种情况。当操作数是整数时,“/”表示整数除法,当它们是浮点数时,它表示常规除法。这是一个常见的错误来源。例如,在我们的温度转换程序中,公式9/5 * celsius + 32不会计算正确的结果,因为9/5将使用整数除法计算为1。在这些语言中,你需要小心地将此表达式编写为9.0 / 5.0 * celsius + 32,以便使用正确的除法形式,从而得到分数结果。
1.2 类型转换和舍入
在某些情况下,值可能需要从一种数据类型转换为另一种数据类型。你已知道,int和int组合(通常)产生一个int,float和float组合创建另一个float。但是如果我们写一个混合int和float的表达式会发生什么呢?例如,在下列赋值语句之后,x的值应该是什么:
x = 5.0 * 2
如果这是浮点乘法,则结果应为浮点值10.0。如果执行整型乘法,结果就是10。在继续读下去获得答案之前,请花一点时间考虑:你认为Python应该怎样处理这种情况。
为了理解表达式5.0 * 2,Python必须将5.0转换为5并执行int操作,或将2转换为2.0并执行浮点操作。一般来说,将float转换为int是一个危险的步骤,因为一些信息(小数部分)会丢失。另一方面,int可以安全地转换为浮点,只需添加一个小数部分0。因此,在“混合类型表达式”中,Python会自动将int转换为浮点数,并执行浮点运算以产生浮点数结果。
有时我们可能希望自己执行类型转换。这称为显式类型转换。Python为这些场合提供了内置函数int和float。以下一些交互示例说明了它们的行为:
>>> int(4.5)
4
>>> int(3.9)
3
>>> float(4)
4.0
>>> float(4.5)
4.5
>>> float(int(3.3))
3.0
>>> int(float(3.3))
3
>>> int(float(3))
3
如你所见,转换为int就是丢弃浮点值的小数部分,该值将被截断,而不是舍入。如果你希望一个四舍五入的结果,假设值为正,可以在使用int()之前加上0.5。对数字进行四舍五入的更一般方法是使用内置的round函数,它将数字四舍五入到最接近的整数值。
>>> round(3.14)
3
>>> round(3.5)
4
请注意,像这样调用round会产生一个int值。因此,对round的简单调用是将float转换为int的另一种方法。
如果要将浮点值舍入为另一个浮点值,则可以通过提供第二个参数来指定在小数点后的数字位数。下面的交互处理π的值:
>>> pi = 3.141592653589793
>>> round(pi, 2)
3.14
>>> round(pi,3)
3.142
请注意,当我们将π近似舍入到两位或三位小数时,我们得到一个浮点数,其显示值看起来像一个完全舍入的结果。记住,浮点值是近似。真正得到的是一个非常接近我们要求的值。实际存储的值类似于3.140000000000000124345……,最接近的可表示的浮点值为3.14。幸运的是,Python是聪明的,知道我们可能不希望看到所有这些数字,所以它显示了舍入的形式。这意味着如果你编写一个程序,将一个值四舍五入到两位小数,并打印出来,就会看到两位小数,与你期望的一样。在第5章中,我们将看到如何更好地控制打印数字的显示方式,那时如果你希望,就能查看所有的数字。
类型转换函数int和float也可以用于将数字字符串转换为数字。
>>> int("32")
32
>>> float("32")
32.0
>>> float("9.8")
9.8
作为替代eval从用户获取数字数据的另一种方法,这特别有用。例如,下面是本章开始时零钱计数程序的一个改进版本:
# change2.py
# A program to calculate the value of some change in dollars
def main():
print("Change Counter")
print()
print("Please enter the count of each coin type.")
quarters = int(input("Quarters: "))
dimes = int(input("Dimes: "))
nickels = int(input("Nickels: "))
pennies = int(input("Pennies: "))
total = .25*quarters + .10*dimes + .05*nickels + .01*pennies
print()
print("The total value of your change is", total)
main()
在input语句中使用int而不是eval,可以确保用户只能输入有效的整数。任何非法(非int)输入将导致程序崩溃和错误消息,从而避免代码注入攻击的风险(在第2.5.2节讨论)。另一个好处是,这个版本的程序强调输入应该是整数。
使用数字类型转换代替eval的唯一缺点是,它不支持同时输入(在单个输入中获取多个值),如下例所示:
>>> # simultaneous input using eval
>>> x,y = eval(input("Enter (x,y): "))
Enter (x,y): 3,4
>>> x
3
>>> y
4
>>> # does not work with float
>>> x,y = float(input("Enter (x,y): "))
Enter (x,y): 3,4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '3,4'
这个代价很小,换来了额外的安全性。在第5章,你将学习如何克服这个限制。作为一种良好的实践,你应该尽可能使用适当的类型转换函数代替eval。
1.3 使用math库
除表1.1中列出的操作之外,Python还在一个特殊的math“库”中提供了许多其他有用的数学函数。库就是一个模块,包含了一些有用定义。我们的下一个程序展示了使用这个库来计算二次方程的根。
二次方程的形式为ax2 + bx + c = 0。这样的方程有两个解,由求根公式给出:
让我们编写一个程序,找到二次方程的解。程序的输入将是系数a、b和c的值,输出是由求根公式给出的两个值。下面是完成这项工作的程序:
# quadratic.py
# A program that computes the real roots of a quadratic equation.
# Illustrates use of the math library.
# Note: This program crashes if the equation has no real roots.
import math # Makes the math library available.
def main():
print("This program finds the real solutions to a quadratic")
print()
a = float(input("Enter coefficient a: "))
b = float(input("Enter coefficient b: "))
c = float(input("Enter coefficient c: "))
discRoot = math.sqrt(b * b - 4 * a * c)
root1 = (-b + discRoot) / (2 * a)
root2 = (-b - discRoot) / (2 * a)
print()
print("The solutions are:", root1, root2 )
main()
该程序使用了math库模块的平方根函数sqrt。在程序的顶部,import math告诉Python我们正在使用math模块。导入模块让程序中定义的任何内容都可用。要计算,我们使用math.sqrt(x)。这个特殊的点符号告诉Python,使用“生存”在math模块中的sqrt函数。在二次方程程序中,我们用下面的代码行来计算:
discRoot = math.sqrt(b * b - 4 * a * c)
下面是程序运行的情况:
This program finds the real solutions to a quadratic
Enter coefficient a: 3
Enter coefficient b: 4
Enter coefficient c: -2
The solutions are: 0.38742588672279316 -1.7207592200561266
只要我们要解的二次方程有实数解,这个程序就很好。但是,一些输入会导致程序崩溃。下面是另一个运行示例:
This program finds the real solutions to a quadratic
Enter coefficient a: 1
Enter coefficient b: 2
Enter coefficient c: 3
Traceback (most recent call last):
File "quadratic.py", line 21, in ?
main()
File "quadratic.py", line 14, in main
discRoot = math.sqrt(b * b - 4 * a * c)
ValueError: math domain error
这里的问题是b2 − 4ac < 0,sqrt函数无法计算负数的平方根。Python打印“math domain error”。这告诉我们,负数不在sqrt函数的定义域中。现在,我们没有工具来解决这个问题,所以我们只需要假设用户会给我们可解的方程。
实际上,quadratic.py不需要使用math库。我们可以用乘方**来取平方根。(你知道怎么做吗?)使用math.sqrt更高效一些,而且它让我展示使用math库。一般来说,如果你的程序需要一个通用的数学函数,首先要看看math库。表3.2显示了math库中提供的一些其他函数。
表1.2 一些math库函数
Python | 数学 | 描述 |
pi | π | π的近似值 |
e | e | e的近似值 |
sqrt(x) | x的平方根 | |
sin(x) | sin x | x的正弦 |
cos(x) | cos x | x的余弦 |
tan(x) | tan x | x的正切 |
asin(x) | arcsin x | x的反正弦 |
acos(x) | arcos x | x的反余弦 |
atan(x) | arctan x | x的反正切 |
log(x) | ln x | x的自然对数(以e为底) |
log10(x) | Log10 x | x的常用对数(以10为底) |
exp(x) | ex | e的x次方 |
ceil(x) | [x] | 最小的>=x的整数 |
floor(x) | [x] | 最大的<=x的整数 |
1.4 累积结果:阶乘
假设你有一个根汁饮料样品包,含有6种不同的根汁饮料。以不同的顺序喝各种口味可能会影响它们的味道。如果你希望尝试一切可能,有多少不同的顺序?结果答案是一个大得惊人的数字,720。你知道这个数字怎么来的吗?720是6的“阶乘”。
在数学中,阶乘通常用感叹号(!)表示,整数n的阶乘定义为n! = n(n – 1)(n – 2)……(1)。这恰好是n项的不同排列的数量。给定6个项,我们计算6! = (6)(5)(4)(3)(2)(1) = 720种可能的排列。
让我们编写一个程序,来计算用户输入数字的阶乘。程序的基本结构遵循“输入、处理、输出”模式:
输入要计算阶乘的数,n
计算n的阶乘,fact
输出fact
显然,这里棘手的是第二步。
实际如何计算阶乘?让我们手工尝试一下,以便得到处理的思路。在计算6的阶乘时,我们首先计算6(5) = 30。然后我们取该结果并做另一个乘法:30(4) = 120。这个结果乘以3:120(3) = 360。这个结果乘以2:360(2) = 720。根据定义,最后我们将这个结果乘以1,但不会改变最终值720。
现在让我们考虑更一般的算法。这里实际上发生了什么?我们正在做重复乘法,在做的过程中,我们记下得到的乘积。这是一种非常常见的算法模式,称为“累积器”。我们一步一步得到(或累积)最终的值。为了在程序中实现这一点,我们使用“累积器变量”和循环结构。一般模式如下:
初始化累积器变量
循环直到得到最终结果
更新累积器变量的值
意识到这是解决阶乘问题的模式,我们只需要填写细节。我们将累积阶乘。让我们把它保存在一个名为fact的变量中。每次通过循环,我们需要用fact乘以因子序列n、(n – 1)、……、1中的一个。看起来我们应该用一个for循环,迭代这个因子序列。例如,要计算6的阶乘,我们需要像这样工作的循环:
fact = 1
for factor in [6,5,4,3,2,1]:
fact = fact * factor
请花一分钟跟踪这个循环的执行,并说服自己它有效。当循环体首次执行时,fact的值为1,因子为6。因此,fact的新值为1 * 6 = 6。下一次通过循环,因子将为5,fact更新为6 * 5 = 30。该模式对后续每个因子继续,直到累积得到最终结果720。
循环之前对fact赋初始值1,是循环开始所必需的。每次通过循环体(包括第一个),fact的当前值用于计算下一个值。初始化确保fact在第一次迭代时有一个值。每次使用累积器模式时,应确保包含正确的初始化。忘记这一点是新程序员的一个常见错误。
当然,我们还有很多其他的方法来编写这个循环。正如你从数学课上了解到的,乘法是可交换和结合的,所以执行乘法的顺序并不重要。我们可以很容易地走另一个方向。你可能还会注意到,在因子列表中包含1是不必要的,因为乘以1不会更改结果。下面是另一个版本,计算出相同的结果:
fact = 1
for factor in [2,3,4,5,6]:
fact = fact * factor
不幸的是,这两个循环都不能解决原来的问题。我们手工编码了因子列表来计算6的阶乘。我们真正希望的是,一个可以计算任何给定输入n的阶乘的程序。我们需要某种方法,从n的值生成适当的因子序列。
好在用Python的range函数,这很容易做到。回想一下,range(n)产生一个数字序列,从0开始,增长到n,但不包括n。range有一些其他调用方式,可用于产生不同的序列。利用两个参数,range(start,n)产生一个以值start开始的序列,增长到n,但不包括n。第三个版本的range(start,n,step)类似于双参数版本,但它使用step作为数字之间的增量。下面有一些例子:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5,10))
[5, 6, 7, 8, 9]
>>> list(range(5, 10, 3))
[5, 8]
给定输入值n,我们有几种不同的range命令,能产生适当的因子列表,用于计算n的阶乘。为了从最小到最大生成它们(我们的第二种循环),我们可以使用range(2,n + 1)。注意,我使用n + 1作为第二个参数,因为范围将上升到n + 1,但不包括此值。我们需要加1来确保n本身被包括,作为最后一个因子。
另一种可能是使用三参数版本的range和负数步长,产生另一个方向(我们的第一种循环)的因子,导致倒计数:range(n,1,−1)。这个循环产生一个列表,从n开始并向下计数(step为−1)到1,但不包括1。
下面是一种可能的阶乘程序版本:
# factorial.py
# Program to compute the factorial of a number
# Illustrates for loop with an accumulator
def main():
n = int(input("Please enter a whole number: "))
fact = 1
for factor in range(n,1,-1):
fact = fact * factor
print("The factorial of", n, "is", fact)
main()
当然,写这个程序还有很多其他方法。我已经提到改变因子的顺序。另一种可能是将fact初始化为n,然后使用从n−1开始的因子(只要n> 0)。你可以尝试这样一些变化,看看你最喜欢哪一个。
1.5 计算机算术的局限性
有时我们会联想到,用“!”表示阶乘是因为该函数增长非常快。例如,下面是用我们的程序求100的阶乘:
Please enter a whole number: 100
The factorial of 100 is 9332621544394415268169923885626670049071596826
43816214685929638952175999932299156089414639761565182862536979208272237
58251185210916864000000000000000000000000
这是一个相当大的数字!
尽管最新版本的Python对此计算没有困难,但是旧版本的Python(以及其他语言的现代版本,例如C ++和Java)不会如此。例如,下面是使用Java编写的类似程序的几次运行:
# run 1
Please enter a whole number: 6
The factorial is: 720
# run 2
Please enter a whole number: 12
The factorial is: 479001600
# run 3
Please enter a whole number: 13
The factorial is: 1932053504
这看起来不错。我们知道6! = 720。快速检查也确认12! = 479001600。遗憾的是,事实证明,13! = 6227020800。看起来Java程序给出了不正确的答案!
这里发生了什么?到目前为止,我们已经讨论了数值数据类型作为熟悉数字的表示,例如整数和小数(分数)。然而,重要的是要记住,数字的计算机表示(实际数据类型)并不总是表现得像它们所代表的数字那样。
在第1章中你了解到,计算机的CPU可以执行非常基本的操作,如两个数字相加或相乘,还记得吗?更准确地说,CPU可以对计算机的数字的内部表示执行基本操作。这个Java程序的问题是它使用计算机的底层int数据类型来表示整数,并依赖于计算机对int的乘法运算。不幸的是,这些机器int不完全像数学整数。有无穷多个整数,但int的范围是有限的。在计算机内部,int以固定大小的二进制表示存储。为了理解这一切,我们需要了解硬件层面发生了什么。
计算机存储器由电“开关”组成,每个开关可以处于两种可能状态之一,即开或关。每个开关表示一个二进制数字的信息,称为“位”。一位可以编码两种可能性,通常用数字0(关闭)和1(打开)表示。位序列可以用于表示更多的可能性。用两位,我们可以表示四件事:
bit 2 bit 1
0 0
0 1
1 0
1 1
三位允许我们通过对四个两位模式中的每一个添加0或1,来表示八个不同的值:
bit 3 bit 2 bit 1
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
你可以看到这里的模式。每增加一位让不同模式的数量加倍。通常,n位可以表示2n个不同的值。
特定计算机用来表示int的位数取决于CPU的设计。现在,典型的PC使用32或64位。对于32位CPU,这意味着有232个可能的值。这些值以0为中心,表示正整数和负整数的范围。现在232/2 = 231。因此,可以在32位int值中表示的整数范围是−231到231 − 1。在上限−1的原因是考虑在范围的上半部分中的0的表示。
有了这个知识,让我们试着理解Java阶乘例子中发生的事情。如果Java程序依赖于32位int表示,它可以存储的最大数字是多少?Python可以给我们一个快速的答案:
>>> 2**31-1
2147483647
注意,这个值(约21亿)在12!(约4.8亿)和13!(约62亿)之间。这意味着Java程序可以很好地计算到12的阶乘,但是之后,表示“溢出”,结果是垃圾。现在你知道了为什么简单的Java程序不能计算13!。当然,这给我们留下了另一个谜题。为什么现代Python程序似乎能很好地用大整数计算?
首先,你可能认为Python使用浮点数据类型来绕过int的大小限制。然而,事实证明,浮点数并没有真正解决这个问题。下面是使用浮点数的修改后的阶乘程序的示例运行:
Please enter a whole number: 30
The factorial of 30 is 2.6525285981219103e+32
虽然这个程序运行很好,但切换到浮点数后,我们不再能得到确切的答案。
非常大(或非常小)的浮点值使用“指数”的方式打印,称为“科学记数法”。结束时的e + 32表示结果等于2.6525285981219103 * 1032。你可以把+32作为一个标记,表示小数点的位置。在这个例子中,它必须向右移动32个位置以获取实际值。但是,小数点右边只有16位数字,因此我们已经“丢失”了最后16位数字。
使用浮点数,我们可以表示比32位int更大的“范围”的值,但“精度”仍然是固定的。事实上,计算机将浮点数保存为一对固定长度(二进制)整数。一个整数称为“尾数”,表示值中的数字串,第二个称为“指数”,记录整数部分结束和小数部分开始的位置(“二进制小数点”在哪里)。回忆一下,我告诉过你浮点是近似值。现在你可以看到原因。因为底层数字是二进制的,所以只有涉及2的幂的分数可以被精确地表示。任何其他分数产生无限重复的尾数。(就像1/3产生无限重复的十进制,因为3不是10的幂)。当无限长的尾数被截断到固定长度以进行存储时,结果是近似的。用于尾数的位数决定了近似值的精确程度,但绕不过它们是近似的事实。
幸运的是,Python对于大的、精确的值有一个更好的解决方案。Python的int不是固定的大小,而是扩展到适应任何值。唯一的限制是计算机可用的内存量。当值很小时,Python就用计算机的底层int表示和操作。当值变大时,Python会自动转换为使用更多位的表示。当然,为了对更大的数字执行操作,Python必须将操作分解为计算机硬件能够处理的更小的单元,类似于你手工计算长除法的方式。这些操作不会那么有效(它们需要更多的步骤),但是它们允许Python的int增长到任意大小。这就是为什么我们的简单阶乘程序可以计算一些大的结果。这是一个非常酷的Python特性。
1.6 小结
本文介绍了一些有关进行数值计算的程序的重要细节。下面是一些关键概念的快速摘要。
-
计算机表示特定类型的信息的方式称为数据类型。对象的数据类型决定了它可以具有的值和它支持的操作。
-
Python有几种不同的数据类型来表示数值,包括int和float。
-
整数通常使用int数据类型表示,小数值使用float表示。所有Python数字数据类型都支持标准的内置数学运算:加法(+)、减法(−)、乘法(*)、除法(/),整除(//),取余(%)和绝对值(abs(x))。
-
Python在某些情况下,自动将数字从一种数据类型转换为另一种。例如,在涉及int和float的混合类型表达式中,Python先将int转换为float,然后使用浮点运算。
-
程序还可以使用函数float()、int()和round()将一个数据类型显式转换为另一个数据类型。通常应该使用类型转换函数代替eval来处理用户的数字输入。
-
其他数学函数在math库中定义。要使用这些功能,程序必须首先导入该库。
-
数值结果通常通过计算值序列的和或积来计算。循环累积器编程模式对于这种计算很有用。
-
int和float在底层计算机上都使用固定长度的位序列表示。这让这些表示有某些限制。在32位的机器上,硬件int必须在−231~231−1中。浮点数的精度有限,不能精确地表示大多数数字。
-
Python的int数据类型可以用于存储任意大小的整数。如果int值对于底层硬件int太大,就会自动转换为更长的表示。涉及这些长int的计算比只使用短int的计算效率低。
1.7 练习
复习问题
判断对错
1.由计算机存储和操作的信息称为数据。
2.由于浮点数是非常准确的,所以通常应该使用它们,而不是int
。
3.像加法和减法这样的操作在math
库中定义。
4.n项的可能排列的数目等于n!。
5.sqrt
函数计算数字的喷射(squirt)。
6.float
数据类型与实数的数学概念相同。
7.计算机使用二进制表示数字。
8.硬件float
可以表示比硬件int
更大范围的值。
9.在获取数字作为用户输入时,类型转换函数(如float
)是eval
的安全替代。
10.在Python中,4 + 5产生与4.0 + 5.0相同的结果类型。
多项选择
1.下列 项不是内置的Python数据类型。
a.int
b.float
c.rational
d.string
2.以下 项不是内置操作。
a.+
b.%
c.abs()
d.sqrt()
3.为了使用math库中的函数,程序必须包括 。
a.注释
b.循环
c.操作符
d.import语句
4.4!的值是 。
a.9
b.24
c.41
d.120
5.用于存储π的值,最合适的数据类型是 。
a.int
b.float
c.irrational
d.string
6.可以使用5位比特表示的不同值的数量是 。
a.5
b.10
c.32
d.50
7.在包含int和float的混合类型表达式中,Python会进行的转换是 。
a.浮点数到整数
b.整数到字符串
c.浮点数和整数到字符串
d.整数到浮点数
8.下列 项不是Python类型转换函数。
a.float
b.round
c.int
d.abs
9.用于计算阶乘的模式是 。
a.累积器
b.输入、处理、输出
c.计数循环
d.格子
10.在现代Python中,int值大于底层硬件int时,会 。
a.导致溢出
b.转换为float
c.打破计算机
d.使用更多的内存
讨论
1.显示每个表达式求值的结果。确保该值以正确的形式表示其类型(int或float)。如果表达式是非法的,请解释为什么。
a.4.0 / 10.0 + 3.5 * 2
b.10 % 4 + 6 / 2
b.abs(4 - 20 // 3) ** 3
d.sqrt(4.5 - 5.0) + 7 * 3
e.3 * 10 // 3 + 10 % 3
f.3 ** 3
2.将以下每个数学表达式转换为等效的Python表达式。你可以假定math库已导入(通过import math)。
a.(3 + 4)(5)
b.
c.4πr2
d.
e.
3.显示将由以下每个range表达式生成的数字序列。
a.range(5)
b.range(3, 10)
c.range(4, 13, 3)
d.range(15, 5, -2)
e.range(5, 3)
4.显示以下每个程序片段产生的输出。
a.
for i in range(1, 11):
print(i*i)
b.
for i in [1,3,5,7,9]:
print(i, ":", i**3)
print(i)
c.
x = 2
y = 10
for j in range(0, y, x):
print(j, end="")
print(x + y)
print("done")
d.
ans = 0
for i in range(1, 11):
ans = ans + i*i
print(i)
print (ans)
5.如果使用负数作为round函数中的第二个参数,你认为会发生什么?例如,round(314.159265,−1)的结果应该是什么?请解释答案的理由。在你写下答案后,请参阅Python文档或尝试一些例子,看看Python在这种情况下实际上做了什么。
6.当整数除法或余数运算的操作数为负数时,你认为会发生什么?考虑以下每种情况并尝试预测结果。然后在Python中试试。(提示:回顾一下神奇的公式a = (a//b)(b) + (a%b)。)
a.−10 // 3
b.−10 % 3
c.10 // −3
d.10 % −3
e.−10 // −3
编程练习
1.编写一个程序,利用球体的半径作为输入,计算体积和表面积。以下是一些可能有用的公式:
V = 4/3πr3
A = 4πr2
2.给定圆形比萨饼的直径和价格,编写一个程序,计算每平方英寸的成本。面积公式为A = πr2。
3.编写一个程序,该程序基于分子中的氢、碳和氧原子的数量计算碳水化合物的分子量(以克/摩尔计)。程序应提示用户输入氢原子的数量、碳原子的数量和氧原子的数量。然后程序基于这些单独的原子量打印所有原子的总组合分子量。
原子 | 质量(克/摩尔) |
H | 1.00794 |
C | 12.0107 |
O | 15.9994 |
例如,水(H2O)的分子量为2(1.00794)+ 15.9994 = 18.01528。
4.编写一个程序,根据闪光和雷声之间的时间差来确定雷击的距离。声速约为1100英尺/秒,1英里为5280英尺。
5.Konditorei咖啡店售卖咖啡,每磅10.50美元加上运费。每份订单的运费为每磅0.86美元 +固定成本1.50美元。编写计算订单费用的程序。
6.使用坐标(x1,y1)和(x2,y2)指定平面中的两个点。编写一个程序,计算通过用户输入的两个(非垂直)点的直线的斜率。
{-:-}斜率 =
7.编写一个程序,接受两点(见上一个问题),并确定它们之间的距离。
{-:-}距离 =
8.格里高利闰余是从1月1日到前一个新月的天数。此值用于确定复活节的日期。它由下列公式计算(使用整型算术):
C = year//100
epact = (8 + (C//4) - C + ((8C + 13)//25) + 11(year%19))%30
编写程序,提示用户输入4位数年份,然后输出闰余的值。
9.使用以下公式编写程序以计算三角形的面积,其三边的长度为a、b和c:
{-:-}
10.编写程序,确定梯子斜靠在房子上时,达到给定高度所需的长度。梯子的高度和角度作为输入。计算长度使用公式为:
注意:角度必须以弧度表示。提示输入以度为单位的角度,并使用以下公式进行转换:
11.编程计算前n个自然数的和,其中n的值由用户提供。
12.编程计算前n个自然数的立方和,其中n的值由用户提供。
13.编程对用户输入的一系列数字求和。 程序应该首先提示用户有多少数字要求和,然后依次提示用户输入每个数字,并在输入所有数字后打印出总和。(提示:在循环体中使用输入语句。)
14.编程计算用户输入的一系列数字的平均值。与前面的问题一样,程序会首先询问用户有多少个数字。注意:平均值应该始终为float,即使用户输入都是int。
15.编写程序,通过对这个级数的项进行求和来求近似的π值:4/1 – 4/3 + 4/5 – 4/7 + 4/9 − 4/11 +……程序应该提示用户输入n,要求和的项数,然后输出该级数的前n个项的和。让你的程序从math.pi的值中减去近似值,看看它的准确性。
16.斐波那契序列是数字序列,其中每个连续数字是前两个数字的和。经典的斐波那契序列开始于1,1,2,3,5,8,13,……。编写计算第n个斐波纳契数的程序,其中n是用户输入的值。例如,如果n = 6,则结果为8。
17.你已经看到math库包含了一个计算数字平方根的函数。在本练习中,你将编写自己的算法来计算平方根。解决这个问题的一种方法是使用猜测和检查。你首先猜测平方根可能是什么,然后看看你的猜测是多么接近。你可以使用此信息进行另一个猜测,并继续猜测,直到找到平方根(或其近似)。一个特别好的猜测方法是使用牛顿法。假设x是我们希望的根,guess是当前猜测的答案。猜测可以通过使用计算下一个猜测来改进:
编程实现牛顿方法。程序应提示用户找到值的平方根(x)和改进猜测的次数。从猜测值x / 2开始,你的程序应该循环指定的次数,应用牛顿的方法,并报告猜测的最终值。你还应该从math.sqrt(x)的值中减去你的估计值,以显示它的接近程度。
本文摘自《Python程序设计(第3版)》
Python之父作序推荐
Python编程入门经典
编辑推荐:
● 广泛使用计算机图形学——本书提供一个简单的图形软件包graphics.py作为示例。
● 生动有趣的例子——本书包含了完整的编程示例来解决实际问题。
● 亲切自然的行文——以自然的叙事风格介绍了重要的计算机科学概念。
● 灵活的螺旋式学习过程——简单地呈现概念,逐渐介绍新的思想,章节末加以巩固强化。
● 时机恰好地介绍对象——本书既不是严格的“早讲对象”,也不是“晚讲对象”,而是在命令式编程 的基础上简要地介绍了对象概念。
● 提供丰富的教学素材——提供了大量的章末习题。还提供代码示例和教学PPT下载。
本书以Python语言为工具教授计算机程序设计。本书强调解决问题、设计和编程是计算机科学的核心技能。本书特色鲜明、示例生动有趣、内容易读易学,适合Python入门程序员阅读,也适合高校计算机专业的教师和学生参考。
作者简介
John Zelle是美国Wartburg大学数学和计算机系教授。他负责教授Python程序设计课程,并且结合多年的教学经验编写了本书,在美国高校受到普遍的欢迎。他还从事VR、AI等方面的研究,发表了一些机器学习方面的论文。
上期获奖名单小福利
关注【异步社区】服务号,转发本文至朋友圈或 50 人以上微信群,截图发送至异步社区服务号后台,并在文章底下留言,分享你的Python开发经验或者本书的试读体验,我们将选出3名读者赠送《Python程序设计(第3版)》1本,赶快积极参与吧!
活动截止时间:2018 年2月4日
箫山茔和 {-_-}
请获奖读者填写下方获奖信息,活动名称《异步社区 Linux二进制分析》/
在“异步社区”后台回复“关注”,即可免费获得2000门在线视频课程;推荐朋友关注根据提示获取赠书链接,免费得异步图书一本。赶紧来参加哦!
扫一扫上方二维码,回复“关注”参与活动!
阅读原文,购买《Python程序设计(第3版)》
更多推荐
一小时学会Python数字计算(文末福利)
发布评论