154 lines
4.1 KiB
Rust
154 lines
4.1 KiB
Rust
//! 插值函数。
|
||
//!
|
||
//! 重构自 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);
|
||
}
|
||
}
|