Skip to content

Latest commit

 

History

History
474 lines (338 loc) · 16.5 KB

matrix_2d.md

File metadata and controls

474 lines (338 loc) · 16.5 KB

为了学好Rust也是拼了系列-数学库-二维矩阵

二维矩阵是数学中的一个重要概念,它是由元素排列成行和列的矩形数组。通常用大写字母表示矩阵,例如 $$A$$,矩阵中的元素用小写字母表示,例如 $$a_{ij}$$,其中 $$i$$ 表示行索引,$$j$$ 表示列索引。

一个二维矩阵 $$A$$ 可以表示为:

$$ A = \begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \ a_{21} & a_{22} & \cdots & a_{2n} \ \vdots & \vdots & \ddots & \vdots \ a_{m1} & a_{m2} & \cdots & a_{mn} \end{bmatrix} $$

这里,$$m$$ 是矩阵的行数,$$n$$ 是矩阵的列数。矩阵的大小通常以 "行数 × 列数" 的形式表示。

例如,一个 2x3 的矩阵可以写作:

$$ B = \begin{bmatrix} b_{11} & b_{12} & b_{13} \ b_{21} & b_{22} & b_{23} \end{bmatrix} $$

其中,这个矩阵有两行($$m=2$$)和三列($$n=3$$)。

程序定义如下

pub struct Matrix {
    rows: usize,    // 行数
    cols: usize,    // 列数
    data: Vec<Vec<f64>>, // 存储矩阵元素的二维向量
}

矩阵加减法

矩阵的加法和减法是基本的线性代数运算,它们分别在相同位置对应元素进行加法和减法。以下是矩阵加法和减法的数学定义:

矩阵加法:

给定两个相同大小的矩阵 $$A$$$$B$$,它们的和 $$C$$(记作 $$C = A + B$$)的每个元素 $$c_{ij}$$ 都是对应位置元素的和:

$$ C = A + B $$ $$ c_{ij} = a_{ij} + b_{ij} $$

其中,$$a_{ij}$$ 是矩阵 $$A$$ 中第 $$i$$ 行第 $$j$$ 列的元素,$$b_{ij}$$ 是矩阵 $$B$$ 中相应位置的元素。

矩阵减法:

给定两个相同大小的矩阵 $$A$$$$B$$,它们的差 $$D$$(记作 $$D = A - B$$)的每个元素 $$d_{ij}$$ 都是对应位置元素的差:

$$ D = A - B $$ $$ d_{ij} = a_{ij} - b_{ij} $$

同样,其中 $$a_{ij}$$ 是矩阵 $$A$$ 中第 $$i$$ 行第 $$j$$ 列的元素,$$b_{ij}$$ 是矩阵 $$B$$ 中相应位置的元素。

注意:矩阵加法和减法要求两个矩阵的维度必须相同,即它们必须有相同的行数和列数。只有在这种情况下,对应位置的元素才能进行加法或减法运算。

程序解如下

// 矩阵加法
pub fn add(&self, other: &Matrix) -> Option<Matrix> {
    if self.rows == other.rows && self.cols == other.cols {
        let mut result_data = Vec::with_capacity(self.rows);
        for i in 0..self.rows {
            let mut row = Vec::with_capacity(self.cols);
            for j in 0..self.cols {
                row.push(self.data[i][j] + other.data[i][j]);
            }
            result_data.push(row);
        }
        Some(Matrix {
            rows: self.rows,
            cols: self.cols,
            data: result_data,
        })
    } else {
        None
    }
}

// 矩阵减法
pub fn subtract(&self, other: &Matrix) -> Option<Matrix> {
    if self.rows == other.rows && self.cols == other.cols {
        let mut result_data = Vec::with_capacity(self.rows);
        for i in 0..self.rows {
            let mut row = Vec::with_capacity(self.cols);
            for j in 0..self.cols {
                row.push(self.data[i][j] - other.data[i][j]);
            }
            result_data.push(row);
        }
        Some(Matrix {
            rows: self.rows,
            cols: self.cols,
            data: result_data,
        })
    } else {
        None
    }
}

矩阵的乘法

矩阵乘法是一个涉及行和列的复杂运算。给定两个矩阵 $$A$$$$B$$,它们的乘积 $$C$$(记作 $$C = A \cdot B$$)的定义如下:

假设矩阵 $$A$$ 的维度为 $$m \times n$$,矩阵 $$B$$ 的维度为 $$n \times p$$,那么矩阵 $$C$$ 的维度为 $$m \times p$$

矩阵 $$C$$ 中的每个元素 $$c_{ij}$$ 计算方式如下:

$$ c_{ij} = \sum_{k=1}^{n} a_{ik} \cdot b_{kj} $$

其中,$$a_{ik}$$ 是矩阵 $$A$$ 中第 $$i$$ 行第 $$k$$ 列的元素,$$b_{kj}$$ 是矩阵 $$B$$ 中第 $$k$$ 行第 $$j$$ 列的元素。这个计算过程是对矩阵 $$A$$ 的第 $$i$$ 行和矩阵 $$B$$ 的第 $$j$$ 列进行点积(内积),得到矩阵 $$C$$ 中元素的值。

矩阵乘法的关键要点:

  1. 乘法的前提是第一个矩阵的列数等于第二个矩阵的行数。
  2. 乘积矩阵的行数等于第一个矩阵的行数,列数等于第二个矩阵的列数。
  3. 矩阵乘法不满足交换律,即一般情况下 $$A \cdot B \neq B \cdot A$$

程序解如下

/// 矩阵的乘法
pub fn multiply(&self, other: &Matrix) -> Option<Matrix> {
    // 检查矩阵维度是否允许相乘
    if self.cols != other.rows {
        return None;
    }

    let mut result_data = Vec::with_capacity(self.rows);
    for i in 0..self.rows {
        let mut row = Vec::with_capacity(other.cols);
        for j in 0..other.cols {
            let mut sum = 0.0;
            for k in 0..self.cols {
                sum += self.data[i][k] * other.data[k][j];
            }
            row.push(sum);
        }
        result_data.push(row);
    }

    Some(Matrix {
        rows: self.rows,
        cols: other.cols,
        data: result_data,
    })
}

转置

矩阵的转置是一种操作,通过这种操作,矩阵的行和列交换位置。给定一个矩阵 $$A$$,其转置记作 $$A^T$$。对于矩阵 $$A$$ 中的每一个元素 $$a_{ij}$$,在转置后的矩阵 $$A^T$$ 中,该元素位于 $$A$$ 中的列 $$j$$ 和行 $$i$$ 的位置。

具体来说,如果矩阵 $$A$$ 的维度是 $$m \times n$$,那么它的转置 $$A^T$$ 的维度是 $$n \times m$$。矩阵转置的数学表示如下:

$$ (A^T){ij} = A{ji} $$

其中,$$(A^T){ij}$$ 表示转置矩阵 $$A^T$$ 中的第 $$i$$ 行第 $$j$$ 列的元素,而 $$A{ji}$$ 表示原矩阵 $$A$$ 中的第 $$j$$ 行第 $$i$$ 列的元素。

矩阵转置的性质:

  1. $$(A^T)^T = A$$,即对一个矩阵进行两次转置等于原矩阵。
  2. $$(A + B)^T = A^T + B^T$$,即转置的和等于和的转置。
  3. $$(kA)^T = kA^T$$,其中 $$k$$ 是常数。

下面是一个简单的例子,演示了如何计算矩阵的转置:

假设有矩阵 $$A$$

$$ A = \begin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \end{bmatrix} $$

那么,矩阵 $$A$$ 的转置 $$A^T$$ 是:

$$ A^T = \begin{bmatrix} 1 & 4 \ 2 & 5 \ 3 & 6 \end{bmatrix} $$

你可以看到,原矩阵 $$A$$ 的行变成了转置矩阵 $$A^T$$ 的列,而原矩阵 $$A$$ 的列变成了转置矩阵 $$A^T$$ 的行。

程序解如下

/// 转置矩阵
pub fn transpose(&self) -> Matrix {
    let mut result_data = Vec::with_capacity(self.cols);
    for j in 0..self.cols {
        let mut row = Vec::with_capacity(self.rows);
        for i in 0..self.rows {
            row.push(self.data[i][j]);
        }
        result_data.push(row);
    }

    Matrix {
        rows: self.cols,
        cols: self.rows,
        data: result_data,
    }
}

行列式

行列式是一个与方阵相关的数学概念,它为方阵提供了一个标量值。给定一个 $$n \times n$$ 的方阵 $$A$$,其行列式通常表示为 $$|A|$$$$\text{det}(A)$$

行列式的计算涉及矩阵的元素,其表达式如下:

$$ |A| = \sum_{\sigma \in S_n} \text{sgn}(\sigma) \cdot a_{1\sigma(1)} \cdot a_{2\sigma(2)} \cdot \ldots \cdot a_{n\sigma(n)} $$

这里,$$S_n$$ 是所有 $$n$$ 阶排列的集合,$$\sigma$$ 是一个排列,$$\text{sgn}(\sigma)$$ 是排列的符号(奇置换为 -1,偶置换为 1),$$a_{i\sigma(i)}$$ 是矩阵 $$A$$ 中的元素。

这个公式展示了一个递归的计算过程,它对所有可能的排列进行了求和。对于每个排列,我们将相应位置的元素相乘,然后乘以符号 $$\text{sgn}(\sigma)$$。最后,将所有这些结果相加得到最终的行列式值。

行列式有一些重要的性质:

  1. 行列式的值与矩阵的行列互换无关,即 $$|A| = |A^T|$$
  2. 如果矩阵有两行或两列相同,则行列式为零。
  3. 行列式的值等于它的伴随矩阵(adjugate matrix)与原矩阵的乘积。

程序解如下

// 计算矩阵的行列式
    pub fn determinant(&self) -> Option<f64> {
        // 检查矩阵是否为方阵
        if self.rows != self.cols {
            return None;
        }

        // 递归计算行列式
        self.calculate_determinant(&self.data)
    }

    // 递归计算行列式的辅助函数
    fn calculate_determinant(&self, matrix: &Vec<Vec<f64>>) -> Option<f64> {
        match self.rows {
            1 => Some(matrix[0][0]), // 1x1 矩阵的行列式为其唯一元素的值
            2 => Some(matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]), // 2x2 矩阵的行列式计算公式
            _ => {
                let mut det = 0.0;

                for col in 0..self.cols {
                    // 计算代数余子式
                    let submatrix_data: Vec<Vec<f64>> = matrix
                        .iter()
                        .enumerate()
                        .filter(|&(i, _)| i != 0)
                        .map(|(_, row)| row.iter().enumerate().filter(|&(j, _)| j != col).map(|(_, &val)| val).collect())
                        .collect();

                    let submatrix = Matrix {
                        rows: self.rows - 1,
                        cols: self.cols - 1,
                        data: submatrix_data,
                    };

                    let cofactor = if col % 2 == 0 { 1.0 } else { -1.0 };

                    match self.calculate_determinant(&submatrix.data) {
                        Some(sub_det) => det += cofactor * matrix[0][col] * sub_det,
                        None => return None,
                    }
                }

                Some(det)
            }
        }
    }

逆矩阵

逆矩阵是与原矩阵相乘后得到单位矩阵的矩阵。给定一个方阵 $$A$$,如果存在一个方阵 $$B$$ 使得 $$AB = BA = I$$,其中 $$I$$ 是单位矩阵,那么矩阵 $$B$$ 就是矩阵 $$A$$ 的逆矩阵,通常表示为 $$A^{-1}$$

逆矩阵的性质:

  1. 只有方阵才有可能有逆矩阵。
  2. 如果矩阵 $$A$$ 有逆矩阵,则逆矩阵唯一。

逆矩阵的计算方法: 如果一个 $$n \times n$$ 的矩阵 $$A$$ 可逆,其逆矩阵 $$A^{-1}$$ 可以通过以下公式计算:

$$ A^{-1} = \frac{1}{\text{det}(A)} \cdot \text{adj}(A) $$

其中,$$\text{det}(A)$$ 是矩阵 $$A$$ 的行列式,$$\text{adj}(A)$$ 是矩阵 $$A$$ 的伴随矩阵。伴随矩阵的元素由原矩阵的代数余子式组成,并按矩阵的转置排列。

具体而言,对于 $$n \times n$$ 矩阵 $$A$$,其伴随矩阵的元素 $$(\text{adj}(A))_{ij}$$ 可以通过以下方式计算:

$$ (\text{adj}(A)){ij} = (-1)^{i+j} \cdot \text{det}(M{ij}) $$

其中,$$M_{ij}$$ 是去掉矩阵 $$A$$ 的第 $$i$$ 行和第 $$j$$ 列后的子矩阵。

特征值和特征向量

特征值和特征向量是矩阵理论中的重要概念,它们在许多数学和工程应用中起着关键的作用。

特征值和特征向量的定义

给定一个方阵 $$A$$,非零向量 $$v$$$$A$$ 的特征向量,如果存在一个标量 $$\lambda$$,使得满足如下关系:

$$ Av = \lambda v $$

这里,$$\lambda$$ 是 $$A$$ 的特征值,$$v$$ 是对应于特征值 $$\lambda$$ 的特征向量。

解释和性质

  1. 特征值(Eigenvalues): 特征值是一个标量,它表示了矩阵 $$A$$ 在变换中的缩放因子。一个 $$n \times n$$ 的矩阵 $$A$$ 最多有 $$n$$ 个特征值。

  2. 特征向量(Eigenvectors): 特征向量是与特征值关联的非零向量,其在矩阵变换下只发生缩放而不改变方向。特征向量的长度(模)不是固定的,其方向是关键的。

  3. 特征值方程(Characteristic Equation): 特征值和特征向量的关系由特征值方程表示:

$$ \text{det}(A - \lambda I) = 0 $$

其中,$$\text{det}$$ 表示矩阵的行列式,$$I$$ 是单位矩阵。特征值是满足方程的根。

  1. 特征空间: 与特定特征值关联的所有特征向量形成一个向量空间,称为特征空间。

  2. 线性独立性: 不同特征值对应的特征向量是线性独立的,即它们不可由其他特征向量的线性组合表示。

  3. 对角化: 如果矩阵 $$A$$$$n$$ 个线性独立的特征向量,可以构成一个特征向量矩阵 $$V$$,则 $$A$$ 可对角化为 $$V^{-1}AV = \Lambda$$,其中 $$\Lambda$$ 是一个对角矩阵,其对角线上的元素是 $$A$$ 的特征值。

程序解如下

// 计算矩阵的特征值和特征向量
pub fn eigenvalue_eigenvector(&self) -> Option<(Vec<f64>, Vec<Vec<f64>>)> {
    // 检查矩阵是否为方阵
    if self.rows != self.cols {
        return None;
    }

    // 构造特征值方程的左侧矩阵 A - λI
    let a_minus_lambda_i: Matrix = self.subtract_identity();

    // 求解特征值
    let eigenvalues = match a_minus_lambda_i.determinant() {
        Some(det) => self.find_eigenvalues(det),
        None => return None,
    };

    // 求解特征向量
    let mut eigenvectors: Vec<Vec<f64>> = Vec::with_capacity(eigenvalues.len());
    for &eigenvalue in &eigenvalues {
        let eigenvector = self.solve_eigenvector(eigenvalue);
        eigenvectors.push(eigenvector);
    }

    Some((eigenvalues, eigenvectors))
}

// 辅助方法:构造 A - λI
fn subtract_identity(&self) -> Matrix {
    let mut result_data = Vec::with_capacity(self.rows);
    for i in 0..self.rows {
        let mut row = Vec::with_capacity(self.cols);
        for j in 0..self.cols {
            let element = if i == j {
                self.data[i][j] - 1.0 // λ 对应的位置减去 1
            } else {
                self.data[i][j]
            };
            row.push(element);
        }
        result_data.push(row);
    }

    Matrix {
        rows: self.rows,
        cols: self.cols,
        data: result_data,
    }
}

// 辅助方法:求解特征值方程的根
fn find_eigenvalues(&self, det_a_minus_lambda_i: f64) -> Vec<f64> {
    // 这里可以使用任何你喜欢的求根算法,这里简化为一个简单的方式
    // 注意:这里没有处理重根的情况,实际应用中可能需要更复杂的算法
    let mut eigenvalues = Vec::with_capacity(self.rows);
    for i in 0..self.rows {
        eigenvalues.push(self.data[i][i]);
    }
    eigenvalues
}

// 辅助方法:求解特征向量
fn solve_eigenvector(&self, eigenvalue: f64) -> Vec<f64> {
    // 这里可以使用任何你喜欢的求解线性方程组的算法,这里简化为一个简单的方式
    // 注意:这里没有处理奇异矩阵的情况,实际应用中可能需要更复杂的算法
    let mut augmented_matrix_data = Vec::with_capacity(self.rows);
    for i in 0..self.rows {
        let mut row = Vec::with_capacity(self.cols + 1);
        for j in 0..self.cols {
            row.push(self.data[i][j] - eigenvalue * if i == j { 1.0 } else { 0.0 });
        }
        row.push(0.0); // 右侧的常数项
        augmented_matrix_data.push(row);
    }

    let mut  augmented_matrix = Matrix {
        rows: self.rows,
        cols: self.cols + 1,
        data: augmented_matrix_data,
    };

    // 简单的高斯消元法求解线性方程组
    let mut solution = Vec::with_capacity(self.rows);
    for i in 0..self.rows {
        let mut pivot_row = i;
        for j in i + 1..self.rows {
            if augmented_matrix.data[j][i].abs() > augmented_matrix.data[pivot_row][i].abs() {
                pivot_row = j;
            }
        }

        if augmented_matrix.data[pivot_row][i].abs() < EPSILON {
            // 这里可以添加一些处理奇异矩阵的逻辑
            return vec![];
        }

        augmented_matrix.swap_rows(i, pivot_row);

        for j in i + 1..self.rows {
            let factor = augmented_matrix.data[j][i] / augmented_matrix.data[i][i];
            for k in i..self.cols + 1 {
                augmented_matrix.data[j][k] -= factor * augmented_matrix.data[i][k];
            }
        }
    }

    // 回代求解
    for i in (0..self.rows).rev() {
        let mut sum = 0.0;
        for j in i + 1..self.cols {
            sum += augmented_matrix.data[i][j] * solution[j - i - 1];
        }
        solution.push((augmented_matrix.data[i][self.cols] - sum) / augmented_matrix.data[i][i]);
    }

    // 归一化特征向量
    let norm = solution.iter().fold(0.0, |acc, &x| acc + x.powi(2)).sqrt();
    solution.iter().map(|&x| x / norm).collect()
}

// 辅助方法:交换矩阵的两行
fn swap_rows(&self, i: usize, j: usize) {
    let mut data_copy = self.data.clone();
    data_copy.swap(i, j);
    Matrix {
        rows: self.rows,
        cols: self.cols,
        data: data_copy,
    };
}