commons-math3(科学计算与线性代数)
-
- 1、概述
- 2、基础统计 (Stat)
- 3、回归分析 (Regression)
- 4、线性代数 (Linear)
-
- [4.1、核心 API 与类体系](#4.1、核心 API 与类体系)
-
- 4.1.1、向量(Vector)与矩阵(Matrix)
- [4.1.2、矩阵分解与求解器(Decomposition & Solver)](#4.1.2、矩阵分解与求解器(Decomposition & Solver))
- 4.2、使用示例
-
- [4.2.1、1. 基础矩阵与向量运算](#4.2.1、1. 基础矩阵与向量运算)
- [4.2.2、求解线性方程组 A x = b \mathbf{A}\mathbf{x} = \mathbf{b} Ax=b 与矩阵求逆](#4.2.2、求解线性方程组 A x = b \mathbf{A}\mathbf{x} = \mathbf{b} Ax=b 与矩阵求逆)
- 4.2.3、特征值与特征向量分解 (EigenDecomposition)
- 4.2.4、奇异值分解 (SingularValueDecomposition - SVD)
- 4.3、性能与避坑指南
1、概述
commons-math3(Apache Commons Math 3)是 Java 中功能最丰富、使用最广泛的轻量级数学与统计学计算库。它不依赖任何第三方二进制库,完全由纯 Java 实现,涵盖了从基础算法、线性代数、随机数生成到复杂的数值优化、插值、拟合及微分方程求解等全套数学工具。
| 分类模块 | 核心包 / 类 (Package / Class) | 核心功能与解决问题 |
|---|---|---|
| 基础统计 (Stat) | DescriptiveStatistics SummaryStatistics StatUtils | 单变量统计:均值、方差、标准差、分位数、中位数、偏度、峰度等计算 |
| 回归分析 (Regression) | SimpleRegression OLSMultipleLinearRegression | 一元线性回归与普通最小二乘法(OLS)多元线性回归 |
| 线性代数 (Linear) | RealMatrix, RealVector Array2DRowRealMatrix LUDecomposition, EigenDecomposition | 矩阵与向量运算、矩阵分解(LU、QR、SVD 奇异值分解、特征值分解)及线性方程组求解 |
| 数值分析与插值 (Analysis) | UnivariateFunction SplineInterpolator, PolynomialFunction UnivariateSolver | 函数求根(二分法、牛顿法)、插值计算(三次样条插值)、多项式运算 |
| 优化与拟合 (Optim) | SimplexOptimizer CurveFitter, ParametricUnivariateFunction | 线性规划(单纯形法)、非线性最小二乘拟合(如高斯拟合、自定义曲线拟合) |
| 分布与概率 (Distribution) | NormalDistribution TDistribution, PoissonDistribution | 概率分布计算:概率密度(PDF)、累积分布(CDF)、分位数(Inverse CDF) |
| 复数与数值计算 (Complex/Util) | Complex, ComplexUtils FastMath | 复数运算、高精度/高性能数学运算(FastMath 可替代 java.lang.Math) |
xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
2、基础统计 (Stat)
Apache Commons Math(commons-math3)是 Java 中功能极其强大的数学与统计计算库。其基础统计模块(Statics) 提供了非常完备的 API,涵盖了描述性统计、频数统计、假设检验以及回归分析等能力。
commons-math3 的基础统计 API 主要分为两大计算模式:
- 内存加载模式(Frame-based / In-Memory):将数据保存在内存数组中,支持计算中位数、分位数等依赖全量数据的指标。
- 流式增量模式(Stream-based / Incremental):数据随到随算,无需将所有数据保存在内存中,空间复杂度为 O ( 1 ) O(1) O(1),非常适合大数据流或超大文件的实时统计。
2.1、核心统计 API 概览
| 核心类 / 接口 | 核心职责 | 存储模式 | 特性与适用场景 |
|---|---|---|---|
| DescriptiveStatistics | 描述性统计全家桶 | 内存存储 (保存所有双精度值) | 既能算均值、方差,也能算中位数、百分位数(Percentile) |
| SummaryStatistics | 基础描述性统计 | 流式增量 (不保存原始数据) | 占用内存极小,无法计算中位数/百分位数 |
| StatUtils | 静态统计工具类 | 不存储(即时计算) | 类似于 java.lang.Math,对 double\[\] 数组进行直接静态计算 |
| Frequency | 频数/频率统计 | 内部 Map 计数 | 统计不同元素出现的次数、百分比及累计频数 |
| SimpleRegression | 一元线性回归 | 流式增量 | 实时计算斜率、截距、相关系数 R 2 R^2 R2 等 |
2.2、使用示例
2.2.1、内存全量描述性统计 (DescriptiveStatistics)
DescriptiveStatistics 在内存中维护了一个可变长数组。除了均值、极值外,它还支持计算百分位数(Percentile)和偏度/峰度(Skewness/Kurtosis)。
java
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
public class DescriptiveStatsDemo {
public static void main(String[] args) {
// 1. 创建 DescriptiveStatistics 实例
DescriptiveStatistics stats = new DescriptiveStatistics();
// 2. 动态添加数据点
double[] inputData = {12.5, 18.2, 25.0, 14.8, 30.1, 8.9, 15.4, 22.3, 19.0};
for (double val : inputData) {
stats.addValue(val);
}
// 3. 读取各类基础统计指标
System.out.println("数据总量 (N): " + stats.getN());
System.out.println("最小值 (Min): " + stats.getMin());
System.out.println("最大值 (Max): " + stats.getMax());
System.out.println("算术平均值 (Mean): " + stats.getMean());
System.out.println("标准差 (Standard Deviation): " + stats.getStandardDeviation());
System.out.println("方差 (Variance): " + stats.getVariance());
// 4. 计算百分位数(依赖内存排序,流式计算无法完成)
System.out.println("中位数 (50th Percentile): " + stats.getPercentile(50));
System.out.println("P95 分位数 (95th Percentile): " + stats.getPercentile(95));
// 5. 高阶矩统计 (偏度与峰度)
System.out.println("偏度 (Skewness): " + stats.getSkewness());
System.out.println("峰度 (Kurtosis): " + stats.getKurtosis());
}
}
java
数据总量 (N): 9
最小值 (Min): 8.9
最大值 (Max): 30.1
算术平均值 (Mean): 18.46666666666667
标准差 (Standard Deviation): 6.5482822174979605
方差 (Variance): 42.88
中位数 (50th Percentile): 18.2
P95 分位数 (95th Percentile): 30.1
偏度 (Skewness): 0.4256186537979001
峰度 (Kurtosis): -0.16232139267639223
2.2.2、流式增量描述性统计 (SummaryStatistics)
在处理成千上万或海量流式数据时,若只需要均值、方差、极值等指标,应绝对优先选择 SummaryStatistics。它每接收一个数据只更新内部累加器(滚动更新公式),极省内存。
java
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
public class SummaryStatsDemo {
public static void main(String[] args) {
// 1. 创建 SummaryStatistics 实例
SummaryStatistics stats = new SummaryStatistics();
// 2. 模拟海量数据流式写入
for (int i = 1; i <= 1000000; i++) {
stats.addValue(i); // 滚动更新,无需将 100 万个 double 存入内存
}
// 3. 获取统计结果
System.out.println("总量: " + stats.getN());
System.out.println("均值: " + stats.getMean());
System.out.println("标准差: " + stats.getStandardDeviation());
System.out.println("几何平均值: " + stats.getGeometricMean());
System.out.println("平方和: " + stats.getSumsq());
// 注意:stats.getPercentile(50); <-- 报错!SummaryStatistics 不支持百分位数/中位数计算
}
}
java
总量: 1000000
均值: 500000.5
标准差: 288675.27893234405
几何平均值: 367882.3204623857
平方和: 3.3333383333312755E17
2.2.3、静态工具类直接计算 (StatUtils)
对于已有现成 double\[\] 数组且无需实例化对象的场景,直接调用 StatUtils 的静态方法最快捷:
java
import org.apache.commons.math3.stat.StatUtils;
public class StatUtilsDemo {
public static void main(String[] args) {
double[] values = new double[] { 2.3, 5.4, 1.2, 8.9, 3.1, 4.5 };
// 直接进行静态计算
double mean = StatUtils.mean(values);
double sum = StatUtils.sum(values);
double max = StatUtils.max(values);
// 计算 75th 百分位数
double p75 = StatUtils.percentile(values, 75);
// 指定区间计算(例如仅计算数组索引 1 到 4 的子区间)
double subMean = StatUtils.mean(values, 1, 4);
System.out.println("均值: " + mean + " | 和: " + sum + " | 最大值: " + max + " | P75: " + p75);
System.out.println("子区间均值: " + subMean);
}
}
java
均值: 4.233333333333333 | 和: 25.400000000000002 | 最大值: 8.9 | P75: 6.275
子区间均值: 4.65
2.2.4、频数与频率统计 (Frequency)
Frequency 用于统计数据集中不同对象/数值出现的频数(Count)、频率(Percentage)以及累计百分比:
java
import org.apache.commons.math3.stat.Frequency;
public class FrequencyDemo {
public static void main(String[] args) {
Frequency f = new Frequency();
// 添加离散数据 (支持 String, Comparable, long, double 等)
f.addValue("A");
f.addValue("A");
f.addValue("B");
f.addValue("C");
f.addValue("A");
// 1. 获取特定元素的出现次数
System.out.println("'A' 出现的频数: " + f.getCount("A")); // 3
// 2. 获取百分比 (0.0 ~ 1.0)
System.out.println("'B' 出现的频率: " + f.getPct("B")); // 0.2 (20%)
// 3. 累计频率 (按照 Comparable 自然顺序排序,计算 <= 'B' 的累计占比)
System.out.println("<= 'B' 的累计频率: " + f.getCumPct("B")); // A(3) + B(1) = 4/5 = 0.8
}
}
java
'A' 出现的频数: 3
'B' 出现的频率: 0.2
<= 'B' 的累计频率: 0.8
2.2.5、一元线性回归 (SimpleRegression)
用于拟合最小二乘法线性方程 y = slope ⋅ x + intercept y = \text{slope} \cdot x + \text{intercept} y=slope⋅x+intercept,同样支持流式实时添加数据点:
java
import org.apache.commons.math3.stat.regression.SimpleRegression;
public class RegressionDemo {
public static void main(String[] args) {
// false 参数表示包含截距项 (intercept)
SimpleRegression regression = new SimpleRegression(true);
// 动态添加 (x, y) 样本点
regression.addData(1.0, 2.1);
regression.addData(2.0, 3.9);
regression.addData(3.0, 6.1);
regression.addData(4.0, 8.0);
regression.addData(5.0, 9.9);
// 1. 拟合参数输出
System.out.println("斜率 (Slope): " + regression.getSlope());
System.out.println("截距 (Intercept): " + regression.getIntercept());
// 2. 拟合优度 / 判定系数 R^2 (0~1 之间,越接近 1 拟合度越高)
System.out.println("判定系数 R^2: " + regression.getRSquare());
// 3. 基于拟合的线性方程预测新数据点
double predictedY = regression.predict(6.0); // 预测 x = 6.0 时的 y 值
System.out.println("预测 x=6.0 时的 y 值: " + predictedY);
}
}
java
斜率 (Slope): 1.97
截距 (Intercept): 0.08999999999999986
判定系数 R^2: 0.9992018537590114
预测 x=6.0 时的 y 值: 11.91
2.3、选型指南与最佳实践
- 选择 DescriptiveStatistics 还是 SummaryStatistics:
- 如果你需要计算 中位数(Median)、众数、P90/P99 百分位数,必须选择 DescriptiveStatistics(因为这些指标要求必须获取数据的全量分布或进行排序)。
- 如果仅需统计 均值、方差、标准差、极值、总和,且数据量较大,必须选择 SummaryStatistics,以节省堆内存开销。
- 滚动窗口(Sliding Window)统计:
- DescriptiveStatistics 提供了 setWindowSize(int windowSize) 方法。设置窗口大小(如 100)后,当添加第 101 个数据时,最老的数据会自动被踢出,非常适合计算滑动平均线(SMA)等实时指标。
- 并发安全提示:
- DescriptiveStatistics 和 SummaryStatistics 不是线程安全的。如果在多线程高并发环境下使用,可以通过 SynchronizedDescriptiveStatistics.copy() 或者使用 synchronized 关键字手动保护共享变量。
3、回归分析 (Regression)
在 commons-math3 中,回归分析(Regression Analysis) 模块提供了丰富且灵活的 API。无论是简单的一元线性回归、多元线性回归,还是带权重的回归,org.apache.commons.math3.stat.regression 包均能通过高效的算法(如最小二乘法 OLS、QR 分解)提供强大的支持。
3.1、核心回归类概览
根据变量数量和数据引入方式,核心 API 分为以下三类:
| 类名 | 模型类型 | 特点与适用场景 |
|---|---|---|
| SimpleRegression | 一元线性回归 ( y = β 0 + β 1 x y = \beta_0 + \beta_1 x y=β0+β1x) | 支持流式/增量添加数据,内存占用 O ( 1 ) O(1) O(1),适合计算单特征预测与相关性 |
| OLSMultipleLinearRegression | 普通最小二乘多元线性回归 ( y = X β + ε y = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\varepsilon} y=Xβ+ε) | 基于矩阵运算,需一次性传入完整数据集 X \mathbf{X} X 和 y y y,提供完整的统计检验数据 |
| GLSMultipleLinearRegression | 广义最小二乘多元线性回归 | 支持异方差或自相关误差项,通过传入协方差矩阵( Ω \mathbf{\Omega} Ω) 进行加权回归 |
3.2、使用示例
3.2.1、一元线性回归 (SimpleRegression)
SimpleRegression 最大的特点是支持动态/流式数据追加,且包含了丰富的统计指标(如斜率标准误、拟合优度 R 2 R^2 R2、显著性检验 p p p-value 等)。
java
import org.apache.commons.math3.stat.regression.SimpleRegression;
public class SimpleRegressionDemo {
public static void main(String[] args) {
// 1. 创建 SimpleRegression 实例
// 构造函数参数 true 表示模型包含截距项 β0 (默认就是 true)
SimpleRegression regression = new SimpleRegression(true);
// 2. 动态添加 (x, y) 样本点
regression.addData(1.0, 2.1);
regression.addData(2.0, 3.9);
regression.addData(3.0, 6.1);
regression.addData(4.0, 8.0);
regression.addData(5.0, 9.9);
// 也可以通过二维数组一次性批量添加
// double[][] data = {{1.0, 2.1}, {2.0, 3.9}, ...};
// regression.addData(data);
// 3. 输出模型参数:y = β0 + β1 * x
double intercept = regression.getIntercept(); // 截距 β0
double slope = regression.getSlope(); // 斜率 β1
System.out.println(String.format("拟合方程: y = %.4f + %.4f * x", intercept, slope));
// 4. 获取回归模型统计指标
System.out.println("样本量 N: " + regression.getN());
System.out.println("判定系数 R^2: " + regression.getRSquare()); // R^2 越接近 1 拟合度越高
System.out.println("斜率标准误: " + regression.getSlopeStdErr());
System.out.println("残差平方和 (SSE): " + regression.getSumSquaredErrors());
// 5. 预测新数据
double xNew = 6.0;
double yPredicted = regression.predict(xNew);
System.out.println(String.format("当 x = %.1f 时,预测 y = %.4f", xNew, yPredicted));
}
}
java
拟合方程: y = 0.0900 + 1.9700 * x
样本量 N: 5
判定系数 R^2: 0.9992018537590114
斜率标准误: 0.03214550253664257
残差平方和 (SSE): 0.030999999999998806
当 x = 6.0 时,预测 y = 11.9100
3.2.2、多元线性回归 (OLSMultipleLinearRegression)
当自变量有多个(即 y = β 0 + β 1 x 1 + β 2 x 2 + . . . + β k x k y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + ... + \beta_k x_k y=β0+β1x1+β2x2+...+βkxk)时,使用 OLSMultipleLinearRegression。该类内部通过 QR 分解(QR Decomposition) 求解参数,数值稳定性极强。
java
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;
public class OLSRegressionDemo {
public static void main(String[] args) {
// 1. 准备样本数据
// 因变量 y (长度为 N 的一维数组)
double[] y = new double[] { 11.0, 19.0, 29.0, 41.0, 50.0 };
// 自变量矩阵 X (N 行 K 列,此处 5 个样本,每个样本 2 个特征)
double[][] x = new double[][] {
{ 1.0, 2.0 },
{ 2.0, 3.0 },
{ 3.0, 5.0 },
{ 4.0, 7.0 },
{ 5.0, 8.0 }
};
// 2. 创建并配置 OLS 回归器
OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression();
// 默认会在 X 矩阵第一列自动补充全 1 列以计算截距项 β0。
// 如果设置为 false,则强行拟合过原点的模型:
// regression.setNoIntercept(true);
// 3. 加载数据(必须一次性传入完整数据)
regression.newSampleData(y, x);
// 4. 获取估计参数 β 向量 (索引 0 为截距 β0,后续为 β1, β2 ...)
double[] beta = regression.estimateRegressionParameters();
System.out.println("--- 参数估计值 ---");
System.out.println("截距 β0: " + beta[0]);
System.out.println("系数 β1: " + beta[1]);
System.out.println("系数 β2: " + beta[2]);
// 5. 获取详细统计评估数据
System.out.println("\n--- 模型统计评估 ---");
System.out.println("判定系数 R^2: " + regression.calculateRSquared());
System.out.println("调整后 R^2: " + regression.calculateAdjustedRSquared());
// 参数标准误 (Standard Errors)
double[] betaStdErrors = regression.estimateRegressionParametersStandardErrors();
System.out.println("β1 标准误: " + betaStdErrors[1]);
// 获取每个样本点的残差 (y - y_hat)
double[] residuals = regression.estimateResiduals();
System.out.println("第一个样本点的残差: " + residuals[0]);
}
}
java
--- 参数估计值 ---
截距 β0: -0.4999999999999833
系数 β1: 5.999999999999995
系数 β2: 2.5000000000000013
--- 模型统计评估 ---
判定系数 R^2: 0.9985059760956175
调整后 R^2: 0.9970119521912351
β1 标准误: 2.2079402165819673
第一个样本点的残差: 0.4999999999999858
3.2.3、广义最小二乘多元回归 (GLSMultipleLinearRegression)
当数据存在异方差(Heteroscedasticity) 或 自相关(Autocorrelation) 时,传统的 OLS 会失效。GLS 允许你通过传入一个 N × N N \times N N×N 的 协方差矩阵( Ω \mathbf{\Omega} Ω) 对不同的样本赋予不同的权重。
java
import org.apache.commons.math3.stat.regression.GLSMultipleLinearRegression;
public class GLSRegressionDemo {
public static void main(String[] args) {
double[] y = new double[] { 1.1, 1.9, 3.2, 3.8 };
// 4 个样本,每个样本 1 个自变量
double[][] x = new double[][] {
{ 1.0 },
{ 2.0 },
{ 3.0 },
{ 4.0 }
};
// 定义 4x4 的加权协方差矩阵 Omega (对角阵表示加权最小二乘 WLS)
// 对角线上的元素代表各个样本的方差大小
double[][] omega = new double[][] {
{ 1.0, 0.0, 0.0, 0.0 },
{ 0.0, 1.5, 0.0, 0.0 },
{ 0.0, 0.0, 2.0, 0.0 },
{ 0.0, 0.0, 0.0, 2.5 }
};
GLSMultipleLinearRegression gls = new GLSMultipleLinearRegression();
// 传入 y, x 及协方差矩阵 omega
gls.newSampleData(y, x, omega);
double[] beta = gls.estimateRegressionParameters();
System.out.println("GLS 截距 β0: " + beta[0]);
System.out.println("GLS 系数 β1: " + beta[1]);
}
}
java
GLS 截距 β0: 0.13559322033898358
GLS 系数 β1: 0.945762711864407
3.3、避坑指南与最佳实践
- 截距项(Intercept)处理:
- OLSMultipleLinearRegression 默认会自动在自变量矩阵最左侧添加全 1 列来拟合截距 β 0 \beta_0 β0。因此切勿在你的传入矩阵 x 中手动再加全 1 列,否则会导致特征多重共线性(Rank Deficient)而求解失败。
- 如果模型确实不需要截距,显式调用 regression.setNoIntercept(true)。
- 多重共线性与矩阵奇异:
- 如果自变量矩阵 X \mathbf{X} X 存在严重的线性相关(如某特征是另一特征的常数倍),会导致逆矩阵不存在。OLSMultipleLinearRegression 在底层求解时会抛出 SingularMatrixException。
- 数据量限制:
- 样本量 N N N 必须严格大于特征数量 K K K(即 N > K + 1 N > K + 1 N>K+1),否则模型自由度不足,无法计算残差方差及相关显著性指标。
4、线性代数 (Linear)
在 commons-math3 中,线性代数(Linear Algebra) 模块提供了极具实用价值且数值稳定的 API。该模块的核心集中在 org.apache.commons.math3.linear 包下,覆盖了矩阵/向量操作、线性方程组求解、矩阵分解(LU、QR、Cholesky、SVD) 等标准代数运算。
4.1、核心 API 与类体系
4.1.1、向量(Vector)与矩阵(Matrix)
| 接口 / 实现类 | 描述与特性 |
|---|---|
| RealVector / ArrayRealVector | 实数向量接口及其常用实现类(基于 double\[\]) |
| RealMatrix | 实数矩阵核心接口,定义了加减乘、转置、求逆、迹等基础运算 |
| Array2DRowRealMatrix | 最常用的稠密矩阵,底层为二维数组 double\[\]\[\],适合中小规模计算 |
| BlockRealMatrix | 分块稠密矩阵,将大矩阵切分为 52 × 52 52 \times 52 52×52 的小块存储,对大矩阵乘法有极佳的 CPU 缓存优化 |
| OpenMapRealMatrix | 稀疏矩阵,基于哈希表存储非零元素,适合维度极大但非零值极少的场景 |
4.1.2、矩阵分解与求解器(Decomposition & Solver)
对于求逆或求解形如 A x = b \mathbf{A}\mathbf{x} = \mathbf{b} Ax=b 的方程组,commons-math3 不推荐直接对 A \mathbf{A} A 求逆(数值不稳定性高),而是推荐使用矩阵分解求解器:
| 分解类 | 适用条件 | 特点 |
|---|---|---|
| LUDecomposition | 方阵( n × n n \times n n×n) | 通用且高效,适合常规方阵求解和行列式计算 |
| QRDecomposition | 任意形状矩阵( m × n m \times n m×n) | 极佳的数值稳定性,常用于超定方程组的最小二乘求解 |
| CholeskyDecomposition | 对称正定矩阵 | 速度是 LU 分解的 2 倍,数值稳定性极高 |
| SingularValueDecomposition (SVD) | 任意矩阵(含奇异/退化矩阵) | 奇异值分解,可计算伪逆(Moore-Penrose Inverse) |
| EigenDecomposition | 实对称方阵 | 计算特征值(Eigenvalues)与特征向量(Eigenvectors) |
4.2、使用示例
4.2.1、1. 基础矩阵与向量运算
包含矩阵的创建、加减乘、转置以及元素标量缩放:
java
import org.apache.commons.math3.linear.*;
public class MatrixBasicDemo {
public static void main(String[] args) {
// 1. 创建矩阵 A (2x3) 与 B (3x2)
double[][] rawA = {
{ 1.0, 2.0, 3.0 },
{ 4.0, 5.0, 6.0 }
};
RealMatrix matrixA = new Array2DRowRealMatrix(rawA);
double[][] rawB = {
{ 7.0, 8.0 },
{ 9.0, 1.0 },
{ 2.0, 3.0 }
};
RealMatrix matrixB = new Array2DRowRealMatrix(rawB);
// 2. 矩阵乘法: C = A * B (结果为 2x2 矩阵)
RealMatrix matrixC = matrixA.multiply(matrixB);
System.out.println("--- A * B 乘积矩阵 C ---");
printMatrix(matrixC);
// 3. 矩阵转置与标量缩放: C^T * 2.0
RealMatrix cTransposedScaled = matrixC.transpose().scalarMultiply(2.0);
System.out.println("--- (C^T) * 2.0 ---");
printMatrix(cTransposedScaled);
// 4. 向量与矩阵点乘
RealVector vectorX = new ArrayRealVector(new double[]{ 1.0, 2.0, 3.0 });
RealVector resultVector = matrixA.operate(vectorX); // A * x
System.out.println("--- A * x 向量结果: " + resultVector);
}
private static void printMatrix(RealMatrix matrix) {
for (int r = 0; r < matrix.getRowDimension(); r++) {
System.out.println(java.util.Arrays.toString(matrix.getRow(r)));
}
}
}
java
--- A * B 乘积矩阵 C ---
[31.0, 19.0]
[85.0, 55.0]
--- (C^T) * 2.0 ---
[62.0, 170.0]
[38.0, 110.0]
--- A * x 向量结果: {14; 32}
4.2.2、求解线性方程组 A x = b \mathbf{A}\mathbf{x} = \mathbf{b} Ax=b 与矩阵求逆
求解方程组:
{ 2 x + 1 y + 1 z = 8 1 x + 3 y + 2 z = 13 1 x + 0 y + 0 z = 1 \begin{cases} 2x + 1y + 1z = 8 \\ 1x + 3y + 2z = 13 \\ 1x + 0y + 0z = 1 \end{cases} ⎩ ⎨ ⎧2x+1y+1z=81x+3y+2z=131x+0y+0z=1
java
import org.apache.commons.math3.linear.*;
public class LinearSolverDemo {
public static void main(String[] args) {
// 系数矩阵 A
double[][] coefficients = {
{ 2.0, 1.0, 1.0 },
{ 1.0, 3.0, 2.0 },
{ 1.0, 0.0, 0.0 }
};
RealMatrix matrixA = new Array2DRowRealMatrix(coefficients);
// 常数向量 b
RealVector vectorB = new ArrayRealVector(new double[]{ 8.0, 13.0, 1.0 });
// 1. 使用 LU 分解创建求解器
LUDecomposition lu = new LUDecomposition(matrixA);
DecompositionSolver solver = lu.getSolver();
// 2. 求解 Ax = b -> x
RealVector vectorX = solver.solve(vectorB);
System.out.println("--- 方程组解向量 x ---");
System.out.println("x = " + vectorX.getEntry(0)); // 1.0
System.out.println("y = " + vectorX.getEntry(1)); // 3.0
System.out.println("z = " + vectorX.getEntry(2)); // 3.0
// 3. 计算行列式 det(A)
double det = lu.getDeterminant();
System.out.println("行列式 det(A): " + det);
// 4. 求逆矩阵 A^-1
if (solver.isNonSingular()) { // 确保非奇异(可逆)
RealMatrix inverseA = solver.getInverse();
System.out.println("--- 逆矩阵 A^-1 第一行: " + java.util.Arrays.toString(inverseA.getRow(0)));
}
}
}
java
--- 方程组解向量 x ---
x = 0.9999999999999999
y = -7.105427357601002E-16
z = 6.000000000000001
行列式 det(A): -0.9999999999999998
--- 逆矩阵 A^-1 第一行: [0.0, 0.0, 1.0]
4.2.3、特征值与特征向量分解 (EigenDecomposition)
适用于主成分分析(PCA)或谱分析场景(要求矩阵为对称方阵):
java
import org.apache.commons.math3.linear.*;
public class EigenDemo {
public static void main(String[] args) {
// 对称矩阵 A
double[][] symmetricData = {
{ 4.0, 1.0, -2.0 },
{ 1.0, 2.0, 0.0 },
{ -2.0, 0.0, 3.0 }
};
RealMatrix matrix = new Array2DRowRealMatrix(symmetricData);
// 1. 特征分解
EigenDecomposition eigen = new EigenDecomposition(matrix);
// 2. 获取特征值数组
double[] realEigenvalues = eigen.getRealEigenvalues();
System.out.println("特征值 (Real Eigenvalues): " + java.util.Arrays.toString(realEigenvalues));
// 3. 获取第一个特征值对应的特征向量
RealVector firstEigenvector = eigen.getEigenvector(0);
System.out.println("第一个特征向量: " + firstEigenvector);
// 4. 获取特征值矩阵 D 与特征向量矩阵 V (满足 A = V * D * V^T)
RealMatrix D = eigen.getD();
RealMatrix V = eigen.getV();
System.out.println("主对角特征值矩阵 D 的阶数: " + D.getRowDimension());
}
}
java
特征值 (Real Eigenvalues): [5.732050807568878, 2.267949192431124, 1.0]
第一个特征向量: {0.7886751346; 0.2113248654; -0.5773502692}
主对角特征值矩阵 D 的阶数: 3
4.2.4、奇异值分解 (SingularValueDecomposition - SVD)
SVD 可对任意 m × n m \times n m×n 矩阵分解为 A = U Σ V T \mathbf{A} = \mathbf{U} \mathbf{\Sigma} \mathbf{V}^T A=UΣVT,常用于伪逆计算与降维:
java
import org.apache.commons.math3.linear.*;
public class SvdDemo {
public static void main(String[] args) {
// 任意非正方形矩阵 (3x2)
double[][] data = {
{ 1.0, 2.0 },
{ 3.0, 4.0 },
{ 5.0, 6.0 }
};
RealMatrix matrix = new Array2DRowRealMatrix(data);
// 执行 SVD
SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
// 获取奇异值
double[] singularValues = svd.getSingularValues();
System.out.println("奇异值数组: " + java.util.Arrays.toString(singularValues));
// 计算伪逆矩阵 (Moore-Penrose Pseudo-Inverse)
RealMatrix pseudoInverse = svd.getSolver().getInverse();
System.out.println("伪逆矩阵维度: " + pseudoInverse.getRowDimension() + "x" + pseudoInverse.getColumnDimension());
}
}
java
奇异值数组: [9.525518091565106, 0.5143005806586448]
伪逆矩阵维度: 2x3
4.3、性能与避坑指南
- 选择正确的矩阵实现:
- 尺寸 < 100 × 100 < 100 \times 100 <100×100:优先使用 Array2DRowRealMatrix。
- 尺寸 > 500 × 500 > 500 \times 500 >500×500(高维稠密):推荐使用 BlockRealMatrix,它通过切块极大提升 CPU L1/L2 缓存命中率。
- 高维稀疏矩阵(非零元素 < 5 % < 5\% <5%):务必使用 OpenMapRealMatrix,避免 OOM。
- 避免直接显式调用 inverse() 求逆:
- 求逆矩阵 A − 1 \mathbf{A}^{-1} A−1 既消耗 CPU 且容易产生严重的浮点误差。如果目的是求解 A x = b \mathbf{A}\mathbf{x} = \mathbf{b} Ax=b,应使用 DecompositionSolver.solve(b),性能更好且数值更稳定。
- 不可变性(Immutability)与内存:
- RealMatrix 的加减乘等方法(如 a.add(b)、a.multiply(b))均会产生并返回一个新的矩阵对象。在频繁循环迭代中,应注意垃圾回收(GC)压力。