182 lines
4.4 KiB
Rust
182 lines
4.4 KiB
Rust
//! 通用插值过程
|
||
//!
|
||
//! 原始 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);
|
||
}
|
||
}
|