ApacheCommons——commons-math3(科学计算与线性代数)(一)

commons-math3(科学计算与线性代数)

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)压力。
相关推荐
QCodingDev1 小时前
Spring AI 2.0企业级RAG实战:引用校验、无依据拒答与知识治理怎么做?
java·人工智能·spring·ai
tachibana21 小时前
Embedding 有哪几种算法?
人工智能·算法·ai·大模型·llm·embedding·agent
砚底藏山河1 小时前
行情工程实战 M01|存储选型 CSVSQLiteMySQL
java·python·金融·maven
名字还没想好☜1 小时前
Java 用 LinkedHashMap 三行实现 LRU 缓存:accessOrder、removeEldestEntry 与线程安全
java·后端·安全·spring·缓存
谢亮_vipxieliang1 小时前
ValidX vs Google Guava Preconditions:验证 vs 断言
java·spring boot·后端·spring cloud·hibernate·guava
吴声子夜歌1 小时前
ApacheCommons——commons-configuration2(多数据源配置统一管理与热加载)
java·开发语言·算法·apache
kyle~2 小时前
奇异值分解(SVD)
人工智能·算法·机器学习
HugoStudio_SWAN2 小时前
洛谷 B4502 [GESP202603 四级] 礼盒排序——多关键字排序与排名计数法
算法
星星落进兜里2 小时前
Java虚拟机面试题-补充
java·开发语言