山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他パラメトリック3次スプライン曲線の係数算定プログラム(32/64bit)
概要
本プログラムは、パラメトリック3次スプラインの係数a,b,c,dを算出します。コマンドライン上で動作します。
詳しい計算方法は、3次スプライン曲線の作成方法(パラメトリック版)を参照してください。
ソースコード・プログラムのダウンロード parametoric_spline.zip
計算結果の見方
inputa dataが補間する前の既知点です。
coefficientのan,bn,cn,dnが各区間の補間係数です。
interpolationが補間された点の一覧です。
計算結果の出力例
input data n,xn,yn 0,2041.68,1575.59 1,2830.84,2298.99 2,3685.07,1900.71 3,4191.15,1353.67 4,4889.14,1648.74 5,5403.39,2536.84 coefficient y=an*(t-tn)^3+bn*(t-tn)^2+cn*(t-tn)+dn tn,an,bn,cn,dn 0 0,45.7243,0,743.436,2041.68 0,-274.787,0,998.187,1575.59 1 1,-163.551,137.173,880.609,2830.84 1,252.253,-824.36,173.827,2298.99 2 2,195.261,-353.481,664.3,3685.07 2,238.694,-67.6007,-718.134,1900.71 3 3,-77.4343,232.303,543.121,4191.15 3,-216.161,648.483,-137.252,1353.67 4 4,0,0,514.25,4889.14 4,0,0,888.1,1648.74 interpolation No,xn,yn 0,1.27165e+070,1.27165e+070 1,1.27165e+070,1.27165e+070 2,1.27165e+070,1.27165e+070 3,1.27165e+070,1.27165e+070 4,1.27165e+070,1.27165e+070 5,1.27165e+070,1.27165e+070 6,1.27165e+070,1.27165e+070 7,1.27165e+070,1.27165e+070 8,1.27165e+070,1.27165e+070 9,1.27165e+070,1.27165e+070 10,1.27165e+070,1.27165e+070 11,1.27165e+070,1.27165e+070 12,1.27165e+070,1.27165e+070 13,1.27165e+070,1.27165e+070 14,1.27165e+070,1.27165e+070 15,1.27165e+070,1.27165e+070 16,1.27165e+070,1.27165e+070 17,1.27165e+070,1.27165e+070 18,1.27165e+070,1.27165e+070 19,1.27165e+070,1.27165e+070 20,1.27165e+070,1.27165e+070
ソースコードの説明
入力座標
座標は、構造体の配列sp2で定義しています。座標数は、マクロ NMAX で定義しています。
補完後の座標は、構造体の配列pに格納されます。
SPLINEクラス(spline1.cpp)
入力座標より係数a,b,c,d及び補完値を計算します。
coefficient
入力座標の設定及び係数a,b,c,dを計算します。
係数の計算には、MATRIXクラスを使います。
interpolation
coefficientによって計算された係数より補完値を計算します。
MATRIX_OWN_COLUMN,MAXRIXクラス(matrix.h)
ヘッダーファイルmatrix.hで定義されSPLINEクラス用に最低限の行列クラスを提供します。
alloc
行列の大きさを定義します。
MATRIX_OWN_COLUMNクラスは、1列固定で、行数が任意となります。
MATRIXクラスは、行数・列数を指定できますが、正方行列のみ正常に動作します。
行列のデーターは、newで確保されます。allocを2回以上実行するとサイズが不足する場合、メモリを再確保します。頻繁に行列サイズが確保する場合は、最初に大きめに確保しておくとよいでしょう。
set
double型の配列を渡すことにより行列に値を設定します。
rswap
行列の行同士を交換します。
Triangular
行列を上三角行列にガウスの前進消去法により変換します。引数によりピポット選択をしないようにも設定できます。
forward_sub
Triangularと同様、行列を上三角行列に変換できますが、このメンバー関数は、他の行列(1列の行列に限る)も同時にピポット選択時に行の交換及びガウスの消去法の対象にできます。引数違いで同名メンバー関数(行列のポインタ)がありますが、これは複数の行列を対象にできます。
backward_sub
後退代入法で連立方程式を解きます。あらかじめ行列は上三角行列に変換しておく必要があります。
det
行列の値を求めます。上三角行列でない場合は、行列のコピーを取ってから値を求めます。
zero
行列を0クリアします。
コンパイル方法
Win32コンソールアプリケーションでプロジェクトをつくり、spline1.cppとmatrix.hをプロジェクトに追加します。
ソースコード(spline2.cpp)
// n次座標上のパラメトリックスプライン曲線による補間プログラム
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <tchar.h>
#include "matrix.h"
class SPLINE_P{ // 一次のパラメトリックスプライン曲線クラス
friend class SPLINE_N;
double* a; // 区間ごとの係数
double* b; // 区間ごとの係数
double* c; // 区間ごとの係数
double* d;
int nmax;
int max;
public:
SPLINE_P(){
a=b=c=d=0;
nmax=0;
max=0;
}
SPLINE_P(int n){
alloc(n);
}
~SPLINE_P(){
delete [] a;
delete [] b;
delete [] c;
delete [] d;
}
void alloc(int n){
if(max<n){
if(a){
delete [] a;
delete [] b;
delete [] c;
delete [] d;
}
a=new double[n-1];
b=new double[n-1];
c=new double[n-1];
d=new double[n-1];
nmax=n;
max=n;
}else{
nmax=n;
}
}
void put(int n){
_tprintf(TEXT("%i,%g,%g,%g,%g\n"),n,a[n],b[n],c[n],d[n]);
}
void put(void){
for(int n=0;n<nmax-1;n++){
put(n);
}
}
double interpolation(double x,int n){ // 指定した区間のdx=x-xnに対する補間値
return a[n]*x*x*x + b[n]*x*x + c[n]*x + d[n];
}
};
// 1次配列を次配列としてアクセスするクラス
class DOUBLE_2VEC{
double* p;
int cram;
public:
DOUBLE_2VEC(int n){
cram=n;
}
double* operator=(double* x){
return p=x;
}
double* operator[](int n){
return p+n*cram;
}
};
// n次座標上のパラメトリックスプライン曲線クラス
class SPLINE_N{
public:
SPLINE_P* spline_p; // パラメトリックスプラインクラスn次分
int nmax;
int dim;
int dmax;
MATRIX g;
MATRIX_OWN_COLUMN* k;
MATRIX_OWN_COLUMN* s;
SPLINE_N(){
dmax=nmax=0;
dim=0;
spline_p=0;
}
SPLINE_N(int d,int n){ // 次数,既知点数
dmax=nmax=0;
dim=0;
spline_p=0;
alloc(d,n);
}
~SPLINE_N(){
delete [] spline_p;
delete [] s;
delete [] k;
}
void alloc(int d,int n){
if(dmax<d){
if(spline_p){
delete [] spline_p;
delete [] k;
delete [] s;
}
dmax=d;
}
nmax=n;
dim=d;
spline_p=new SPLINE_P[dmax];
k=new MATRIX_OWN_COLUMN[dim];
s=new MATRIX_OWN_COLUMN[dim];
for(int i=0;i<dim;i++){
spline_p[i].alloc(nmax);
}
g.alloc(nmax-3,nmax-3);
}
void put(int n){
_tprintf(TEXT("%i\n"),n);
for(int i=0;i<dim;i++){
_tprintf(TEXT("\t"));
spline_p[i].put(n);
}
}
void put(void){
for(int n=0;n<nmax-1;n++){
put(n);
}
}
void coefficient(double* pa){ // 座標一覧から係数の算定を行う。
DOUBLE_2VEC p(dim); // 1次配列を次配列としてアクセスするクラス
p=pa;
int i;
for(i=0;i<dim;i++){
k[i].alloc(nmax-3);
s[i].alloc(nmax-3);
}
for(i=1;i<nmax-2;i++){
for(int j=0;j<dim;j++){
k[j][i-1]=6*(p[i+1][j]-2*p[i][j]+p[i-1][j]);
}
}
g.zero();
for(i=0;i<=nmax-4;i++){
if(0<i)
g[i][i-1]=1;
g[i][i]=4;
if(i<nmax-4)
g[i][i+1]=1;
}
g.forward_sub(k,dim,false); // 前進消去
g.backward_sub(k,s,dim); // 後退代入
double s0,s1;
double ka,kb,kc,kd;
for(int n=0;n<dim;n++){
for(i=0;i<=nmax-2;i++){
if(i==0){
s0=0;
s1=s[n][i];
}else if(i==nmax-3){
s0=s[n][i-1];
s1=0;
}else if(i==nmax-2){
s0=0;
s1=0;
}else{
s0=s[n][i-1];
s1=s[n][i];
}
spline_p[n].a[i]=ka=(s1-s0)/6.0f;
spline_p[n].b[i]=kb=s0/2.0f;
spline_p[n].c[i]=kc=(p[i+1][n]-p[i][n])-1.0f/6.0f*(2.0f*s0+s1);
spline_p[n].d[i]=kd=p[i][n];
}
}
}
void interpolation(double* p2,double* p1,int d){ // 区間をd分割して補間値の座標一覧を作成しp1に保存する
int i;
int u=0;
DOUBLE_2VEC pb(dim); // 1次配列を次配列としてアクセスするクラス
pb=p2;
DOUBLE_2VEC p(dim); // 1次配列を次配列としてアクセスするクラス
p=p1;
for(i=0;i<nmax-1;i++){
double dx,x,y;
dx=1.0f/double(d);
for(int j=0;j<d;j++){
for(int n=0;n<dim;n++){
x=dx*double(j);
y=spline_p[n].interpolation(x,i);
p[u][n]=y;
}
++u;
}
for(int n=0;n<dim;n++){
p[u][n]=pb[i+1][n];
}
}
}
};
#define HMAX 4 // 区間の分割数
// 入力座標
#define NMAX2 6 // 入力データー数
#define DIM 2 // 座標の次数
double sp[][2]={{ 2041.68, 1575.59 },
{ 2830.84, 2298.99 },
{ 3685.07, 1900.71 },
{ 4191.15, 1353.67 },
{ 4889.14, 1648.74 },
{ 5403.39, 2536.84 }};
double* p[NMAX2*HMAX][DIM]; // 補間された座標が保存される
void main(void){
int i;
SPLINE_N s;
s.alloc(DIM,NMAX2);
_tprintf(TEXT("input data\n"));
_tprintf(TEXT("n,xn,yn\n"));
for(i=0 ; i<NMAX2 ; i++){
_tprintf(TEXT("%i"),i);
for(int j=0;j<DIM;j++){
_tprintf(TEXT(",%g"),sp[i][j]);
}
_tprintf(TEXT("\n"));
}
s.coefficient((double*)sp);
_tprintf(TEXT("\ncoefficient\n"));
_tprintf(TEXT("y=an*(t-tn)^3+bn*(t-tn)^2+cn*(t-tn)+dn\n"));
_tprintf(TEXT("tn,an,bn,cn,dn\n"));
s.put();
s.interpolation((double*)sp,(double*)p,HMAX);
_tprintf(TEXT("\ninterpolation\n"));
_tprintf(TEXT("No,xn,yn\n"));
for(i=0 ; i<(NMAX2-1)*HMAX+1 ; i++){
_tprintf(TEXT("%i"),i);
for(int j=0;j<DIM;j++){
_tprintf(TEXT(",%g"),p[i][j]);
}
_tprintf(TEXT("\n"));
}
}
ソースコード(matrix.h)
#ifndef MATRIX001_H
// スプライン用の最小限の行列クラス
#define MATRIX001_H 1
// 行列の状態
enum MATRIX_TYPE{ undecided=0,upper_triangular,lowwer__triangular };
// 1列の行列クラス
class MATRIX_OWN_COLUMN{
double* d; // 行列データー
double k; // 行列の符号および乗数を示す 例えばスカラー倍した場合はこの係数のみ乗ずる。
int rmax; // 行列 行数
int max; // 確保されている配列のサイズ
MATRIX_TYPE flag; // 行列タイプ
public:
MATRIX_OWN_COLUMN(){
d=0;
rmax=max=0;
}
MATRIX_OWN_COLUMN(int ar){
d=0;
rmax=max=0;
alloc(ar);
}
~MATRIX_OWN_COLUMN(){
delete [] d;
}
void alloc(int ar){
if(max<ar){ // 配列サイズが不足している場合は再確保する
max=rmax=ar;
if(d)
delete [] d;
d=new double[rmax];
}else{
rmax=ar;
}
flag=undecided;
k=1;
}
void rswap(int a,int b){ // 行の交換
double t=d[a];
d[a]=d[b];
d[b]=t;
k=-k;
}
void set(double* ad){ // 行列への定数の代入
for(int n=0;n<rmax;n++)
d[n]=ad[n];
}
void put(void){ // 行列を標準出力へ表示
for(int rn=0;rn<rmax;rn++){
_tprintf(TEXT("%i %g\n"),rn,d[rn]);
}
}
double& operator[](int n){
return d[n];
}
friend class MATRIX;
};
// 正方行列クラス
class MATRIX{
double* d; // 行列データー
int cmax,rmax; // 行列 列数 行数
double k; // 行列の符号および乗数を示す 例えばスカラー倍した場合はこの係数のみ乗ずる。det
int max; // 確保されている配列のサイズ
MATRIX_TYPE flag; // 行列タイプ
public:
MATRIX(){
d=0;
cmax=rmax=0;
max=0;
k=1;
}
MATRIX(int ar,int ac){
d=0;
cmax=rmax=0;
max=0;
k=1;
alloc(ar,ac);
}
~MATRIX(){
delete [] d;
}
void alloc(int ar,int ac){
cmax=ac;
rmax=ar;
if(max<ar*ac){ // 配列サイズが不足する場合は配列を再確保する
if(d){
delete [] d;
}
max=cmax*rmax;
d=new double[max];
}
flag=undecided;
k=1;
}
double* operator[](int n){
return d+n*cmax;
}
void rswap(int a,int b){ // 行の交換
for(int i=0;i<cmax;i++){
double t=(*this)[a][i];
(*this)[a][i]=(*this)[b][i];
(*this)[b][i]=t;
}
k=-k; // 符号を反転する
}
void set(double* ad){ // 行列への定数の代入
for(int n=0;n<cmax*rmax;n++)
d[n]=ad[n];
}
void put(void){ // 行列を標準出力へ表示
for(int rn=0;rn<rmax;rn++){
for(int cn=0;cn<cmax;cn++){
_tprintf(TEXT("%g "),(*this)[rn][cn]);
}
_puttc(_T('\n'),stdout);
}
}
void Triangular(bool pivoting=true){ // 上三角行列に変換(ガウスの前進消去法) 引数でピポット選択の有無を選択
int i,j,k;
double uk1;
for(k=0;k<rmax-1;k++){
double m=0;
double u;
int n=k;
if(pivoting){ // ピポット選択を行う
for(i=k;i<rmax;i++){ // 絶対値が最大の行の検索
u=fabs( (*this)[i][k] );
if(m!=0 && m<u){
m=u;
n=i;
}
}
if(n!=k)
rswap(n,k); // 絶対値が最大の行と入れ替え
}
for(i=k+1;i<rmax;i++){
uk1 = (*this)[i][k] / (*this)[k][k]; // ui1=ai1/a11
(*this)[i][k]=0; // 上三角行列にすると参照されないので一般的には不要
for(j=k+1;j<cmax;j++){
(*this)[i][j] = (*this)[i][j] - uk1 * (*this)[k][j]; //aji = aji - ui1 * a1j
}
}
}
flag=upper_triangular; // 行列タイプを設定
}
void forward_sub(MATRIX_OWN_COLUMN& b,bool pivoting=true){ // 上三角行列に変換(ガウスの前進消去法) 引数でピポット選択の有無を選択
int i,j,k;
double uk1;
for(k=0;k<rmax-1;k++){
double m=0;
double u=0;
int n=k;
if(pivoting){
for(i=k;i<rmax;i++){ // 絶対値が最大の行の検索
u=fabs( (*this)[i][k] );
if(m<u){
m=u;
n=i;
}
}
if(m!=0 && n!=k){
rswap(n,k); // 絶対値が最大の行と入れ替えただしすべてが0の時は入れ替えない
b.rswap(n,k);
}
}
for(i=k+1;i<rmax;i++){
uk1 = (*this)[i][k] / (*this)[k][k]; // ui1=ai1/a11
b[i] = b[i] - uk1 * b[k];
(*this)[i][k]=0; // 上三角行列にすると参照されないので一般的には不要
for(j=k+1;j<cmax;j++){
(*this)[i][j] = (*this)[i][j] - uk1 * (*this)[k][j]; //aji = aji - ui1 * a1j
}
}
}
flag=upper_triangular; // 行列タイプを設定
}
void forward_sub(MATRIX_OWN_COLUMN* b,int nmax, bool pivoting=true){ // 上三角行列に変換(ガウスの前進消去法) ピポット選択有り
int i,j,k;
double uk1;
for(k=0;k<rmax-1;k++){
double m=0;
double u=0;
int n=k;
if(pivoting){
for(i=k;i<rmax;i++){ // 絶対値が最大の行の検索
u=fabs( (*this)[i][k] );
if(m<u){
m=u;
n=i;
}
}
if(m!=0 && n!=k){
rswap(n,k); // 絶対値が最大の行と入れ替えただしすべてが0の時は入れ替えない
for(int o=0;o<nmax;o++){
b[o].rswap(n,k);
}
}
}
for(i=k+1;i<rmax;i++){
uk1 = (*this)[i][k] / (*this)[k][k]; // ui1=ai1/a11
for(int o=0;o<nmax;o++){
MATRIX_OWN_COLUMN& t=b[o];
t[i] = t[i] - uk1 * t[k];
}
(*this)[i][k]=0; // 上三角行列にすると参照されないので一般的には不要
for(j=k+1;j<cmax;j++){
(*this)[i][j] = (*this)[i][j] - uk1 * (*this)[k][j]; //aji = aji - ui1 * a1j
}
}
}
flag=upper_triangular; // 行列タイプを設定
}
void backward_sub(MATRIX_OWN_COLUMN& b,MATRIX_OWN_COLUMN& x){ // 後退代入法 あらかじめ上三角行列に変換しておく必要がある。
int i,j;
for(i=rmax-1;0<=i;i--){
double c=b[i]*b.k;
for(j=i+1;j<rmax;j++){
c -= (*this)[i][j] * x[j];
}
x[i] = c / (*this)[i][i];
}
}
void backward_sub(MATRIX_OWN_COLUMN* b,MATRIX_OWN_COLUMN* x,int nmax){ // 後退代入法 あらかじめ上三角行列に変換しておく必要がある。
int i,j;
for(i=rmax-1;0<=i;i--){
for(int n=0;n<nmax;n++){
double c=b[n][i]*b[n].k;
for(j=i+1;j<rmax;j++){
c -= (*this)[i][j] * x[n][j];
}
x[n][i] = c / (*this)[i][i];
}
}
}
double det(void){ // 行列の値を算出
double a=k;
if(flag==upper_triangular){ // 上三角行列
for(int i=0;i<cmax;i++)
a *= (*this)[i][i];
return a;
}else{ // 上三角行列に変換してから値を求める
MATRIX x(rmax,cmax);
x=(*this);
x.Triangular();
return x.det();
}
}
void zero(void){ // 行列の初期化
ZeroMemory((void*)d,sizeof(d[0])*rmax*cmax);
}
};
#endif