Guava——数学工具

1、标量数值计算工具类

包含 IntMath、LongMath、BigIntegerMath 和 DoubleMath,重点解决数值溢出、精确舍入和数论计算。

1.1、核心API

类名特有/核心 API说明
IntMath / LongMathcheckedAdd / checkedMultiply溢出检查:发生溢出时直接抛出 ArithmeticException
saturatingAdd / saturatingMultiply饱和算术:溢出时返回 MAX_VALUEMIN_VALUE,不抛异常
divide(a, b, RoundingMode)指定舍入模式的整数除法,避免转换为 double 导致的精度丢失
log2 / log10 / sqrt结合 RoundingMode 计算对数和开平方根
factorial(n) / binomial(n, k)阶乘与组合数 C n k C_n^k Cnk 计算(自动内部优化)
gcd(a, b) / mod(x, m)计算最大公约数;mod 保证返回非负结果(区别于 % 运算符)
BigIntegerMathdivide, log2, log10, sqrt, factorial针对大整数的对数、平方根与阶乘,支持 RoundingMode
DoubleMathisMathematicalInteger(x)判断 double 值是否为无小数部分的数学整数(如 3.0
roundToInt / roundToLong安全地将 double 按指定舍入模式转为整数

1.2、使用示例

import com.google.common.math.BigIntegerMath;
import com.google.common.math.IntMath;
import com.google.common.math.DoubleMath;
import java.math.BigInteger;
import java.math.RoundingMode;

public class ScalarMathExample {
    public static void main(String[] args) {
        // 饱和加法:Integer.MAX_VALUE + 100 -> Integer.MAX_VALUE
        int saturated = IntMath.saturatingAdd(Integer.MAX_VALUE, 100);
        
        // 整数除法向上取整:10 / 4 -> 3
        int divCeil = IntMath.divide(10, 4, RoundingMode.CEILING);
        
        // 大整数开平方根向下取整
        BigInteger bigNum = new BigInteger("1000000000000000000000000");
        BigInteger sqrtVal = BigIntegerMath.sqrt(bigNum, RoundingMode.FLOOR);
        
        // 双精度转整数(四舍五入)
        int roundedInt = DoubleMath.roundToInt(3.5, RoundingMode.HALF_UP); // 4
    }
}

2、单变量统计分析(Stats & StatsAccumulator)

用于计算数据的均值、方差、标准差和极值。提供不可变的 Stats 和可变的流式累加器 StatsAccumulator。

2.1、核心API

方法名返回值 / 说明
count() / sum()数据总量 / 总和
mean()算术平均值
min() / max()最小值 / 最大值
populationVariance() / sampleVariance()总体方差 / 样本方差
populationStandardDeviation()总体标准差 σ \sigma σ
sampleStandardDeviation()样本标准差 s s s

2.2、使用示例

import com.google.common.math.Stats;
import com.google.common.math.StatsAccumulator;

public class StatsExample {
    public static void main(String[] args) {
        // 1. 静态一次性计算
        Stats stats = Stats.of(1.0, 2.0, 3.0, 4.0, 5.0);
        System.out.println("均值: " + stats.mean() + ", 标准差: " + stats.populationStandardDeviation());

        // 2. 流式动态累加 (适合处理数据流)
        StatsAccumulator accumulator = new StatsAccumulator();
        accumulator.add(10.5);
        accumulator.addAll(20.1, 30.4);
        
        // 导出不可变的 Stats 统计快照
        Stats snapshot = accumulator.snapshot();
        System.out.println("数据量: " + snapshot.count() + ", 最小值: " + snapshot.min());
    }
}
均值: 3.0, 标准差: 1.4142135623730951
数据量: 3, 最小值: 10.5

3、双变量统计与回归(PairedStats & PairedStatsAccumulator)

用于分析两组成对数据 ( x , y ) (x, y) (x,y) 之间的相关性、协方差及最小二乘法拟合直线。

3.1、核心API

方法名说明
populationCovariance() / sampleCovariance()计算总体/样本协方差
pearsonsCorrelationCoefficient()计算皮尔逊相关系数 r ∈ [ − 1 , 1 ] r \in [-1, 1] r[1,1]
leastSquaresFit()计算拟合直线,返回 LinearTransformation 对象
xStats() / yStats()提取 x x x 轴或 y y y 轴独立的 Stats 对象

3.2、使用示例

import com.google.common.math.LinearTransformation;
import com.google.common.math.PairedStats;
import com.google.common.math.PairedStatsAccumulator;

public class PairedStatsDemo {
    public static void main(String[] args) {
        // 1. 创建累加器并添加数据点 (x, y)
        PairedStatsAccumulator accumulator = new PairedStatsAccumulator();
        
        accumulator.add(1.0, 2.1);
        accumulator.add(2.0, 3.9);
        accumulator.add(3.0, 6.1);
        accumulator.add(4.0, 8.0);
        accumulator.add(5.0, 9.8);

        // 2. 计算统计指标
        System.out.println("数据点数量: " + accumulator.count()); // 5
        System.out.println("X 的均值: " + accumulator.xStats().mean()); // 3.0
        System.out.println("样本协方差: " + accumulator.sampleCovariance());
        System.out.println("皮尔逊相关系数: " + accumulator.pearsonsCorrelationCoefficient());

        // 3. 线性回归拟合 (y = slope * x + intercept)
        LinearTransformation fit = accumulator.leastSquaresFit();
        System.out.println("斜率 (Slope): " + fit.slope());
        System.out.println("Y轴截距 (Intercept): " + fit.transform(0.0)); // x=0 时求 y

        // 4. 使用拟合直线进行预测
        double predictedY = fit.transform(6.0); // 预测 x = 6 时 y 的值
        System.out.println("预测 x=6 时的 y 值: " + predictedY);

        // 5. 生成不可变快照
        PairedStats snapshot = accumulator.snapshot();
        // snapshot 可以在多线程间安全共享或返回给调用方
    }
}

4、线性变换(LinearTransformation)

Guava(com.google.common.math)中的 LinearTransformation 表示一个一元线性变换(或二维平面上的直线),形式通常为 y = m ⋅ x + b y = m \cdot x + b y=mx+b 或垂直线 x = c x = c x=c

它主要配合 PairedStats / PairedStatsAccumulator 的 leastSquaresFit()(最小二乘法回归)使用,也可以独立用来构建和执行线性映射(如单位转换、温度转换等)。

4.1、核心API

LinearTransformation 是一个抽象类,提供了多个静态工厂方法来创建不同类型的线性变换,并提供了用于变换和查询属性的实例方法。

1. 创建变换(工厂方法)

方法形式/含义说明
forPointAndSlope(double x1, double y1, double slope)点斜式 y − y 1 = m ( x − x 1 ) y - y_1 = m(x - x_1) yy1=m(xx1):给定直线上一点及斜率
mapping(double x1, double y1, double x2, double y2)两点式:给定直线上不同的两个点
vertical(double x)垂直线 x = c x = c x=c:斜率为无穷大,适用于所有点 x x x 坐标相同的情况
horizontal(double y)水平线 y = c y = c y=c:斜率为 0
identity()恒等变换 y = x y = x y=x:斜率为 1,截距为 0

2. 变换计算(实例方法)

方法返回值说明
transform(double x)double根据 x x x 计算对应的 y y y 值(垂直线调用会抛出异常)
inverse()LinearTransformation返回反函数/逆变换(如原函数为 y = 2 x y=2x y=2x,逆函数为 x = 0.5 y x=0.5y x=0.5y;水平线求逆会变为垂直线)

3. 属性查询(实例方法)

方法返回值说明
isVertical()boolean是否为垂直线
isHorizontal()boolean是否为水平线
slope()double获取斜率 m m m(若为垂直线会抛出 IllegalStateException

4.2、使用示例

示例 1:温度单位转换(摄氏度 ↔ 华氏度)

线性变换非常适合处理公式转换,例如: F = 1.8 ⋅ C + 32 F = 1.8 \cdot C + 32 F=1.8C+32

import com.google.common.math.LinearTransformation;

public class TemperatureConverter {
    public static void main(String[] args) {
        // 1. 通过已知两点构建转换关系:
        // 冰点 (0°C, 32°F),沸点 (100°C, 212°F)
        LinearTransformation celsiusToFahrenheit = LinearTransformation.mapping(0, 32).and(100, 212);

        // 2. 摄氏度转华氏度 (y = 1.8 * x + 32)
        double c = 25.0;
        double f = celsiusToFahrenheit.transform(c);
        System.out.println(c + " °C = " + f + " °F"); // 25.0 °C = 77.0 °F
        System.out.println("斜率 (Slope): " + celsiusToFahrenheit.slope()); // 1.8

        // 3. 获取逆变换(华氏度转摄氏度)
        LinearTransformation fahrenheitToCelsius = celsiusToFahrenheit.inverse();
        double backToC = fahrenheitToCelsius.transform(77.0);
        System.out.println("77.0 °F = " + backToC + " °C"); // 77.0 °F = 25.0 °C
    }
}
25.0 °C = 77.0 °F
斜率 (Slope): 1.8
77.0 °F = 25.0 °C

示例 2:结合回归分析预测数据

结合 PairedStatsAccumulator 拟合线性模型并进行数据预测:

import com.google.common.math.LinearTransformation;
import com.google.common.math.PairedStatsAccumulator;

public class RegressionPredictionDemo {
    public static void main(String[] args) {
        PairedStatsAccumulator stats = new PairedStatsAccumulator();
        
        // 添加样本数据 (工作年限 x, 年薪/万元 y)
        stats.add(1.0, 5.0);
        stats.add(2.0, 6.2);
        stats.add(3.0, 7.8);
        stats.add(4.0, 9.1);

        // 最小二乘法拟合直线
        LinearTransformation fit = stats.leastSquaresFit();

        if (fit.isVertical()) {
            System.out.println("数据点分布在同一垂直线上,无法进行常规线性回归!");
        } else {
            System.out.printf("拟合回归方程: y = %.2fx + %.2f%n", fit.slope(), fit.transform(0));

            // 预测工作 6 年时的薪资
            double years = 6.0;
            double predictedSalary = fit.transform(years);
            System.out.printf("预测工作 %.1f 年后的薪资为: %.2f 万元%n", years, predictedSalary);
        }
    }
}

5、分位数计算(Quantiles)

Guava(com.google.common.math 包)中的 Quantiles 是一个专门用于高效计算数据流或数组中分位数(如中位数 Median、四分位数 Quartiles、百分位数 Percentiles 等)的工具类。

它采用了选择算法(基于 Quickselect 的算法变体),可以在 O ( N ) O(N) O(N) 平均时间复杂度内直接在输入数组上完成计算,无需对整个数据集进行完整的 O ( N log ⁡ N ) O(N \log N) O(NlogN) 排序。

5.1、核心API

Quantiles 采用了 Fluent API(流式接口) 结构,按照以下三步来完成计算:

  • 指定分位数切分粒度:例如 median()(中位数/二分)、quartiles()(四分位/4分)、percentiles()(百分位/100分)、indexes(k)(自定义 k k k 等分)。
  • 选择要计算的具体位置:如四分位数中的第 1 和第 3 个切点(即 Q1 和 Q3)。
  • 传入数据源计算:支持 double[]、long[]、int[] 数组或 Collection<? extends Number> 集合。

5.2、使用示例

1. 计算单个分位数(中位数、百分位数等)

使用 index() 指定计算某一个具体的分位数,返回单个 double 值。

import com.google.common.math.Quantiles;
import java.util.Arrays;
import java.util.List;

public class QuantilesSingleDemo {
    public static void main(String[] args) {
        List<Double> data = Arrays.asList(6.0, 1.0, 2.0, 3.0, 4.0, 5.0, 7.0, 8.0, 9.0);

        // 1. 计算中位数 (Median)
        double median = Quantiles.median().compute(data);
        System.out.println("中位数: " + median); // 5.0

        // 2. 计算第 90 百分位数 (P90)
        double p90 = Quantiles.percentiles().index(90).compute(data);
        System.out.println("P90: " + p90); // 8.2

        // 3. 自定义切分:将数据切分为 5 等份 (Quintiles),计算第 3 个切点 (60%)
        double quintile3 = Quantiles.scale(5).index(3).compute(data);
        System.out.println("第3个五分位数: " + quintile3); // 5.8
    }
}
中位数: 5.0
P90: 8.23个五分位数: 5.8

2. 批量计算多个分位数

如果需要同时求多个分位数(例如箱线图所需的 Q1, Q2, Q3),使用 indexes() 一次性计算效率更高(算法会复用已划好的分区)。返回结果为 Map<Integer, Double>,Key 为对应切点的索引号。

import com.google.common.math.Quantiles;
import java.util.Map;

public class QuantilesBatchDemo {
    public static void main(String[] args) {
        double[] data = {12.0, 3.0, 5.0, 7.0, 11.0, 13.0, 17.0, 19.0, 23.0, 29.0};

        // 1. 批量计算四分位数 (Q1=第1切点, Q2=第2切点/中位数, Q3=第3切点)
        Map<Integer, Double> quartiles = Quantiles.quartiles()
                .indexes(1, 2, 3)
                .compute(data);

        System.out.println("Q1 (25%): " + quartiles.get(1));
        System.out.println("Q2 (50%): " + quartiles.get(2));
        System.out.println("Q3 (75%): " + quartiles.get(3));

        // 2. 批量获取 P50, P90, P99(常用于系统延迟指标耗时分析)
        Map<Integer, Double> latencies = Quantiles.percentiles()
                .indexes(50, 90, 99)
                .compute(data);

        System.out.println("P50: " + latencies.get(50));
        System.out.println("P90: " + latencies.get(90));
        System.out.println("P99: " + latencies.get(99));
    }
}
Q1 (25%): 8.0
Q2 (50%): 12.5
Q3 (75%): 18.5
P50: 12.5
P90: 23.6
P99: 28.46

5.3、主要切分粒度(Scale)方法对比

方法切份数 (k 等分)索引范围常见用途
median()2固定的切点 1中位数
quartiles()41, 2, 3箱线图分析 (Q1, Q2, Q3)
percentiles()1001 到 99系统 P90 / P95 / P99 延迟响应指标
scale(k) k k k1 到 k − 1 k-1 k1任意 k k k 等分计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值