SpectraRust/src/math/interp.rs
2026-03-21 09:12:18 +08:00

182 lines
4.4 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.

//! 通用插值过程
//!
//! 原始 Fortran: interp.f
//! 支持 (NPOL-1) 阶插值,可选对数插值
use crate::state::MDEPTH;
/// 通用插值过程
///
/// 对给定点进行 (NPOL-1) 阶拉格朗日插值
///
/// # 参数
/// - `x`: 原始 x 坐标数组 (可能被修改)
/// - `y`: 原始 y 值数组 (可能被修改)
/// - `xx`: 新 x 坐标数组 (可能被修改)
/// - `yy`: 输出的插值结果
/// - `nx`: 原始数组大小
/// - `nxx`: 新数组大小
/// - `npol`: 插值阶数+1
/// - `ilogx`: 1 表示对 x 取对数插值
/// - `ilogy`: 1 表示对 y 取对数插值
///
/// # 说明
/// - 当 NPOL <= 0 或 NX <= 0 时,直接复制数据
/// - 对数插值会修改输入数组
pub fn interp(
x: &mut [f64],
y: &mut [f64],
xx: &mut [f64],
yy: &mut [f64],
nx: usize,
nxx: usize,
npol: i32,
ilogx: i32,
ilogy: i32,
) {
// 常量
const LN10: f64 = 2.30258509299405;
// 无效情况: 直接复制
if npol <= 0 || nx == 0 {
let n = if nxx >= nx { nxx } else { nx };
for i in 0..n.min(MDEPTH) {
if i < xx.len() && i < x.len() {
xx[i] = x[i];
}
if i < yy.len() && i < y.len() {
yy[i] = y[i];
}
}
return;
}
// 对数插值预处理
if ilogx > 0 {
for i in 0..nx.min(x.len()) {
x[i] = x[i].ln() / LN10; // log10
}
for i in 0..nxx.min(xx.len()) {
xx[i] = xx[i].ln() / LN10; // log10
}
}
if ilogy > 0 {
for i in 0..nx.min(y.len()) {
y[i] = y[i].ln() / LN10; // log10
}
}
// 插值参数
let nm = (npol + 1) as usize / 2;
let nm1 = nm + 1;
let nup = nx + nm1 - npol as usize;
// 对每个目标点进行插值
for id in 0..nxx {
let xxx = xx[id];
// 找到插值区间
let mut i = nm1;
while i <= nup && xxx > x[i - 1] {
i += 1;
}
if i > nup {
i = nup;
}
let j = i - nm;
let jj = j + npol as usize - 1;
// 拉格朗日插值
let mut yyy = 0.0;
for k in j..=jj {
let mut t = 1.0;
for m in j..=jj {
if k != m {
t *= (xxx - x[m - 1]) / (x[k - 1] - x[m - 1]);
}
}
yyy += y[k - 1] * t;
}
yy[id] = yyy;
}
// 对数插值后处理: 恢复原值
if ilogx > 0 {
for i in 0..nx.min(x.len()) {
x[i] = (x[i] * LN10).exp();
}
for i in 0..nxx.min(xx.len()) {
xx[i] = (xx[i] * LN10).exp();
}
}
if ilogy > 0 {
for i in 0..nx.min(y.len()) {
y[i] = (y[i] * LN10).exp();
}
for i in 0..nxx.min(yy.len()) {
yy[i] = (yy[i] * LN10).exp();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interp_linear() {
// 线性函数 y = 2x
let mut x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mut y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
let mut xx = vec![1.5, 2.5, 3.5, 4.5];
let mut yy = vec![0.0; 4];
interp(
&mut x, &mut y, &mut xx, &mut yy,
5, 4, 2, 0, 0 // npol=2 (线性), 无对数
);
assert!((yy[0] - 3.0).abs() < 1e-10);
assert!((yy[1] - 5.0).abs() < 1e-10);
assert!((yy[2] - 7.0).abs() < 1e-10);
assert!((yy[3] - 9.0).abs() < 1e-10);
}
#[test]
fn test_interp_quadratic() {
// 二次函数 y = x^2
let mut x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mut y = vec![1.0, 4.0, 9.0, 16.0, 25.0];
let mut xx = vec![2.5];
let mut yy = vec![0.0];
interp(
&mut x, &mut y, &mut xx, &mut yy,
5, 1, 3, 0, 0 // npol=3 (二次)
);
// 2.5^2 = 6.25
assert!((yy[0] - 6.25).abs() < 1e-10);
}
#[test]
fn test_interp_log() {
// 对数插值测试
let mut x = vec![1.0, 10.0, 100.0, 1000.0];
let mut y = vec![1.0, 2.0, 3.0, 4.0];
let mut xx = vec![10.0_f64.sqrt()]; // sqrt(10)
let mut yy = vec![0.0];
interp(
&mut x, &mut y, &mut xx, &mut yy,
4, 1, 2, 1, 0 // 对数 x
);
// 在 log10 空间中sqrt(10) ≈ 0.5,应该得到 1.5
assert!((yy[0] - 1.5).abs() < 1e-10);
}
}