SpectraRust/src/math/interpolate.rs
2026-03-19 14:05:33 +08:00

154 lines
4.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 插值函数。
//!
//! 重构自 TLUSTY `yint.f` 和 `lagran.f`
/// 使用 3 点进行二次插值。
///
/// 给定 3 个 x 值和 3 个 y 值的数组,插值求 `xl0` 处的 y 值。
///
/// # 参数
///
/// * `xl` - 3 个 x 坐标的数组
/// * `yl` - 3 个 y 坐标的数组f(x) 值)
/// * `xl0` - 要插值到的 x 值
///
/// # 返回值
///
/// `xl0` 处的插值 y 值。
///
/// # Panics
///
/// 如果输入数组不正好有 3 个元素则 panic。
///
/// # 示例
///
/// ```
/// use tlusty_rust::math::yint;
///
/// let xl = [0.0, 1.0, 2.0];
/// let yl = [0.0, 1.0, 4.0]; // f(x) = x^2
/// let result = yint(&xl, &yl, 0.5);
/// assert!((result - 0.25).abs() < 1e-10);
/// ```
pub fn yint(xl: &[f64], yl: &[f64], xl0: f64) -> f64 {
assert!(xl.len() == 3 && yl.len() == 3, "yint 需要大小为 3 的数组");
// Fortran 使用 1 索引数组: XL(1), XL(2), XL(3)
let x1 = xl[0];
let x2 = xl[1];
let x3 = xl[2];
let y1 = yl[0];
let y2 = yl[1];
let y3 = yl[2];
// A0 = (x2-x1)*(x3-x2)*(x3-x1)
let a0 = (x2 - x1) * (x3 - x2) * (x3 - x1);
// A1 = (xl0-x2)*(xl0-x3)*(x3-x2)
let a1 = (xl0 - x2) * (xl0 - x3) * (x3 - x2);
// A2 = (xl0-x1)*(x3-xl0)*(x3-x1)
let a2 = (xl0 - x1) * (x3 - xl0) * (x3 - x1);
// A3 = (xl0-x1)*(xl0-x2)*(x2-x1)
let a3 = (xl0 - x1) * (xl0 - x2) * (x2 - x1);
(y1 * a1 + y2 * a2 + y3 * a3) / a0
}
/// 三点 Lagrange 插值。
///
/// # 参数
///
/// * `x0`, `x1`, `x2` - 三个点的 x 坐标
/// * `y0`, `y1`, `y2` - 三个点的 y 坐标
/// * `x` - 要插值到的 x 值
///
/// # 返回值
///
/// `x` 处的插值 y 值。
///
/// # 示例
///
/// ```
/// use tlusty_rust::math::lagran;
///
/// // 使用点 (0,0), (1,1), (2,4) 插值 f(x) = x^2
/// let result = lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 0.5);
/// assert!((result - 0.25).abs() < 1e-10);
/// ```
pub fn lagran(x0: f64, x1: f64, x2: f64, y0: f64, y1: f64, y2: f64, x: f64) -> f64 {
// Lagrange 基多项式
let xl0 = (x - x1) * (x - x2) / (x0 - x1) / (x0 - x2);
let xl1 = (x - x0) * (x - x2) / (x1 - x0) / (x1 - x2);
let xl2 = (x - x0) * (x - x1) / (x2 - x0) / (x2 - x1);
y0 * xl0 + y1 * xl1 + y2 * xl2
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_yint_quadratic() {
// 测试 f(x) = x^2
let xl = [0.0, 1.0, 2.0];
let yl = [0.0, 1.0, 4.0];
// 中点
let result = yint(&xl, &yl, 0.5);
assert_relative_eq!(result, 0.25, epsilon = 1e-10);
// 另一点
let result = yint(&xl, &yl, 1.5);
assert_relative_eq!(result, 2.25, epsilon = 1e-10);
// 已知点(应返回精确值)
let result = yint(&xl, &yl, 1.0);
assert_relative_eq!(result, 1.0, epsilon = 1e-10);
}
#[test]
fn test_yint_linear() {
// 测试 f(x) = 2x + 1
let xl = [0.0, 1.0, 2.0];
let yl = [1.0, 3.0, 5.0];
let result = yint(&xl, &yl, 0.5);
assert_relative_eq!(result, 2.0, epsilon = 1e-10);
}
#[test]
fn test_lagran_quadratic() {
// 测试 f(x) = x^2
let result = lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 0.5);
assert_relative_eq!(result, 0.25, epsilon = 1e-10);
let result = lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 1.5);
assert_relative_eq!(result, 2.25, epsilon = 1e-10);
}
#[test]
fn test_lagran_at_known_points() {
// 在已知点应返回精确值
assert_relative_eq!(lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 0.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 1.0), 1.0, epsilon = 1e-10);
assert_relative_eq!(lagran(0.0, 1.0, 2.0, 0.0, 1.0, 4.0, 2.0), 4.0, epsilon = 1e-10);
}
#[test]
fn test_yint_lagran_equivalence() {
// yint 和 lagran 对相同数据应给出相同结果
let xl = [1.0, 2.0, 3.0];
let yl = [1.0, 8.0, 27.0]; // f(x) = x^3
let x = 2.5;
let yint_result = yint(&xl, &yl, x);
let lagran_result = lagran(xl[0], xl[1], xl[2], yl[0], yl[1], yl[2], x);
assert_relative_eq!(yint_result, lagran_result, epsilon = 1e-10);
}
}