Interpolation — 補間
概要
sangi の補間モジュールは、 既知の標本点 $(x_i, y_i)$ から間の値を再構成する関数群を提供する。 用途に応じて次のカテゴリに分かれる。
- 区分・多項式補間 — ラグランジュ / Newton 分割差分 / 線形・双線形 / 階段 (zero-order hold) / 重心ラグランジュ
- 3 次スプライン — 自然境界・クランプ境界の $C^2$ 連続な区分 3 次曲線
- 形状保存・局所補間 — PCHIP (単調保存)・修正 Akima・Catmull-Rom・滑らか補間・Floater-Hormann 有理補間・5 次 Hermite
- B-スプライン — Cox-de Boor 基底・クランプ節点・de Boor 評価・B-スプライン補間 / 回帰
- 三角補間 — 等間隔周期データの三角多項式補間
- スプラインフィッティング — 平滑化パラメータ付き最小二乗フィット (1D / パラメトリック / 2D テンソル積)
- 画像補間 — 最近傍・双 3 次 (Catmull-Rom カーネル)・Lanczos リサンプリング
テンプレートパラメータ $T$ は concepts::Field または concepts::OrderedField を要求する。 通常は double、 高精度が必要なら多倍長型 Float を渡せる。 すべて namespace sangi。
多くの関数は std::span<const T> を取る版と std::vector<T> を取る版の両オーバーロードを持つ。 本ページでは主に std::vector 版で説明する (span 版も引数の意味は同じ)。
ビルド
// 区分多項式・スプライン・形状保存・フィッティング・画像補間
#include <math/interpolation/Interpolation.hpp>
// De Boor 基底・cubicSpline・B-スプライン回帰
#include <math/interpolation/BSpline.hpp>
ヘッダオンリー。 リンクするライブラリは不要。 内部で線形系を解く関数 (B-スプライン補間・スプラインフィッティング・サーフェススプライン) は <math/linalg/solvers.hpp> に依存するが、 Interpolation.hpp が自動でインクルードする。
区分・多項式補間
| 関数 | 説明 |
|---|---|
lagrange_interpolation(x, y, xi) | ラグランジュ補間多項式を $x_i$ で評価 |
newton_interpolation_coefficients(x, y) | Newton 補間 (分割差分) の係数を計算 |
newton_interpolation_evaluate(x, coeffs, xi) | Newton 補間多項式を Horner 法で評価 |
linear_interpolate(x0, y0, x1, y1, xi) | 2 点間の線形補間 |
bilinear_interpolate(...) | 矩形格子上の双線形補間 |
stepInterpolation(x, y, xi) | 階段補間 (zero-order hold) |
barycentricLagrangeWeights(x) | 重心ラグランジュ補間の重みを計算 |
barycentricLagrangeEvaluate(x, y, w, xi) | 重心ラグランジュ補間を評価 |
関数詳細
lagrange_interpolation
template<concepts::Field T>
T lagrange_interpolation(
const std::vector<T>& x,
const std::vector<T>& y,
T xi);
動作: $n$ 個の標本点を通る次数 $n-1$ のラグランジュ補間多項式 $L(x) = \sum_i y_i \prod_{j \ne i} \frac{x - x_j}{x_i - x_j}$ を点 $x_i$ で直接評価する。 各評価が $O(n^2)$。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (要素は相異なること、 非空) |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
xi | T | 評価点 |
注意: サイズ不一致・空入力は std::invalid_argument を送出。 同じ標本点で多数の点を評価するなら、 重みを再利用できる barycentricLagrangeWeights / barycentricLagrangeEvaluate の方が速い。 高次の等間隔点では Runge 現象に注意 (floaterHormannWeights を参照)。
// 例: 3 点 (0,0),(1,1),(2,4) を通る放物線を x=1.5 で評価
std::vector<double> xs{0.0, 1.0, 2.0}, ys{0.0, 1.0, 4.0};
double v = lagrange_interpolation<double>(xs, ys, 1.5);
// 実行結果: v = 2.25 (= 1.5^2)
newton_interpolation_coefficients
template<concepts::Field T>
std::vector<T> newton_interpolation_coefficients(
const std::vector<T>& x,
const std::vector<T>& y);
動作: Newton の前進分割差分表を構築し、 補間多項式 $p(x) = c_0 + c_1 (x - x_0) + c_2 (x - x_0)(x - x_1) + \cdots$ の係数 $\{c_i\}$ を返す。 係数は newton_interpolation_evaluate に渡して評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (相異なること、 非空) |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
用途: 標本点を固定して何度も評価する場合。 係数を一度求めれば各評価は Horner 法で $O(n)$。
newton_interpolation_evaluate
template<concepts::Field T>
T newton_interpolation_evaluate(
const std::vector<T>& x,
const std::vector<T>& coeffs,
T xi);
動作: newton_interpolation_coefficients の返した分割差分係数を Horner 法で点 $x_i$ で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (係数を求めたときと同じもの) |
coeffs | const std::vector<T>& | 分割差分係数 (x と同サイズ) |
xi | T | 評価点 |
std::vector<double> xs{0.0, 1.0, 2.0}, ys{0.0, 1.0, 4.0};
auto c = newton_interpolation_coefficients<double>(xs, ys);
double v = newton_interpolation_evaluate<double>(xs, c, 1.5);
// 実行結果: v = 2.25 (ラグランジュ補間と同じ多項式)
linear_interpolate
template<concepts::Field T>
T linear_interpolate(T x0, T y0, T x1, T y1, T xi);
動作: 2 点 $(x_0, y_0)$, $(x_1, y_1)$ を通る直線上で $x_i$ の値を返す ($y_0 + t(y_1 - y_0)$, $t = (x_i - x_0)/(x_1 - x_0)$)。 $x_0 \approx x_1$ のときは縮退を避けるため 2 点の平均 $(y_0 + y_1)/2$ を返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x0, y0 | T | 端点 1 の座標 |
x1, y1 | T | 端点 2 の座標 |
xi | T | 評価点 (区間外なら外挿) |
bilinear_interpolate
template<concepts::Field T>
T bilinear_interpolate(
T x, T y,
T x1, T y1, T x2, T y2,
T q11, T q12, T q21, T q22);
動作: 矩形 $[x_1, x_2] \times [y_1, y_2]$ の 4 隅の値から点 $(x, y)$ の値を双線形補間する。 下辺・上辺で線形補間してから縦方向に再度線形補間する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x, y | T | 評価点の座標 (矩形内にあること) |
x1, y1 | T | 左下頂点の座標 |
x2, y2 | T | 右上頂点の座標 |
q11, q12, q21, q22 | T | 左下・左上・右下・右上の頂点値 |
注意: 評価点が矩形 $[x_1, x_2] \times [y_1, y_2]$ の外にある場合は std::invalid_argument を送出する。
stepInterpolation
template<concepts::OrderedField T>
T stepInterpolation(
const std::vector<T>& x,
const std::vector<T>& y,
T xi);
動作: 階段補間 (zero-order hold)。 $x_i$ を含む区間 $[x_i, x_{i+1})$ の左端の値 $y_i$ をそのまま返す。 区間検索は二分探索で $O(\log n)$。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (昇順、 非空) |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
xi | T | 評価点 |
注意: 範囲外は最も近い端点の値を返す ($x_i \le x_0$ で $y_0$、 $x_i \ge x_{n-1}$ で $y_{n-1}$)。
barycentricLagrangeWeights
template<concepts::Field T>
std::vector<T> barycentricLagrangeWeights(
const std::vector<T>& x);
動作: 重心ラグランジュ補間の重み $w_j = 1 / \prod_{i \ne j} (x_j - x_i)$ を計算する。 重みは標本点だけで定まり、 評価点 $y$ には依存しないので一度計算すれば使い回せる。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (要素は相異なること、 非空) |
用途: 通常のラグランジュ補間は毎回 $O(n^2)$ だが、 重心形式は重み計算 $O(n^2)$ + 評価ごと $O(n)$。 同じ標本点で何度も評価する場合に有利。
barycentricLagrangeEvaluate
template<concepts::Field T>
T barycentricLagrangeEvaluate(
const std::vector<T>& x,
const std::vector<T>& y,
const std::vector<T>& weights,
T xi);
動作: 第 2 種重心公式 $p(x_i) = \dfrac{\sum_j w_j y_j / (x_i - x_j)}{\sum_j w_j / (x_i - x_j)}$ で補間値を評価する。 $x_i$ がいずれかの標本点に一致する場合はその $y_j$ をそのまま返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
weights | const std::vector<T>& | barycentricLagrangeWeights の返した重み |
xi | T | 評価点 |
std::vector<double> xs{0.0, 1.0, 2.0}, ys{0.0, 1.0, 4.0};
auto w = barycentricLagrangeWeights<double>(xs);
double v = barycentricLagrangeEvaluate<double>(xs, ys, w, 1.5);
// 実行結果: v = 2.25
3 次スプライン
3 次スプラインは隣接区間を $C^2$ 連続で滑らかに接続する区分 3 次曲線である。 sangi は 2 つの入口を提供する。
- 係数配列を返す
cubic_spline_coefficients+cubic_spline_evaluate(Interpolation.hpp) - 係数と
evalをまとめた結果構造体を返すcubicSpline(BSpline.hpp)
SplineBoundaryCondition
enum class SplineBoundaryCondition {
Natural, // 自然スプライン (両端で 2 階微分が 0)
Clamped // クランプスプライン (両端で 1 階微分値を指定)
};
| 値 | 意味 |
|---|---|
Natural | 自然境界条件 $S''(x_0) = S''(x_{n-1}) = 0$。 端点の傾き情報が無いときの既定 |
Clamped | クランプ境界条件 $S'(x_0)$, $S'(x_{n-1})$ を bc_values で指定 |
cubic_spline_coefficients
template<concepts::Field T>
std::vector<std::array<T, 4>> cubic_spline_coefficients(
const std::vector<T>& x,
const std::vector<T>& y,
int boundary_condition = 0,
const std::pair<T, T>& bc_values = { 0, 0 });
動作: 標本点を通る区分 3 次スプラインの係数を計算する。 各区間の係数は $\{a, b, c, d\}$ で、 区間 $i$ では $S_i(x) = a + b(x - x_i) + c(x - x_i)^2 + d(x - x_i)^3$。 内部で三重対角系を Thomas 法 (前進消去 + 後退代入) で解く。 区間数 $n-1$ 個の std::array<T, 4> を返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (狭義単調増加、 2 点以上) |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
boundary_condition | int | 境界条件 (0 = 自然、 それ以外 = クランプ)。 既定 0 |
bc_values | const std::pair<T, T>& | クランプ時の両端の傾き $\{S'(x_0),\, S'(x_{n-1})\}$。 自然境界では無視。 既定 $\{0, 0\}$ |
注意: span 版は第 3 引数を SplineBoundaryCondition の列挙値で受ける。 サイズ不一致・点数 2 未満・$x$ が単調増加でない場合は std::invalid_argument を送出。 返した係数は cubic_spline_evaluate で評価する。
cubic_spline_evaluate
template<concepts::Field T>
T cubic_spline_evaluate(
const std::vector<T>& x,
const std::vector<std::array<T, 4>>& coeffs,
T xi);
動作: スプライン係数を点 $x_i$ で評価する。 $x_i$ を含む区間を線形探索し、 $S_i(x_i) = a + b\,\delta + c\,\delta^2 + d\,\delta^3$ ($\delta = x_i - x_i^{\text{left}}$) を返す。 範囲外は最終区間の定数項を返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (係数を求めたときと同じもの) |
coeffs | const std::vector<std::array<T, 4>>& | 区間ごとの係数 (サイズ $= x$ サイズ $- 1$) |
xi | T | 評価点 |
用途: cubic_spline_coefficients だけでなく、 pchipCoefficients・catmullRomCoefficients・modifiedAkimaCoefficients・smoothInterpolationCoefficients はいずれも同じ $\{a, b, c, d\}$ 形式の係数を返すため、 この関数で評価できる。
std::vector<double> xs{0.0, 1.0, 2.0, 3.0}, ys{0.0, 1.0, 0.0, 1.0};
auto coeffs = cubic_spline_coefficients<double>(xs, ys); // 既定: 自然境界
double v = cubic_spline_evaluate<double>(xs, coeffs, 1.5);
// 実行結果: v ≒ 0.5 付近 (標本点 (1,1)-(2,0) の間を滑らかに通過)
cubicSpline
template<typename T>
struct CubicSplineResult {
std::vector<T> a, b, c, d; // 区間ごとの係数: S_i(x) = a + b(x-x_i) + c(x-x_i)^2 + d(x-x_i)^3
std::vector<T> x; // 節点
T eval(T t) const; // 補間値を評価
};
template<typename T>
CubicSplineResult<T> cubicSpline(
const std::vector<T>& xs,
const std::vector<T>& ys);
動作: BSpline.hpp が提供する自然 3 次スプライン補間 (両端で $S'' = 0$)。 係数と評価メソッド eval をまとめた CubicSplineResult<T> を返すので、 係数配列と $x$ を別々に持ち回る必要がない。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
xs | const std::vector<T>& | 節点の $x$ 座標 (昇順、 2 点以上) |
ys | const std::vector<T>& | 節点の $y$ 座標 (xs と同サイズ) |
返り値: CubicSplineResult<T>。 result.eval(t) で任意点の補間値を得る。
std::vector<double> xs{0.0, 1.0, 2.0, 3.0}, ys{0.0, 1.0, 4.0, 9.0};
auto spline = cubicSpline<double>(xs, ys);
double v = spline.eval(1.5);
// 実行結果: v ≒ 2.3 付近 (節点 (1,1)-(2,4) の間を滑らかに通過)
形状保存・局所補間
次の関数群は区間ごとの 3 次 (一部 5 次) 係数を返し、 cubic_spline_evaluate (5 次は quinticHermiteEvaluate) で評価する。 全体最適化を行わず局所的に傾きを決めるため、 標準 3 次スプラインより振動 (オーバーシュート) を抑えやすい。
| 関数 | 係数型 | 説明 |
|---|---|---|
pchipCoefficients(x, y) | array<T,4> | PCHIP。 Fritsch-Carlson の単調保存 3 次 Hermite |
modifiedAkimaCoefficients(x, y) | array<T,4> | 修正 Akima。 Boost.Math 準拠の安定化局所傾き |
catmullRomCoefficients(x, y) | array<T,4> | Catmull-Rom。 $C^1$ 連続、 CG で定番 |
smoothInterpolationCoefficients(x, y) | array<T,4> | 傾き混合による滑らか区分補間 (端区間は 2 次) |
floaterHormannWeights(x, d) | — | Floater-Hormann 有理補間の重み (Runge 現象を回避) |
floaterHormannEvaluate(x, y, w, xi) | — | Floater-Hormann 有理補間を評価 |
quinticHermiteCoefficients(x, y, dy, d2y) | array<T,6> | 5 次 Hermite ($f, f', f''$ 指定、 $C^2$ 連続) |
quinticHermiteEvaluate(x, coeffs, xi) | — | 5 次 Hermite 補間を評価 |
関数詳細
pchipCoefficients
template<concepts::OrderedField T>
std::vector<std::array<T, 4>> pchipCoefficients(
const std::vector<T>& x,
const std::vector<T>& y);
動作: PCHIP (Piecewise Cubic Hermite Interpolating Polynomial)。 Fritsch-Carlson アルゴリズムで各節点の傾きを定め、 単調区間では単調性を保存してオーバーシュートを抑える。 内部点は調和平均、 端点は Bessel の片側 3 点公式で傾きを推定し、 単調性条件 $\alpha^2 + \beta^2 \le 9$ を満たすよう傾きを制限する。 区間ごとの $\{a, b, c, d\}$ を返し、 cubic_spline_evaluate で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 (昇順、 2 点以上) |
y | const std::vector<T>& | 節点の $y$ 座標 (x と同サイズ) |
用途: 単調なデータ (累積分布・濃度プロファイル等) を滑らかに、 しかし非物理的なアンダー / オーバーシュートなしで補間したいとき。 点数 2 未満やサイズ不一致では空ベクトルを返す。
std::vector<double> xs{0.0, 1.0, 2.0, 3.0}, ys{0.0, 0.0, 1.0, 1.0}; // 段差状の単調データ
auto coeffs = pchipCoefficients<double>(xs, ys);
double v = cubic_spline_evaluate<double>(xs, coeffs, 1.5);
// 実行結果: 0 <= v <= 1 (単調保存ゆえ範囲外に飛び出さない)
modifiedAkimaCoefficients
template<concepts::OrderedField T>
std::vector<std::array<T, 4>> modifiedAkimaCoefficients(
const std::vector<T>& x,
const std::vector<T>& y);
動作: Akima の局所傾き推定に Boost.Math 準拠の修正を加えた版。 傾き差 $|\delta_{i+1} - \delta_i|$ の絶対値に微小量 $\varepsilon$ を足すことでゼロ除算を回避し、 平坦域・等間隔データでの不安定さを改善する。 区間ごとの $\{a, b, c, d\}$ を返し、 cubic_spline_evaluate で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 (昇順、 2 点以上) |
y | const std::vector<T>& | 節点の $y$ 座標 (x と同サイズ) |
用途: 外れ値の影響を局所に閉じ込めたいデータ。 一点の異常値が遠くの区間まで波及しにくい (Akima の特徴)。 点数 2 未満やサイズ不一致では空ベクトルを返す。
catmullRomCoefficients
template<concepts::OrderedField T>
std::vector<std::array<T, 4>> catmullRomCoefficients(
const std::vector<T>& x,
const std::vector<T>& y);
動作: Catmull-Rom スプライン。 各節点の接線を両隣の節点から差分で求める $C^1$ 連続な補間スプライン。 端点は片側差分。 区間ごとの $\{a, b, c, d\}$ を返し、 cubic_spline_evaluate で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 (昇順、 2 点以上) |
y | const std::vector<T>& | 節点の $y$ 座標 (x と同サイズ) |
用途: コンピュータグラフィックスでのキーフレーム補間・曲線描画。 点数 2 未満やサイズ不一致では空ベクトルを返す。
smoothInterpolationCoefficients
template<concepts::OrderedField T>
std::vector<std::array<T, 4>> smoothInterpolationCoefficients(
const std::vector<T>& x,
const std::vector<T>& y);
動作: 隣接区間の傾きを混合して接続を滑らかにする区分補間。 混合式 $A_{\text{mix}} = (|A_0| A_1 + |A_1| A_0)/(|A_0| + |A_1|)$ により、 3 点が一直線なら直線を再現し、 片側が平坦なら混合も 0、 山型 (傾きが逆符号) なら混合 0 となる。 先頭区間と末尾区間は端点傾きを拘束した 2 次、 中間区間は両端傾きを拘束した 3 次。 区間ごとの $\{a, b, c, d\}$ を返し、 cubic_spline_evaluate で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 (狭義単調増加、 2 点以上) |
y | const std::vector<T>& | 節点の $y$ 座標 (x と同サイズ) |
注意: $x$ が狭義単調増加でない場合は std::invalid_argument を送出。
floaterHormannWeights
template<concepts::OrderedField T>
std::vector<T> floaterHormannWeights(
const std::vector<T>& x,
std::size_t d = 3);
動作: Floater-Hormann 重心有理補間の重みを計算する。 次数 $d$ の局所多項式をブレンドした有理補間で、 等間隔点でも Runge 現象を起こさず安定。 $d = 0$ で区分定数、 $d = n-1$ で多項式補間に対応する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 ($n$ 点) |
d | std::size_t | ブレンド次数 ($0 \le d \le n-1$)。 既定 3。 $d \ge n$ は $n-1$ に丸める |
用途: 高次の等間隔データを多項式補間したいが Runge 現象を避けたい場合。 重みは floaterHormannEvaluate に渡す。
floaterHormannEvaluate
template<concepts::OrderedField T>
T floaterHormannEvaluate(
const std::vector<T>& x,
const std::vector<T>& y,
const std::vector<T>& weights,
T xi);
動作: 重心公式 $r(x_i) = \dfrac{\sum_k w_k y_k / (x_i - x_k)}{\sum_k w_k / (x_i - x_k)}$ で Floater-Hormann 有理補間を評価する。 評価点が節点に一致する (または極めて近い) 場合はその $y_k$ を返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 |
y | const std::vector<T>& | 節点の $y$ 座標 (x と同サイズ) |
weights | const std::vector<T>& | floaterHormannWeights の返した重み |
xi | T | 評価点 |
quinticHermiteCoefficients
template<concepts::OrderedField T>
std::vector<std::array<T, 6>> quinticHermiteCoefficients(
const std::vector<T>& x,
const std::vector<T>& y,
const std::vector<T>& dy,
const std::vector<T>& d2y);
動作: 各節点で値 $f$・1 階微分 $f'$・2 階微分 $f''$ を指定する 5 次 Hermite 補間。 $C^2$ 連続。 区間ごとに 6 係数 $\{a, b, c, d, e, f\}$ を返し、 $S(t) = a + b\,t + c\,t^2 + d\,t^3 + e\,t^4 + f\,t^5$ ($t = x - x_i$) で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 (昇順、 2 点以上) |
y | const std::vector<T>& | 各節点の値 $f$ |
dy | const std::vector<T>& | 各節点の 1 階微分値 $f'$ (x と同サイズ) |
d2y | const std::vector<T>& | 各節点の 2 階微分値 $f''$ (x と同サイズ) |
用途: 曲率まで指定したい軌道・運動プロファイルの補間。 サイズ不一致・点数 2 未満では空ベクトルを返す。 評価には quinticHermiteEvaluate を使う (係数長が 6 なので cubic_spline_evaluate は不可)。
quinticHermiteEvaluate
template<concepts::OrderedField T>
T quinticHermiteEvaluate(
const std::vector<T>& x,
const std::vector<std::array<T, 6>>& coeffs,
T xi);
動作: quinticHermiteCoefficients の係数を点 $x_i$ で評価する。 区間は二分探索で特定し、 範囲外は端区間にクランプする。 Horner 法で 5 次多項式を評価。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 節点の $x$ 座標 |
coeffs | const std::vector<std::array<T, 6>>& | 5 次 Hermite 係数 (サイズ $= x$ サイズ $- 1$) |
xi | T | 評価点 |
B-スプライン
B-スプラインは局所台 (compact support) を持つ基底関数の線形結合で曲線を表す方式。 sangi は 2 系統の基底評価を提供する。
Interpolation.hppのbsplineBasis(degree, i, x, knots)(Cox-de Boor 再帰、OrderedField)・bsplineCoefficients・bsplineEvaluateBSpline.hppのbsplineBasis(i, p, t, knots)(De Boor 再帰、int次数)・bsplineBasisAll・uniformKnots・deBoor・bsplineRegression
両ヘッダに同名の bsplineBasis があるが引数並びが異なる (前者は std::span<const T> の節点と std::size_t 次数、 後者は std::vector<T> の節点と int 次数)。
| 関数 | ヘッダ | 説明 |
|---|---|---|
bsplineBasis(degree, i, x, knots) | Interpolation | 基底 $B_{i,\text{degree}}(x)$ の Cox-de Boor 再帰評価 |
bsplineCoefficients(x, y, degree) | Interpolation | クランプ節点 + 制御点係数を計算 |
bsplineEvaluate(knots, coeffs, degree, x) | Interpolation | B-スプライン補間を評価 |
bsplineBasis(i, p, t, knots) | BSpline | 基底 $N_{i,p}(t)$ の De Boor 再帰評価 |
bsplineBasisAll(p, t, knots) | BSpline | 全基底を一括評価 |
uniformKnots(n, p, a, b) | BSpline | クランプ一様節点ベクトルを生成 |
deBoor(controlPoints, knots, p, t) | BSpline | De Boor 法で曲線上の点を評価 |
bsplineRegression(x, y, nBasis, degree) | BSpline | B-スプライン回帰 (最小二乗フィット) |
関数詳細
bsplineBasis (Interpolation.hpp)
template<concepts::OrderedField T>
T bsplineBasis(
std::size_t degree,
std::size_t i,
T x,
std::span<const T> knots);
動作: Cox-de Boor 再帰で B-スプライン基底関数 $B_{i,\text{degree}}(x)$ を評価する。 次数 0 では区間 $[\text{knot}_i, \text{knot}_{i+1})$ の指示関数 (最終区間のみ右端を含む)。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
degree | std::size_t | B-スプラインの次数 |
i | std::size_t | 基底関数のインデックス |
x | T | 評価点 |
knots | std::span<const T> | 節点ベクトル |
bsplineCoefficients
template<concepts::OrderedField T>
std::pair<std::vector<T>, std::vector<T>> bsplineCoefficients(
const std::vector<T>& x,
const std::vector<T>& y,
std::size_t degree = 3);
動作: 標本点を補間する B-スプラインの節点ベクトルと制御点係数の組 {knots, coefficients} を返す。 両端に多重度 $\text{degree}+1$ のクランプ節点を置き、 内部節点は平均化節点で選ぶ。 コロケーション行列 $B_{j,\text{degree}}(x_i)$ を組んで LU 分解で線形系を解く。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 標本点の $x$ 座標 (昇順、 $n$ 点) |
y | const std::vector<T>& | 標本点の $y$ 座標 (x と同サイズ) |
degree | std::size_t | B-スプラインの次数。 既定 3 (3 次)。 $n \ge \text{degree}+1$ が必要 |
注意: サイズ不一致、 または点数が $\text{degree}+1$ 未満では std::invalid_argument を送出。 返した knots と coefficients は bsplineEvaluate に渡す。
bsplineEvaluate
template<concepts::OrderedField T>
T bsplineEvaluate(
const std::vector<T>& knots,
const std::vector<T>& coefficients,
std::size_t degree,
T x);
動作: 制御点係数と基底の線形結合 $\sum_i c_i B_{i,\text{degree}}(x)$ で B-スプライン補間値を評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
knots | const std::vector<T>& | 節点ベクトル (bsplineCoefficients の第 1 要素) |
coefficients | const std::vector<T>& | 制御点係数 (第 2 要素) |
degree | std::size_t | B-スプラインの次数 (係数を求めたときと同じ) |
x | T | 評価点 |
std::vector<double> xs{0.0, 1.0, 2.0, 3.0, 4.0}, ys{0.0, 1.0, 0.0, 1.0, 0.0};
auto [knots, coeffs] = bsplineCoefficients<double>(xs, ys, 3);
double v = bsplineEvaluate<double>(knots, coeffs, 3, 2.5);
// 標本点を通る 3 次 B-スプライン上の値 (xs[2]=2, xs[3]=3 の間)
bsplineBasis (BSpline.hpp)
template<typename T>
T bsplineBasis(size_t i, int p, T t, const std::vector<T>& knots);
動作: De Boor 再帰で基底 $N_{i,p}(t)$ を評価する (Interpolation.hpp 版と同じ Cox-de Boor 漸化式だが、 次数を int で、 節点を std::vector で取る別オーバーロード)。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
i | size_t | 基底関数のインデックス |
p | int | 次数 |
t | T | パラメータ値 |
knots | const std::vector<T>& | 節点ベクトル |
bsplineBasisAll
template<typename T>
std::vector<T> bsplineBasisAll(int p, T t, const std::vector<T>& knots);
動作: 点 $t$ における全基底 $N_{0,p}(t), \ldots, N_{n-1,p}(t)$ ($n = \text{knots.size}() - p - 1$) を一括評価する。 $t$ が節点ベクトルの右端のときは最後の基底だけを 1 にする (右端の取りこぼし防止)。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
p | int | 次数 |
t | T | パラメータ値 |
knots | const std::vector<T>& | 節点ベクトル |
uniformKnots
template<typename T>
std::vector<T> uniformKnots(int n, int p, T a = T{0}, T b = T{1});
動作: 区間 $[a, b]$ にクランプ一様節点ベクトル (両端の多重度 $p+1$、 内部は等間隔) を生成する。 節点総数は $n + p + 1$。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
n | int | 基底関数 (制御点) の個数 |
p | int | 次数 |
a, b | T | パラメータ区間の下限・上限。 既定 $[0, 1]$ |
deBoor
template<typename T>
std::vector<T> deBoor(
const std::vector<std::vector<T>>& controlPoints,
const std::vector<T>& knots,
int p,
T t);
動作: De Boor アルゴリズムで B-スプライン曲線 $C(t) = \sum_i N_{i,p}(t) P_i$ 上の 1 点を評価する。 節点スパンを特定し、 三角表を再帰的に縮約して曲線上の点 (次元 $d$ のベクトル) を返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
controlPoints | const std::vector<std::vector<T>>& | 制御点 ($n$ 個、 各々 $d$ 次元ベクトル) |
knots | const std::vector<T>& | 節点ベクトル |
p | int | 次数 |
t | T | パラメータ値 |
返り値: 曲線上の点 (制御点と同じ次元 $d$ の std::vector<T>)。
bsplineRegression
template<typename T>
struct BSplineRegressionResult {
std::vector<T> coefficients; // B-スプライン係数
std::vector<T> knots; // 節点ベクトル
int degree; // 次数
T eval(T t) const; // 予測値を評価
};
template<typename T>
BSplineRegressionResult<T> bsplineRegression(
const std::vector<T>& x,
const std::vector<T>& y,
int nBasis = 10,
int degree = 3);
動作: $n_{\text{basis}}$ 個の B-スプライン基底に対する最小二乗回帰。 一様節点で設計行列 $B$ を作り、 正規方程式 $B^\top B\, c = B^\top y$ を (微小正則化を加えて) Gauss 消去で解く。 補間 (全点を通る) ではなく平滑なフィット。 結果型の eval(t) で予測値を得る。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | 入力データ ($N$ 標本) |
y | const std::vector<T>& | 出力データ ($N$ 標本、 x と同サイズ) |
nBasis | int | B-スプライン基底の本数。 既定 10。 大きいほど当てはまりが柔軟 |
degree | int | B-スプラインの次数。 既定 3 |
用途: ノイズを含むデータの平滑な傾向曲線を引きたいとき。 基底本数 nBasis で滑らかさと当てはまりのバランスを調整する。
三角補間
cardinalTrigonometricInterpolate
template<concepts::OrderedField T>
T cardinalTrigonometricInterpolate(
const std::vector<T>& y,
T period,
T xi);
動作: 等間隔の周期データ $y[0..N-1]$ (標本点 $x_k = k \cdot \text{period}/N$) を三角多項式で補間する。 Dirichlet 核アプローチ $S(x) = \frac{1}{N}\sum_k y_k\, D_N(x - x_k)$ を直接計算する (FFT 不使用、 中小規模データ向き)。 $N$ の偶奇で核を切り替える。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
y | const std::vector<T>& | 等間隔の標本値 $y[0..N-1]$ |
period | T | 周期 $P$ (標本点は $x_k = k P / N$) |
xi | T | 評価点 |
用途: 周期信号・スペクトル法の基礎となる周期データの再構成。 標本点で厳密に元の値を再現する。
// 例: 周期 2π の正弦波を 8 点で標本化して補間
std::vector<double> y(8);
for (int k = 0; k < 8; ++k) y[k] = std::sin(2.0 * M_PI * k / 8.0);
double v = cardinalTrigonometricInterpolate<double>(y, 2.0 * M_PI, 0.3);
// 実行結果: v ≒ sin(0.3) (標本間も三角多項式で滑らかに再構成)
スプラインフィッティング (平滑化)
標本点を厳密に通す補間ではなく、 平滑化パラメータ $s$ で「当てはまり」と「滑らかさ」を調整する最小二乗フィット。 FITPACK (Dierckx) の splrep/splev に相当する。 1D・パラメトリック曲線・2D 曲面の 3 種。
| 型 / 関数 | 説明 |
|---|---|
SplineFitResult<T> / splineFit(x, y, w, s, degree) | 1D 平滑化スプラインフィット |
splineEval(fit, x) | フィット結果を評価 (単点 / 多点) |
ParametricSplineResult<T> / parametricSplineFit(x, y, s, degree) | 平面曲線 $(x(t), y(t))$ の弧長パラメトリックフィット |
parametricSplineEval(fit, t) | パラメトリックスプラインを評価 |
SurfaceSplineResult<T> / surfaceSplineFit(x, y, z, degree) | 格子データの 2D テンソル積スプラインフィット |
surfaceSplineEval(fit, x, y) | サーフェススプラインを評価 |
関数詳細
SplineFitResult / splineFit
template<concepts::OrderedField T>
struct SplineFitResult {
std::vector<T> knots; // 節点ベクトル
std::vector<T> coefficients; // B-スプライン係数
std::size_t degree; // スプライン次数
T smoothing; // 実際の平滑化パラメータ
T residual; // 残差二乗和
};
template<concepts::OrderedField T>
SplineFitResult<T> splineFit(
const std::vector<T>& x,
const std::vector<T>& y,
const std::vector<T>& w = {},
T s = T(-1),
std::size_t degree = 3);
動作: データ $(x_i, y_i)$ に対し $\sum_i w_i (y_i - S(x_i))^2 + s \int (S''(x))^2 dx$ を最小化する平滑化 B-スプラインフィット。 $s = 0$ なら補間 (全点を通る)、 $s > 0$ なら平滑化 (ノイズ抑制)。 平滑化パラメータに応じて節点数を自動選択する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | データ点の $x$ 座標 (昇順、 $n \ge \text{degree}+1$) |
y | const std::vector<T>& | データ点の $y$ 座標 (x と同サイズ) |
w | const std::vector<T>& | 各点の重み (空なら全て 1.0)。 省略可 |
s | T | 平滑化パラメータ ($0$ = 補間、 $< 0$ = 自動 [Dierckx 推奨の $s = n$])。 既定 $-1$ (自動) |
degree | std::size_t | スプライン次数。 既定 3 (3 次) |
注意: 点数が $\text{degree}+1$ 未満、 サイズ不一致、 重み長不正では std::invalid_argument を送出。 結果は splineEval で評価する。
splineEval
template<concepts::OrderedField T>
T splineEval(const SplineFitResult<T>& fit, T x);
template<concepts::OrderedField T>
std::vector<T> splineEval(const SplineFitResult<T>& fit, std::span<const T> xs);
動作: splineFit の結果を任意点で評価する (FITPACK splev 相当)。 単点版とまとめて評価する多点版がある。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
fit | const SplineFitResult<T>& | splineFit の返した結果 |
x / xs | T / std::span<const T> | 評価点 (単点) または評価点列 (多点) |
// ノイズ入りデータを平滑化フィット
std::vector<double> xs = /* 昇順の x */;
std::vector<double> ys = /* ノイズ入り y */;
auto fit = splineFit<double>(xs, ys); // s 自動、 3 次
double v = splineEval<double>(fit, 1.5); // 平滑化曲線上の値
// fit.residual に残差二乗和、 fit.smoothing に採用された s が入る
ParametricSplineResult / parametricSplineFit
template<concepts::OrderedField T>
struct ParametricSplineResult {
SplineFitResult<T> x_fit; // x(t) のフィット
SplineFitResult<T> y_fit; // y(t) のフィット
std::vector<T> t; // パラメータ値
};
template<concepts::OrderedField T>
ParametricSplineResult<T> parametricSplineFit(
const std::vector<T>& x,
const std::vector<T>& y,
T s = T(-1),
std::size_t degree = 3);
動作: 平面曲線 $(x(t), y(t))$ を弧長パラメータ (累積弦長を $[0, 1]$ に正規化) でフィットする。 $x(t)$ と $y(t)$ をそれぞれ splineFit で個別にフィットする。 $x$ が単調でなくてもよい (閉曲線・ループ可)。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | データ点の $x$ 座標 (2 点以上) |
y | const std::vector<T>& | データ点の $y$ 座標 (x と同サイズ) |
s | T | 平滑化パラメータ ($0$ = 補間、 $< 0$ = 自動)。 既定 $-1$ |
degree | std::size_t | スプライン次数。 既定 3 |
注意: 点数 2 未満・サイズ不一致では std::invalid_argument を送出。 評価は parametricSplineEval を使う。
parametricSplineEval
template<concepts::OrderedField T>
std::pair<T, T> parametricSplineEval(
const ParametricSplineResult<T>& fit,
T t);
動作: パラメータ $t \in [0, 1]$ における曲線上の点 $(x(t), y(t))$ を std::pair<T, T> で返す。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
fit | const ParametricSplineResult<T>& | parametricSplineFit の結果 |
t | T | パラメータ値 ($[0, 1]$ を想定) |
SurfaceSplineResult / surfaceSplineFit
template<concepts::OrderedField T>
struct SurfaceSplineResult {
std::vector<T> knots_x; // x 方向の節点ベクトル
std::vector<T> knots_y; // y 方向の節点ベクトル
std::vector<T> coefficients; // 係数 (nx × ny の行優先フラット配列)
std::size_t nx; // x 方向の基底数
std::size_t ny; // y 方向の基底数
std::size_t degree; // スプライン次数
};
template<concepts::OrderedField T>
SurfaceSplineResult<T> surfaceSplineFit(
const std::vector<T>& x,
const std::vector<T>& y,
const std::vector<T>& z,
std::size_t degree = 3);
動作: 格子データ $z[i][j] = f(x_i, y_j)$ をテンソル積 B-スプライン $S(x, y) = \sum_i \sum_j c_{ij} B_i(x) B_j(y)$ でフィットする。 まず各 $x$ 行を $y$ 方向にフィットし、 続いてその係数を $x$ 方向にフィットする 2 段階分解。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
x | const std::vector<T>& | $x$ 方向の格子座標 ($m_x$ 点、 昇順、 $m_x \ge \text{degree}+1$) |
y | const std::vector<T>& | $y$ 方向の格子座標 ($m_y$ 点、 昇順、 $m_y \ge \text{degree}+1$) |
z | const std::vector<T>& | 値行列 $z[i \cdot m_y + j] = f(x_i, y_j)$ (サイズ $m_x m_y$、 行優先) |
degree | std::size_t | スプライン次数。 既定 3 |
注意: 各方向の点数が $\text{degree}+1$ 未満、 または $z$ のサイズが $m_x m_y$ でないときは std::invalid_argument を送出。 評価は surfaceSplineEval を使う。
surfaceSplineEval
template<concepts::OrderedField T>
T surfaceSplineEval(const SurfaceSplineResult<T>& fit, T x, T y);
動作: テンソル積スプライン $S(x, y) = \sum_i \sum_j c_{ij} B_i(x) B_j(y)$ を点 $(x, y)$ で評価する。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
fit | const SurfaceSplineResult<T>& | surfaceSplineFit の結果 |
x, y | T | 評価点の座標 |
画像補間 (2D リサンプリング)
行優先の 2 次元グリッド (画像) を、 連続座標 $(x, y)$ で補間する関数群。 $x$ は列方向、 $y$ は行方向の 0 始まり座標。 境界はクランプ。
| 関数 | 窓 | 説明 |
|---|---|---|
nearestNeighbor2D(grid, rows, cols, x, y) | 1 点 | 最近傍補間 (四捨五入) |
bicubicInterpolate(grid, rows, cols, x, y) | 4×4 | 双 3 次補間 (Catmull-Rom カーネル) |
lanczosInterpolate2D(grid, rows, cols, x, y, a) | $2a \times 2a$ | Lanczos リサンプリング (sinc 系) |
関数詳細
nearestNeighbor2D
template<concepts::OrderedField T>
T nearestNeighbor2D(
std::span<const T> grid,
std::size_t rows, std::size_t cols,
T x, T y);
動作: 最も近い格子点の値をそのまま返す (座標を四捨五入してインデックス化、 範囲はクランプ)。 画像の縮小やピクセルアートの拡大に使う。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
grid | std::span<const T> | 行優先の 2D データ ($\text{rows} \times \text{cols}$) |
rows, cols | std::size_t | グリッドの行数・列数 |
x, y | T | 評価点の座標 ($x$ = 列方向、 $y$ = 行方向、 0 始まり) |
bicubicInterpolate
template<concepts::OrderedField T>
T bicubicInterpolate(
std::span<const T> grid,
std::size_t rows, std::size_t cols,
T x, T y);
動作: 近傍 $4 \times 4 = 16$ 格子点を Catmull-Rom カーネル ($a = -0.5$) で重み付けして滑らかに補間する。 OpenCV の INTER_CUBIC・Photoshop のバイキュービック法に相当する。 境界はインデックスをクランプ。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
grid | std::span<const T> | 行優先の 2D データ ($\text{rows} \times \text{cols}$) |
rows, cols | std::size_t | グリッドの行数・列数 |
x, y | T | 評価点の座標 ($x$ = 列方向、 $y$ = 行方向、 0 始まり) |
用途: 写真の拡大・縮小で最近傍・双線形より高品質な結果が欲しいとき。
lanczosInterpolate2D
template<concepts::OrderedField T>
T lanczosInterpolate2D(
std::span<const T> grid,
std::size_t rows, std::size_t cols,
T x, T y,
int a = 3);
動作: sinc を基にした高品質リサンプリング。 Lanczos-$a$ カーネル $L(x) = \mathrm{sinc}(x)\,\mathrm{sinc}(x/a)$ ($|x| < a$、 それ以外 0) を $2a \times 2a$ の窓で適用する。 境界で重み和が崩れないよう正規化する。 重みは精度を落とさないよう $T$ 型で計算する (多倍長 Float でも精度を保つ)。
パラメータ:
| 引数 | 型 | 説明 |
|---|---|---|
grid | std::span<const T> | 行優先の 2D データ ($\text{rows} \times \text{cols}$) |
rows, cols | std::size_t | グリッドの行数・列数 |
x, y | T | 評価点の座標 ($x$ = 列方向、 $y$ = 行方向、 0 始まり) |
a | int | Lanczos パラメータ (窓幅)。 $a = 2$ で 4×4 窓、 $a = 3$ で 6×6 窓。 既定 3 (最高品質) |
用途: 高品質な画像縮小・拡大。 双 3 次よりリンギングと鮮鋭度のバランスがよく、 写真リサンプリングの定番。
使用例
#include <math/interpolation/Interpolation.hpp>
#include <math/interpolation/BSpline.hpp>
#include <iostream>
#include <vector>
using namespace sangi;
int main() {
// ---- 1) cubicSpline で点列を補間して eval する ----
std::vector<double> xs{0.0, 1.0, 2.0, 3.0, 4.0};
std::vector<double> ys{0.0, 1.0, 4.0, 9.0, 16.0}; // y = x^2 の標本
auto spline = cubicSpline<double>(xs, ys);
std::cout << "spline(2.5) = " << spline.eval(2.5) << '\n';
// 出力: spline(2.5) ≒ 6.25 付近 (2.5^2 = 6.25)
// ---- 2) cubic_spline_coefficients / evaluate (係数配列版) ----
auto coeffs = cubic_spline_coefficients<double>(xs, ys); // 自然境界
std::cout << "eval(1.5) = "
<< cubic_spline_evaluate<double>(xs, coeffs, 1.5) << '\n';
// ---- 3) splineFit で平滑化 (ノイズ抑制) ----
std::vector<double> xn{0,1,2,3,4,5,6,7,8,9};
std::vector<double> yn{0.1,0.9,2.1,2.9,4.2,4.8,6.1,6.9,8.2,8.9}; // y≒x + ノイズ
auto fit = splineFit<double>(xn, yn); // s 自動、 3 次
std::cout << "fit(4.5) = " << splineEval<double>(fit, 4.5) << '\n';
std::cout << "residual = " << fit.residual << '\n';
// ---- 4) PCHIP で単調保存補間 ----
std::vector<double> xm{0,1,2,3}, ym{0,0,1,1}; // 段差状の単調データ
auto pc = pchipCoefficients<double>(xm, ym);
std::cout << "pchip(1.5) = "
<< cubic_spline_evaluate<double>(xm, pc, 1.5) << '\n';
// 単調保存ゆえ 0 〜 1 の範囲に収まる (オーバーシュートなし)
}
関連する数学的背景
以下の記事では、補間モジュールの基盤となる数学的概念を解説している。