基础运算

四则运算和指数运算

加法、减法和四舍五入

>>> 3 - 9
-6
>>> 6 + 1
7

整数加法和减法,等同于小学算术的内容。

但是,要注意,以下关于浮点数的计算。

>>> 3.14 + 2.1
5.24
>>> 2.3 - 4.1
-1.7999999999999998
>>> 4.1 - 2.3
1.7999999999999998

2.3 - 4.1的计算中,所得的结果并不是-1.8——显然应该是这个。这是因为,在利用Python进行计算的时候,要将上面示例中所使用的十进制数字,转换为二进制的数字。在这个转换中,并不是所有的浮点数都对应有限个数的二进制数字,如果没有有限个数的二进制数字,那么计算的时候就产生了误差。这并不是Python的Bug。

要解决这个问题,可以使用四舍五入的方法。

>>> a = 2.3 - 4.1
>>> round(a,1)
-1.8

四舍五入,已经能够解决绝大部分问题了。

这里使用了Python的内置函数round(),用它实现四舍五入计算。可以用help(round)的方式查看它的使用方法。

round(number, ndigits=None)
    Round a number to a given precision in decimal digits.

    The return value is an integer if ndigits is omitted or None.  Otherwise
    the return value has the same type as the number.  ndigits may be negative.

参数ndigits表示要保留的有理数位数。

如果参数number是浮点数,当ndigits是正数的时候,就表示所保留的小数位数。例如上面的示例。

看上面文档,ndigits还可以是负数,其含义为有理数的整数部分所不保留的位数。例如:

>>> round(163, -2)
200
>>> round(126.78, -1)
130.0

乘法和乘方

乘法使用一个 *实现。

>>> 2 * 3
6
>>> 2.3 * 4.5
10.35

乘方运算,可以使用 **实现:

>>> 2 ** 3
8
>>> 2.3 ** 2
5.289999999999999

根据数学中的知识,指数还可以不是整数,例如 ,其含义就是

>>> 4 ** (1/2)
2.0

但注意,此时返回的是浮点数。

当然,指数变成小数,与分数是同样的含义。

>>> 4 ** 0.5
2.0

指数也可以是负数,如

>>> 2 ** -2
0.25

除了使用 **实现乘方运算,还可以用内置函数实现。

>>> pow(2, 3)
8
>>> pow(4, -2)
0.0625
>>> pow(4, 1/2)
2.0

pow()的第一个参数是底,第二个是幂。用上面的方法,与使用 **完全一致,即pow(n, m)等于n ** m

如果查看pow()的帮助文档:help(pow)

pow(base, exp, mod=None)
    Equivalent to base**exp with 2 arguments or base**exp % mod with 3 arguments

pow()函数有三个参数,如果要给mod参数赋值,则表示base ** exp % mod,其含义是baseexp次方,然后除以mod,返回其余数。例如:

>>> pow(2, 3, 5)
3

,除以 的余数是 。关于余数和符号%,见后面的除法计算。

科学计数法

在数学中,对于很大或者很小的数,习惯使用科学计数法,例如 可以记作 ,用Python实现科学记数法:

>>> 2e2
200.0
>>> 2e-2
0.02
>>> e3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'e3' is not defined
>>> 1e3
1000.0

必须要在e前面有数字,可以是整数,也可以是浮点数。

>>> 23e2
2300.0
>>> 23e-2
0.23
>>> 12.34e2
1234.0

除法

Python3.x与Python2.x对除法的定义有所不同,这里所演示的是Python3.x中的除法。

>>> 3 / 2
1.5
>>> 4 / 2
2.0
>>> 1 / 5
0.2

如果用//,结果返回商,俗称“地板除”。

>>> 5 // 2
2
>>> 3 // 4
0
>>> 3.4 // 2
1.0
>>> 6.4 // 3
2.0

“地板除”能够得到商,用%则得到余数。

>>> 5 % 2
1

在内置函数中,有divmod(),可以同时返回商和余数。

>>> divmod(5, 2)
(2, 1)

分数

Python中表示分数,实用标准库中的一个模块fractions

>>> from fractions import Fraction
>>> a = Fraction(3, 4)
>>> b = Fraction(2, 6)
>>> a
Fraction(3, 4)
>>> b
Fraction(1, 3)

其中,a是分数 b在定义的时候,是分数 ,Python会自动将这个分数最简化,即

分数之间可以进行运算。

>>> a + b
Fraction(13, 12)
>>> a * b
Fraction(1, 4)
>>> a - b
Fraction(5, 12)
>>> a / b
Fraction(9, 4)
>>> a ** b
0.9085602964160698
>>> pow(a, b)
0.9085602964160698

分数也可以跟整数、浮点数进行运算。

>>> a + 2
Fraction(11, 4)
>>> a * 2.1
1.5750000000000002
>>> b / 4
Fraction(1, 12)
>>> b / 2.5
0.13333333333333333
>>> a ** 2
Fraction(9, 16)
>>> pow(a, 2)
Fraction(9, 16)

复数

前面讨论中,所涉及到的都是实数。Python中也支持定义复数(complex number)。

# 定义1,注意表示虚数必须用j,不能用数学中习惯的i
>>> a = 2 + 3j
>>> type(a)
<class 'complex'>
# 定义2
>>> b = complex(4, 5)
>>> b
(4+5j)
# 只定义虚部
>>> 2j
2j
>>> type(2j)
<class 'complex'>

同时,支持复数的有关基本运算。

>>> a + b
(6+8j)
>>> a - b
(-2-2j)
>>> a * b
(-7+22j)
>>> a / b
(0.5609756097560976+0.0487804878048781j)
>>> a ** b
(-0.7530458367485596-0.9864287886477445j)

通过复数的如下两个方法,分别得到其实部和虚部。

# 实部
>>> a.real
2.0
# 虚部
>>> a.imag
3.0

复数的共轭。数学中定义 的共轭复数是

>>> a
(2+3j)
>>> a.conjugate()
(2-3j)

在数学中,因为 ,如果用Python表示:

# 这样做不行,因为这样做会认为 j 是变量
>>> j ** 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined
# 必须这样做
>>> 1j ** 2
(-1+0j)
>>> 2j ** 2
(-4+0j)

对于复数 ,称为“绝对值”或者“模”。

利用前面已经学过的运算,可以计算:

>>> (a.real ** 2 + a.imag ** 2) ** 0.5
3.605551275463989

最简捷的方法,是使用内置函数abs()直接计算

>>> abs(a)
3.605551275463989

函数abs()中的参数也可以是实数,即得到了该实数的绝对值。

>>> abs(-2)
2

初等函数

Python标准库有一个模块math,其中包括了常用的初等函数。

>>> import math
>>> dir(math)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

例如, 可以是:

>>> math.pi
3.141592653589793

计算 ,数学上应该等于 ,用Python计算结果:

>>> math.sin(2*math.pi)
-2.4492935982947064e-16

>>> math.sin(math.pi/2)
1.0

有了math模块,常见函数计算就容易了。

综合实践

斐波那契数列简介

神奇的斐波那契数列。公元1150年印度数学家首先描述了斐波那契数列,此后,意大利人斐波那契(Leonardo Fibonacci)在描述兔子繁殖的数量,这样描述:

  • 第一个月初有一对刚诞生的兔子
  • 第二个月之后(第三个月初)它们可以生育
  • 每月每对可生育的兔子会繁殖一对新兔子
  • 兔子永不死

假设在 月有兔子共 对, 月共有 对,在 月必定共有 对:因为在 月的时候,前一月(n+1月)的 对兔子可以存留至第 月(在当月属于新诞生的兔子尚不能生育)。而新生育出的兔子对数等于所有在 月就已存在的 对。

即设

按照表达式,可知斐波那契数是:

编程序,计算斐波那契数。可以有多种方法。

计算斐波那契数

递归

这里编写了一个用递归方法计算斐波那契数的函数,关于函数的创建方法,请参阅参考文献[1]。

# 递归
def fib_recu(n):
    if n <= 1:
        return n
    else:
        return fib_recu(n-1) + fib_recu(n-2)

print("fibonacci number:")
for i in range(10):
    print(f"{fib_recu(i)}", end=" ")

# 输出:
fibonacci number:
0 1 1 2 3 5 8 13 21 34

递归方法,思路简单,但是执行效率比较低。

循环

def fib_loop(n):
    fibs = [0, 1]
    if n <= 1:
        return n
    else:
        for i in range(n-2):
            fibs.append(fibs[-2] + fibs[-1])
    return fibs

print(f"fibonacci number: {fib_loop(10)}")

# 输出:
fibonacci number: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

循环方法,也称为递推法,即递增。随着数量的增大,执行速度会越来越慢。

迭代器对象

class Fibs:
    def __init__(self, max):
        self.max = max
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return fib

fibs = Fibs(100000)
lst = [ fibs.__next__() for i in range(10)]
print(lst)

# 输出
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

生成器对象

def fibs():
    prev, curr = 0, 1
    while True:
        yield prev
        prev, curr = curr, prev + curr


import itertools
print(list(itertools.islice(fibs(), 10)))

# 输出
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

通过fibs()得到了含有无限项斐波那契数的对象,只不过在上面只向内存中读入了10个。

矩阵

import numpy as np
def fib_matrix(n):
    F = np.mat([[1,1], [1,0]])
    F_m = pow(F, n)
    return F_m[0, 0]

print("fibonacci numbers:")
for i in range(10):
    print(fib_matrix(i), end=" ")

# 输出
fibonacci numbers:
1 1 2 3 5 8 13 21 34 55

黄金分割

如果用斐波那契数列中的后面的数字除以前面的数字,结果会如何?

def fib_iter():
    prev, curr = 0, 1
    while True:
        yield prev
        prev, curr = curr, prev + curr

import itertools
fibs = list(itertools.islice(fib_iter(), 100))
for i in range(2, 30):
    print(f"{fibs[i+1]}/{fibs[i]}={fibs[i+1]/fibs[i]}")

# 输出
2/1=2.0
3/2=1.5
5/3=1.6666666666666667
8/5=1.6
13/8=1.625
21/13=1.6153846153846154
34/21=1.619047619047619
55/34=1.6176470588235294
89/55=1.6181818181818182
144/89=1.6179775280898876
233/144=1.6180555555555556
377/233=1.6180257510729614
610/377=1.6180371352785146
987/610=1.618032786885246
1597/987=1.618034447821682
2584/1597=1.6180338134001253
4181/2584=1.618034055727554
6765/4181=1.6180339631667064
10946/6765=1.6180339985218033
17711/10946=1.618033985017358
28657/17711=1.6180339901755971
46368/28657=1.618033988205325
75025/46368=1.618033988957902
121393/75025=1.6180339886704431
196418/121393=1.6180339887802426
317811/196418=1.618033988738303
514229/317811=1.6180339887543225
832040/514229=1.6180339887482036

从上面的输出结果发现:

这就是我们熟悉的黄金分割——黄金比例,准确值是 ,这是一个无理数,小数点后20位的近似值是:

一般取 即可。

在历史上,伟大的天文学家开普勒,最早发现了这个结论。

开普勒(Johannes Kepler),德国天文学家、数学家。提出了著名的开普勒三定律,被誉为“为天文立法的人”

为斐波那契数列中的第 项,则:

根据斐波那契数列的特点,设定初始条件:

将(5.1)式写成:

以矩阵方式表示为:

推导通项表达式 的第一种方法

,由(5.2)式得到:

其中, ,初始条件

(5.3)式即为《机器学习数学基础》第3章3.2.2节中所介绍的差分方程。又因为:

所以,只要计算出 ,然后将它与初始条件 相乘,即得到

对于矩阵 ,其特征多项式:

根据特征方程: ,得:

解得:

显然,

再根据 计算(5.6)中每个特征值所对应的特征向量。

时,设特征向量为 ,则:

可得: 是满足条件的一个向量,即特征向量 (将 代入到上式,并利用(5.5)式,上式成立,即说明 是一个基向量)。

同理,求得另外一个特征值 所对应的特征向量

很显然,特征向量 线性无关,则矩阵 可以对角化(参阅《机器学习数学基础》第3章3.3.3节)。

,其中 的列向量即为 的特征向量, 为特征向量构成的对角矩阵(参阅《机器学习数学基础》第3章3.3.3节(3.3.10)式和(3.3.11)式)。

因为:

于是(5.4)式可以转化为:

由于 是一个列向量,则 结果也是列向量,故令 ,(5.8)式变换为:

若用一般化的式子表示, (由 个特征向量组成), ,代入(5.9)式:

即:

,则(5.9)式即为: ,从而得到(5.10)式中的系数 。(5.10)式即为差分方程的通解(《机器学习数学基础》第3章3.2.2节,给出了差分方程通解的另外一种计算方法,在该方法中没有使用对矩阵 可对角化的假设)。

现在将 的特征向量(5.7)和特征值(5.6)式分别代入(5.10)式,即得到(5.3)差分方程的通解:

时,,代入(5.11)式:

解得:

代入(5.11),同时将特征值和特征向量也代入,得:

在(5.3)式中假设 ,对比上式,则:

这样就得到了斐波那契数列通项表达式。

推导通项表达式 的第二种方法

由于 可对角化,根据 得:

其中 由特征向量 构成),则

由(5.4)式可得:

将(5.13)代入上式,可得:

所以,通项表达式:

与(5.12)式一样的结果。

性质1:

证明

对于 ,由(5.12)式得:

证毕。

所以,在前面的程序中,显示后项除以前项的结果趋近于黄金分割。

性质2: 幂矩阵

因为 ,又因为斐波那契数列 ,所以:

以此类推,可得:

其中

这就是矩阵法计算斐波那契数列的程序编写依据。

性质3:Cassini等式

在《机器学习数学基础》第2章2.4节中曾经介绍过行列式的性质,下面应用其中一条:

由(5.14)可得:

又因为:

所以:

(5.15)式即为Cassini等式。表明了斐波那契数列的数之间的一种关系。如:

性质4: 项的和

由(5.1)式得:

上面各式等号相加,得:

因为 ,所以,前 项的和是:

性质5:每项的平方和

又因为: ,所以:

斐波纳契数是帕斯卡三角形的每一条红色对角线上数字的和。

参考文献

[1]. Python大学实用教程. 齐伟. 北京:电子工业出版社. 2019年3月,第1版

[2]. 跟老齐学Python:数据分析. 齐伟. 北京:电子工业出版社. 2018年6月,第1版

[3]. https://zh.wikipedia.org/wiki/黄金分割率

[4]. 开普勒的墓志铭:Mensus eram coelos, nunc terrae metior umbrasMens coelestis erat, corporis umbra iacet.(“我曾测天高,今欲量地深。”“我的灵魂来自上天,凡俗肉体归于此地。”)https://zh.wikipedia.org/wiki/约翰内斯·开普勒

作者: 老齐
链接: http://math.itdiffer.com/doingmath-01.html
来源: 老齐教室-机器学习数学基础
本文原创发布于「老齐教室-机器学习数学基础」,转载请注明出处,谢谢合作!

https://gitee.com/qiwsir/images/raw/master/2021-2-15/1613357594979-1.png

results matching ""

    No results matching ""