Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第三十一 向量(Vector)

向量(Vector)是线性代数的核心概念,也是物理学和工程学中描述方向和大小的基本工具。在计算机科学中,向量是机器学习、计算机图形学、物理模拟等领域的基础数据结构。


一、向量的定义

1.1 什么是向量

n 维向量:n 个有次序的数 $a_1, a_2, a_3, \ldots, a_n$ 所组成的数组称为 n 维向量。这 n 个数称为向量的 n 个分量,第 i 个分量 $a_i$ 为向量的第 i 个分量。

分量全为实数的向量称为实向量,分量中含有复数则称为复向量

向量通常用粗体字母或带箭头的字母表示:

$$\vec{a} = (a_1, a_2, \ldots, a_n)$$

1.2 特殊向量

向量类型定义表示
零向量所有元素都为零$\vec{0} = (0, 0, \ldots, 0)$
单位向量长度为 1 的向量$|\vec{a}| = 1$
负向量元素为对应元素取负数$-\vec{a} = (-a_1, -a_2, \ldots, -a_n)$
标准基向量只有一个分量为 1,其余为 0$\vec{e}_i$

二、向量的基本运算

2.1 向量的加法

两个同维向量相加,对应分量相加:

$$\vec{a} + \vec{b} = (a_1 + b_1, a_2 + b_2, \ldots, a_n + b_n)$$

2.2 向量的数乘

向量与标量相乘,每个分量都乘以该标量:

$$k\vec{a} = (ka_1, ka_2, \ldots, ka_n)$$

2.3 运算律

运算律公式
加法交换律$\vec{u} + \vec{v} = \vec{v} + \vec{u}$
加法结合律$(\vec{u} + \vec{v}) + \vec{w} = \vec{u} + (\vec{v} + \vec{w})$
数乘结合律$k(l\vec{u}) = (kl)\vec{u}$
数乘分配律(对向量)$k(\vec{u} + \vec{v}) = k\vec{u} + k\vec{v}$
数乘分配律(对标量)$(k + l)\vec{u} = k\vec{u} + l\vec{u}$
零向量性质$k\vec{u} = \vec{0} \Leftrightarrow k = 0 \text{ 或 } \vec{u} = \vec{0}$
fn vector_add(a: &[f64], b: &[f64]) -> Vec<f64> {
    a.iter().zip(b.iter()).map(|(x, y)| x + y).collect()
}

fn vector_scale(a: &[f64], k: f64) -> Vec<f64> {
    a.iter().map(|x| x * k).collect()
}

fn main() {
    let a = vec![1.0, 2.0, 3.0];
    let b = vec![4.0, 5.0, 6.0];

    println!("a + b = {:?}", vector_add(&a, &b));     // [5, 7, 9]
    println!("2a = {:?}", vector_scale(&a, 2.0));     // [2, 4, 6]
}

三、向量的内积(点积)

3.1 定义

两个 n 维向量的内积(点积)定义为:

$$\vec{a} \cdot \vec{b} = a_1b_1 + a_2b_2 + \cdots + a_nb_n = \sum_{i=1}^{n} a_ib_i$$

3.2 几何意义

$$\vec{a} \cdot \vec{b} = |\vec{a}| |\vec{b}| \cos\theta$$

其中 $\theta$ 是两个向量之间的夹角。

3.3 内积的性质

性质公式
非负性$\vec{a} \cdot \vec{a} \ge 0$,且等于 0 当且仅当 $\vec{a} = \vec{0}$
对称性$\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a}$
线性性$\vec{a} \cdot (\vec{b} + \vec{c}) = \vec{a} \cdot \vec{b} + \vec{a} \cdot \vec{c}$
数乘性$(k\vec{a}) \cdot \vec{b} = k(\vec{a} \cdot \vec{b})$

3.4 求向量夹角

$$\cos\theta = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}$$

$$\theta = \arccos\left(\frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}\right)$$

fn dot_product(a: &[f64], b: &[f64]) -> f64 {
    a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}

fn magnitude(a: &[f64]) -> f64 {
    a.iter().map(|x| x * x).sum::<f64>().sqrt()
}

fn angle_between(a: &[f64], b: &[f64]) -> f64 {
    let dot = dot_product(a, b);
    let mag_a = magnitude(a);
    let mag_b = magnitude(b);
    (dot / (mag_a * mag_b)).acos()
}

fn main() {
    let a = vec![1.0, 0.0];
    let b = vec![0.0, 1.0];

    println!("点积: {}", dot_product(&a, &b));           // 0
    println!("夹角: {:.2} 弧度 = {:.2}°", angle_between(&a, &b), angle_between(&a, &b).to_degrees());
    // 1.57 弧度 = 90°
}

四、向量的叉积

4.1 定义

两个三维向量的叉积定义为:

$$\vec{a} \times \vec{b} = \begin{vmatrix} \vec{i} & \vec{j} & \vec{k} \ a_1 & a_2 & a_3 \ b_1 & b_2 & b_3 \end{vmatrix}$$

$$= (a_2b_3 - a_3b_2, a_3b_1 - a_1b_3, a_1b_2 - a_2b_1)$$

4.2 几何意义

$$|\vec{a} \times \vec{b}| = |\vec{a}| |\vec{b}| \sin\theta$$

叉积的结果是一个向量,其方向垂直于 $\vec{a}$ 和 $\vec{b}$ 所在的平面(右手定则),大小等于以 $\vec{a}$ 和 $\vec{b}$ 为邻边的平行四边形的面积。

fn cross_product(a: &[f64; 3], b: &[f64; 3]) -> [f64; 3] {
    [
        a[1] * b[2] - a[2] * b[1],
        a[2] * b[0] - a[0] * b[2],
        a[0] * b[1] - a[1] * b[0],
    ]
}

fn main() {
    let a = [1.0, 0.0, 0.0];
    let b = [0.0, 1.0, 0.0];

    let c = cross_product(&a, &b);
    println!("a × b = {:?}", c);  // [0, 0, 1]
}

五、向量的长度(模)

5.1 定义

向量 $\vec{a}$ 的长度(模或范数)定义为:

$$|\vec{a}| = \sqrt{\vec{a} \cdot \vec{a}} = \sqrt{a_1^2 + a_2^2 + \cdots + a_n^2}$$

5.2 范数的类型

范数类型公式说明
L1 范数(曼哈顿范数)$|\vec{a}|_1 = \suma_i
L2 范数(欧几里得范数)$|\vec{a}|_2 = \sqrt{\sum a_i^2}$最常用的范数
L∞ 范数(无穷范数)$|\vec{a}|_\infty = \maxa_i
fn l1_norm(a: &[f64]) -> f64 {
    a.iter().map(|x| x.abs()).sum()
}

fn l2_norm(a: &[f64]) -> f64 {
    a.iter().map(|x| x * x).sum::<f64>().sqrt()
}

fn l_inf_norm(a: &[f64]) -> f64 {
    a.iter().map(|x| x.abs()).fold(0.0, f64::max)
}

fn main() {
    let a = vec![3.0, -4.0];
    println!("L1 范数: {}", l1_norm(&a));      // 7
    println!("L2 范数: {}", l2_norm(&a));      // 5
    println!("L∞ 范数: {}", l_inf_norm(&a));  // 4
}

六、余弦相似度

余弦相似度(Cosine Similarity)衡量两个向量在方向上的相似程度,而不关心它们的大小(长度)。它的值只取决于向量之间的夹角。

$$\cos(\theta) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| \cdot |\vec{b}|}$$

余弦相似度含义
1方向完全相同
0正交(垂直)
-1方向完全相反
fn cosine_similarity(a: &[f64], b: &[f64]) -> f64 {
    let dot = dot_product(a, b);
    let mag_a = magnitude(a);
    let mag_b = magnitude(b);
    dot / (mag_a * mag_b)
}

fn main() {
    let doc1 = vec![1.0, 2.0, 1.0, 0.0];  // 文档1的词频向量
    let doc2 = vec![1.0, 2.0, 1.0, 0.0];  // 文档2(与文档1相同)
    let doc3 = vec![0.0, 0.0, 0.0, 1.0];  // 文档3(完全不同)

    println!("doc1 与 doc2 相似度: {:.4}", cosine_similarity(&doc1, &doc2));  // 1.0000
    println!("doc1 与 doc3 相似度: {:.4}", cosine_similarity(&doc1, &doc3));  // 0.0000
}

应用: 余弦相似度在文本检索、推荐系统、图像识别等领域广泛应用,用于衡量两个对象的相似程度。


七、线性组合与线性相关性

7.1 线性组合

给定向量组 $\vec{a}_1, \vec{a}_2, \ldots, \vec{a}_m$,对于任何一组实数 $k_1, k_2, \ldots, k_m$,表达式:

$$k_1\vec{a}_1 + k_2\vec{a}_2 + \cdots + k_m\vec{a}_m$$

称为向量组的线性组合

  • 零向量是任意向量的线性组合(取所有系数为 0)
  • 向量组中的任一向量都可以表示为该向量组的线性组合

7.2 线性相关与线性无关

线性相关:如果存在不全为零的数 $k_1, k_2, \ldots, k_m$,使得:

$$k_1\vec{a}_1 + k_2\vec{a}_2 + \cdots + k_m\vec{a}_m = \vec{0}$$

则称向量组线性相关。

线性无关:只有当 $k_1 = k_2 = \cdots = k_m = 0$ 时上式才成立,则称向量组线性无关。

判定方法:

  • 两个非零向量线性相关 $\Leftrightarrow$ 它们的分量对应成比例
  • 向量组中含有零向量,则该向量组线性相关
  • 向量组中有两个向量成比例,则该向量组线性相关

7.3 极大线性无关组

极大线性无关组是向量组中满足以下条件的子集:

  • 该子集中的向量线性无关
  • 向量组中任意其他向量都可以由该子集线性表示
  • 添加任何其他向量都会使该子集变为线性相关

性质:

  • 同一个向量组的不同极大线性无关组所含向量个数相同
  • 这个个数称为向量组的
  • 向量组与其极大线性无关组等价

八、正交与标准正交向量组

8.1 正交向量

若两个非零向量 $\vec{\alpha}$ 和 $\vec{\beta}$ 的内积为零,即 $(\vec{\alpha}, \vec{\beta}) = 0$,则称它们正交

性质:

  • 正交向量组是线性无关的
  • 正交向量组中不含零向量

8.2 标准正交向量组

一组向量,其中每个向量都是单位向量(长度为 1),且两两正交(内积为 0),称为标准正交向量组(或单位正交向量组)。

8.3 施密特正交化

施密特正交化方法可以将一组线性无关的向量转化为一组正交向量:

$$\vec{\beta}_1 = \vec{\alpha}_1$$

$$\vec{\beta}_2 = \vec{\alpha}_2 - \frac{(\vec{\alpha}_2, \vec{\beta}_1)}{(\vec{\beta}_1, \vec{\beta}_1)}\vec{\beta}_1$$

$$\vec{\beta}_3 = \vec{\alpha}_3 - \frac{(\vec{\alpha}_3, \vec{\beta}_1)}{(\vec{\beta}_1, \vec{\beta}_1)}\vec{\beta}_1 - \frac{(\vec{\alpha}_3, \vec{\beta}_2)}{(\vec{\beta}_2, \vec{\beta}_2)}\vec{\beta}_2$$

一般地: $$ \vec{\beta}_k = \vec{\alpha}k - \sum{j=1}^{k-1} \frac{(\vec{\alpha}_k, \vec{\beta}_j)}{(\vec{\beta}_j, \vec{\beta}_j)}\vec{\beta}_j $$

fn gram_schmidt(vectors: &[Vec<f64>]) -> Vec<Vec<f64>> {
    let mut result = Vec::new();

    for v in vectors {
        let mut u = v.clone();
        for ortho in &result {
            let proj = dot_product(v, ortho) / dot_product(ortho, ortho);
            u = u.iter().zip(ortho.iter()).map(|(a, b)| a - proj * b).collect();
        }
        result.push(u);
    }

    result
}

fn main() {
    let vectors = vec![
        vec![1.0, 1.0, 0.0],
        vec![1.0, 0.0, 1.0],
        vec![0.0, 1.0, 1.0],
    ];

    let orthogonal = gram_schmidt(&vectors);
    println!("正交化结果:");
    for v in &orthogonal {
        println!("  {:?}", v);
    }
}

九、向量与矩阵的关系

矩阵可以对向量进行线性变换,包括拉升、压缩、旋转、剪切、平移等操作。

9.1 旋转变换

use nalgebra::{Rotation2, Vector2};

fn main() {
    let v = Vector2::new(1.0, 0.0);
    let angle = std::f64::consts::FRAC_PI_4; // 45°

    let rotation = Rotation2::new(angle);
    let rotated = rotation * v;

    println!("原向量: {:?}", v);
    println!("旋转 45° 后: {:?}", rotated);
    // (0.7071, 0.7071)
}

9.2 缩放变换

fn scale_vector(v: &[f64], sx: f64, sy: f64) -> Vec<f64> {
    vec![v[0] * sx, v[1] * sy]
}

fn main() {
    let v = vec![1.0, 1.0];
    let scaled = scale_vector(&v, 2.0, 0.5);
    println!("缩放后: {:?}", scaled);  // [2.0, 0.5]
}

十、向量在 Rust 中的应用

10.1 使用 nalgebra

use nalgebra as na;
use na::{Vector3, Point3};

fn main() {
    let a = Vector3::new(1.0, 2.0, 3.0);
    let b = Vector3::new(4.0, 5.0, 6.0);

    // 向量加法
    let c = a + b;
    println!("a + b = {:?}", c);

    // 向量数乘
    let d = a * 2.0;
    println!("2a = {:?}", d);

    // 点积
    let dot = a.dot(&b);
    println!("a · b = {}", dot);  // 32.0

    // 叉积
    let cross = a.cross(&b);
    println!("a × b = {:?}", cross);  // (-3, 6, -3)

    // 范数
    let norm = a.norm();
    println!("|a| = {}", norm);  // 3.7417

    // 归一化
    let unit = a.normalize();
    println!("单位向量 = {:?}", unit);

    // 距离
    let dist = a.metric_distance(&b);
    println!("距离 = {}", dist);

    // 点 (Point) 与向量 (Vector) 的区别
    let p = Point3::new(1.0, 2.0, 3.0);
    let q = Point3::new(4.0, 5.0, 6.0);
    let vec = q - p;  // 结果是 Vector3
    println!("两点间的向量 = {:?}", vec);
}

十一、总结

概念要点
向量定义n 个有次序的数组成的数组
向量加法对应分量相加
向量数乘每个分量乘以标量
点积$ \vec{a} \cdot \vec{b} = \sum a_ib_i = |\vec{a}| |\vec{b}| \cos\theta$
叉积仅定义于三维,结果垂直于原向量所在平面
向量长度$|\vec{a}| = \sqrt{\sum a_i^2}$
余弦相似度$ \cos\theta = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}||\vec{b}|}$
线性相关存在不全为零的系数使线性组合为零向量
正交内积为零,正交向量组线性无关
施密特正交化将线性无关向量组转化为正交向量组

向量是连接代数与几何的桥梁,也是现代计算机科学的基石。从物理中的力和速度,到机器学习中的特征向量,向量的概念无处不在。

练习建议:

  1. 实现向量的各种运算(加法、数乘、点积、叉积)
  2. 用余弦相似度实现简单的文档相似度计算
  3. 实现施密特正交化算法