第4章 线性代数

4.1 向量

抽象地说,向量是指可以加总(以生成新的向量),可以乘以标量(即数字),也可以生成 新的向量的对象。

具体来说(对我们而言),向量是有限维空间的点。即使你本无意视你的数据为向量,将数值数据表示为向量也是非常好的处理方式。

比如,如果你有很多人的身高、体重、年龄数据,就可以把数据记为三维向量 (height, weight, age)。如果你教的一个班有四门考试,就可以把学生成绩记为四维向量 (exam1, exam2, exam3, exam4)。

最简单的入门方法是将向量表示为数字的列表。一个包含三个数字的列表对应一个三维空 间的向量,反之亦然:


<code>

height_weight_age

=

[70,

170

,

40

]

grade

=

[95,

80

,#考试2

75

,#考试3

62

]

/<code>

首先,我们常常需要对两个向量做加法。向量以分量方式做运算。这意味着,如果两个向量v和w长度相同,那它们的和就是一个新的向量,

其中向量的第一个元素等于 v[0] +w[0], 第二个元素等于v[1] +w[1], 以此类推。(如果两个向量的长度不同,则不能相加。)

例如, 向量[1, 2]加上向量[2, 1] 等于[1+2 , 2+1] 或[3, 3] , 如图 4,1

第4章 线性代数

图4-1

我们可以很容易的实现这个功能: 对向量调用zip函数, 同时用列表解析使向量的相应元素相加:

<code>

def

vector_add

(v , w)

""

"adds corresponding elements"

""

return

[v_i + w_i

for

v_i , w_i

in

zip(v , w)] 同样,对两个向量做减法,只需要使向量的相应元素相减:

def

vector_subtract

( v, w)

:

""

"subtracts corresponding elements"

""

return

[v_i - w_i

for

v_i, w_i

in

zip(v, w)] /<code>

有时,我们需要对一系列向量做加法。即生成一个新向量, 其第一个元素是这一系列向量第一个元素的和,第二个元素是这一系列向量第二个元素的和,以此类推。最简单的方法是每次递加一个向量:

<code>

def

vector_sum

(vectors)

:

"""sums all corresponding elements"""

result = vectors[

0

]

for

vector

in

vectors[

1

: ]: result = vector_add(result, vector)

return

result/<code>

当你思考这个解决方法时,我们正通过 vector_add 函数,即使用 reduce 的方式来加总这 一系列的向量。换句话说,我们用高级的函数更加简洁地实现了这个功能:

<code>

def

vector_sum

(vectors)

:

return

reduce(vector_add, vectors)/<code>

4.2 矩阵

矩阵是一个二维的数据集合。我们将矩阵表示为列表的列表,每个内部列表的大小都一 样,表示矩阵的一行。如果 A 是一个矩阵,那么 A[i][j] 就表示第 i 行第 j 列的元素。按照 数学表达的惯例,我们通常用大写字母表示矩阵。例如:

<code>A = 

[[1, 2, 3], # A有2行3列 [4, 5, 6]]

B =

[[1, 2], # B有3行2列 [3, 4], [5, 6]]

/<code>

在数学中,矩阵的第一行通常称为“第 1 行”,第一列通常称为“第 1 列”。 而我们需要将矩阵的形式和 Python 的列表统一起来:Python 中的列表从 0 开始索引,所以,我们将矩阵的第一行称为“第 0 行”,将第一列称为“第 0 列”。

基于列表的列表这种表达形式,矩阵 A 具有 len(A) 行和 len(A[0]) 列,我们把这称作它的 形状(shape):

<code>

def

shape

(A)

:

num_rows = len(A) num_cols = len(A[

0

])

if

A

else

0

return

num_rows, num_cols/<code>

如果一个矩阵有 n 行 k 列,则可以记为 n×k 矩阵。我们可以把这个 n×k 矩阵的每一行都 当作一个长度为 k 的向量,把每一列都当作一个长度为 n 的向量

<code>

def

get_row

(A, i)

:

return

A[i]

def

get_column

(A, j)

:

return

[A_i[j]

for

A_i

in

A] /<code>

同样,我们也可以根据形状和用来生成元素的函数来创建矩阵。可以通过一个嵌套的列表 解析来实现:

<code>/<code>

有了这个函数,就可以生成一个 5×5 的单位矩阵(对角线元素是 1,其他元素是 0):

<code>

def

is_diagonal

(i, j)

:

"""1's on the 'diagonal', 0's everywhere else"""

return

1

if

i == j

else

0

identity_matrix = make_matrix(

5

,

5

, is_diagonal) /<code>

矩阵的重要性不言而喻,原因有以下几个。

首先,可以通过将每个向量看成是矩阵的一行,来用矩阵表示一个包含多维向量的数据 集。例如,如果有 1000 个人的身高、体重和年龄,就可以创建一个 1000×3 的矩阵

<code>

data

=

[[70,

170

,

40

],

[65,

120

,

26

],

[77,

250

,

19

],

]

/<code>

第二,随后我们会看到,可以用一个 n×k 矩阵表示一个线性函数,这个函数将一个 k 维的

向量映射到一个 n 维的向量上。我们使用的很多技术与概念都隐含着这样的函数。

第三,可以用矩阵表示二维关系。在第 1 章中,我们曾经将网络边际表示为数据对 (i, j) 的集合。我们还可以通过建立矩阵 A 来实现这个描述:如果节点 i 和节点 j 有关系,则用 矩阵的元素 A[i][j] 为 1 来表示;若节点 i 和节点 j 没有关系,则用 A[i][j] 为 0 来表示。

回想前面讲过的关系:

<code>

friendships

=

[(0,

1

),

(0,

2

),

(1,

2

),

(1,

3

),

(2,

3

),

(3,

4

),

(4,

5

),

(5,

6

),

(5,

7

),

(6,

8

),

(7,

8

),

(8,

9

)]

/<code>

可以通过矩阵形式再现:

<code>  
 

friendships

=

[[0,

1

,

1

,

0

,

0

,

0

,

0

,

0

,

0

,

0

],

[1,

0

,

1

,

1

,

0

,

0

,

0

,

0

,

0

,

0

],

[1,

1

,

0

,

1

,

0

,

0

,

0

,

0

,

0

,

0

],

[0,

1

,

1

,

0

,

1

,

0

,

0

,

0

,

0

,

0

],

[0,

0

,

0

,

1

,

0

,

1

,

0

,

0

,

0

,

0

],

[0,

0

,

0

,

0

,

1

,

0

,

1

,

1

,

0

,

0

],

[0,

0

,

0

,

0

,

0

,

1

,

0

,

0

,

1

,

0

],

[0,

0

,

0

,

0

,

0

,

1

,

0

,

0

,

1

,

0

],

[0,

0

,

0

,

0

,

0

,

0

,

1

,

1

,

0

,

1

],

[0,

0

,

0

,

0

,

0

,

0

,

0

,

0

,

1

,

0

]]

/<code>

如果关系很少,这种表示形式的效率就会很低,因为必须存储很多零。但是,通过矩阵表 示可以快速地查找确认某两个节点是否是连接的——通过一个矩阵查找函数即可,不必 (有可能会)搜索每条边:

<code>friendships[

0

][

2

] == 1 # true, 0和2是朋友 friendships[

0

][

8

] == 1 # false, 0和8不是朋友/<code>

同样,若想找到一个节点的所有连接,只需检查这个节点所在的列(或行):

<code>friends_of_five = [i                                                             
                   for i, is_friend in enumerate(friendships[5])    
                   if is_friend]                                                       
/<code>

为了加速这个搜寻过程,我们之前给每个节点对象都添加了一个连接列表。但对于太大 的、扩展性强的图形来说,成本太高,维护也很难。

本书随后还会探讨这些矩阵。


分享到:


相關文章: