○ 線形補間

補間とは間を補うという意味がありますが、 数学では、数字と数字の間の値の近似値を求める事を指します
線形補間では、数字と数字の間が直線的であると考えて、近似値を算出します

2点(xi,yi),(xi+1,yi+1)がわかっているとき、xi ≦ x ≦ xi + 1 の任意の点xに対するf(x)の近似値を 2点の直線で結んだxの1次関数として求める。

線形補間の式は以下のとうり

y = yi + ( yi1 - yi) * ( x - xi ) / ( xi1 - xi )

なんだかややこしいですが、xの前後の値 xixi1 との比率を計算し、比率をyに適用しているだけです。


■以下のような数列で、xが15の場合のyの値を求めてみます

最初にxが当てはまる場所を先頭から順番に比較して探します。
必ず、x軸は数字が小さい値から順番に並んでいる必要があります。


1000 + ( 500 - 1000 ) * ( 15 - 10 ) / ( 20 - 10 ) = 750

xが15の場合のyの値は750付近の値であると算出されます



▼上記数列からxの近似値を算出するプログラムを作成してみます


#include <stdio.h>

//線形補間のマクロ
#define INTERP(xi,xi1,yi,yi1,x) (yi + ((( yi1 - yi ) * ( x - xi )) / ( xi1 - xi )))

//数列
const int ar[]={
/* x */   10,  20,  30,  40,  50,  60,
/* y */ 1000, 500, 100, 300, 600, 300
};
//数列の横の長さ
const int ar_w=(sizeof(ar)/sizeof(ar[0]))/2;


//線形補間 x:補間する値 ar:数列 w:数列の横の長さ
int interp1dim(const int x,const int*ar,const int w){
	int i;
	//xの値が範囲外の場合はxが最大最小値の値を返す
	if(x<=ar[0]){
		return ar[w];
	}else if(x>=ar[w-1]){
		return ar[w*2-1];
	}
	
	for(i=1;i<w;i++){
		if(ar[i]>=x) break;
	}
	// y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
	return INTERP(ar[i-1],ar[i],ar[i+w-1],ar[i+w],x);
}

int main(){
	int i,j;
	for(i=10;i<=60;i++){
		j=interp1dim(i,ar,ar_w);
		printf("%d\t%d\n",i,j);
	}
	return 0;
}


▼実行結果

10	1000
11	950
12	900
13	850
14	800
15	750
16	700
17	650
18	600
19	550
20	500
21	460
22	420
23	380
24	340
25	300
26	260
27	220
28	180
29	140
30	100
31	120
32	140
33	160
34	180
35	200
36	220
37	240
38	260
39	280
40	300
41	330
42	360
43	390
44	420
45	450
46	480
47	510
48	540
49	570
50	600
51	570
52	540
53	510
54	480
55	450
56	420
57	390
58	360
59	330
60	300

▼元になっている数列と実行結果をエクセルでグラフを作成して確認してみます



線形補間が行われ、グラフの要素数が違うのに同一のグラフを描いていることが確認できます。




▼縦方向の線形補間


横方向の線形補間は上でやりましたが、こんな感じでした。



縦方向の線形補間はひっくり返すだけです。



ただ、縦横を組み合わせると2次元で線形補間できるっていうのがミソです。


▼2次元でのの線形補間

2次元での線形補間を行うにあたり、1次元のプログラムを修正します。
//線形補間 x:補間する値 ar:数列 w:数列の横の長さ
//offset_x:x軸の左側の距離1次元配列の場合には常に0 offset_y:x軸とy軸との距離、1次元配列の場合には常に1
int interp1dim(const int x,const int*ar,const int w,const int offset_x = 0,const int offset_y = 1){
	int i;
	//xの値が範囲外の場合はxが最大最小値の値を返す
	if(x<=ar[offset_x]){
		return ar[(w+offset_x)*(offset_y+1)-w];
	}else if(x>=ar[w+offset_x-1]){
		return ar[(w+offset_x)*(offset_y+1)-1];
	}
	
	for(i=1+offset_x;i<w+offset_x;i++){
		if(ar[i]>=x) break;
	}
	// y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
	return INTERP(ar[i-1],ar[i],ar[i+(w+offset_x)*offset_y-1],ar[i+(w+offset_x)*offset_y],x);
}
横軸、縦軸にオフセットを入れられるように改造しました。
上記関数を使って2次元で線形補間を行います。
#include <stdio.h>

//線形補間のマクロ
#define INTERP(xi,xi1,yi,yi1,x) (yi + ((( yi1 - yi ) * ( x - xi )) / ( xi1 - xi )))

//数列 最初の-1は数を合わせるためだけです どのような数字でも可能
const int ar[] = {
	-1,  10,  20,  30,  40,  50,
	10, 110, 120, 130, 140, 150,
	20, 210, 220, 230, 240, 250,
	30, 310, 320, 330, 340, 350,
	40, 410, 420, 430, 440, 450,
	50, 510, 520, 530, 540, 550,
	60, 610, 620, 630, 640, 650
};

//数列の横の長さ
const int ar_w = 5;
const int ar_h = 6;

//1次元の線形補間 x:補間する値 ar:数列 w:数列の横の長さ
//offset_x:x軸の左側の距離1次元配列の場合には常に0 offset_y:x軸とy軸との距離、1次元配列の場合には常に1
int interp1dim(const int x, const int*ar, const int w, const int offset_x = 0, const int offset_y = 1) {
	int i;
	//xの値が範囲外の場合はxが最大最小値の値を返す
	if (x <= ar[offset_x]) {
		return ar[(w + offset_x) * (offset_y + 1) - w];
	} else if (x >= ar[w + offset_x - 1]) {
		return ar[(w + offset_x) * (offset_y + 1) - 1];
	}

	for (i = 1 + offset_x; i < w + offset_x; i++) {
		if (ar[i] >= x) break;
	}
	// y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
	return INTERP(ar[i - 1], ar[i], ar[i + (w + offset_x) * offset_y - 1], ar[i + (w + offset_x) * offset_y], x);
}

//2次元の線形補間 x,y:補間する値 ar:数列 w:数列の横の長さ h:数列の縦の長さ
int interp2dim(const int x, int y, const int*ar, const int w, const int h) {
	int i, old = 0;
	const int offset_x = 1;
	const int offset_y = 1;
	const int ar_len = (w + offset_x) * (h + offset_y); //配列の大きさ
	const int ar_row = w + offset_x; //配列の一行毎の要素数
	for (i = ar_row; i < ar_len; i = i + w + offset_x) {
		if (ar[i] >= y) break;
		old = i;
	}
	if (0 == old) {
		old = i;
		y = ar[i]; //値が範囲から外れていた場合には最小最大値に置換する
	} else if (i > ar_len - ar_row) {
		i = old;
		y = ar[i]; //値が範囲から外れていた場合には最小最大値に置換する
	}
	//x軸 2軸の値を求める
	//i/ar_row  old/ar_row にてy軸オフセット値が計算できる
	int x1_label = ar[old];
	int x1 = interp1dim(x, ar, ar_w, offset_x, old / ar_row);
	int x2_label = ar[i];
	int x2 = interp1dim(x, ar, ar_w, offset_x, i / ar_row);

	if (x1_label == x2_label) { //ラベルが同じだと計算できないため
		return x1;
	} else {
		return INTERP(x1_label, x2_label, x1, x2, y);
	}
}

int main() {
	int i, j, pos;
	printf("\t");
	for (i = 5; i <= 65; i += 5) printf("[%d]\t", i);printf("\n");
	for (j = 5; j <= 65; j += 5) {
		printf("[%d]\t",j);
		for (i = 5; i <= 65; i += 5) {
			pos = interp2dim(i, j, ar, ar_w, ar_h);
			printf("%d\t", pos);
		}
		printf("\n");
	}
	return 0;
}
このプログラムを実行すると次の結果が出力されます。
       [5]  [10] [15][20][25][30] [35][40][45][50] [55][60][65]
[5]   110 110 115 120 125 130 135 140 145 150 150 150 150
[10] 110 110 115 120 125 130 135 140 145 150 150 150 150
[15] 160 160 165 170 175 180 185 190 195 200 200 200 200
[20] 210 210 215 220 225 230 235 240 245 250 250 250 250
[25] 260 260 265 270 275 280 285 290 295 300 300 300 300
[30] 310 310 315 320 325 330 335 340 345 350 350 350 350
[35] 360 360 365 370 375 380 385 390 395 400 400 400 400
[40] 410 410 415 420 425 430 435 440 445 450 450 450 450
[45] 460 460 465 470 475 480 485 490 495 500 500 500 500
[50] 510 510 515 520 525 530 535 540 545 550 550 550 550
[55] 560 560 565 570 575 580 585 590 595 600 600 600 600
[60] 610 610 615 620 625 630 635 640 645 650 650 650 650
[65] 610 610 615 620 625 630 635 640 645 650 650 650 650
Press any key to continue

なかなかデバックが難しく、赤色部分の一部を抜き出し手動で計算した結果と突き合わせて確認しました。
縦軸/横軸の値の変化に対して線形補間が行われます。
デバックが完了した所で、main関数を書き換えて補間した値を総当たりで書き出すようにしました。
int main() {
	int i, j, pos;
	FILE * fp;
	if((fp=fopen("test.txt","w"))==NULL){
		//エラー処理
	}else{
		for (j = 0; j < 100; j++) {
			for (i = 0; i < 100; i++) {
				pos = interp2dim(i, j, ar, ar_w, ar_h);
				fprintf(fp,"%d\n",pos);
			}
		}
		fclose(fp);
	}
	return 0;
}
書き出されたファイルを使えば
他の言語に移植した場合のデバックを簡易に行えるはずです。


■ VB6での線形補間

ちょっと必要があってVB6で線形補間をするハメになりました。
そこで、元となる配列を定義します。
しかし、VB6では静的な配列は定義できないっぽく要素を一つ一つ代入していくのもクソなので次のようにしました。
ただし、配列はVariantしか使えないし、コードのフォーマットは自動で整えられてしまいクソクソクソです。

数列を作成するためのテスト
Dim ar() As Variant

Private Sub Command1_Click()
    Debug.Print UBound(ar)
End Sub

Private Sub Form_Load()
ar = Array( _
    1, 2, 3, 4, 5, _
    6, 7, 8, 9, 10 _
    )
End Sub
出力結果は配列の最大要素数のインデックスである 9 です、要素数ではありません。
ちなみに、 ar(0) には 1 が入り、 ar(9) には 10 が入ります。

以上のC言語との挙動の違いを踏まえて、同様の動作をするようにプログラムを書き直しました。
Option Explicit

Dim ar() As Variant
Dim ar_w As Long

Private Sub Form_Load()
    '数列の作成
    ar = Array( _
    10, 20, 30, 40, 50, 60, _
    1000, 500, 100, 300, 600, 300 _
    )
    '数列の幅を計算
    ar_w = (UBound(ar) + 1) / 2
End Sub

'線形補間のマクロ
Private Function INTERP(xi As Long, xi1 As Long, yi As Long, yi1 As Long, x As Long) As Long
    INTERP = (yi + (((yi1 - yi) * (x - xi)) / (xi1 - xi)))
End Function


'線形補間 x:補間する値 ar:数列 w:数列の横の長さ
Private Function interp1dim(x As Long, ByRef ar_() As Variant, w As Long) As Long
    Dim i As Long
    'xの値が範囲外の場合はxが最大最小値の値を返す
    If x <= ar_(0) Then
        interp1dim = ar_(w)
        Exit Function
    ElseIf x >= ar_(w - 1) Then
        interp1dim = ar_(w * 2 - 1)
        Exit Function
    End If
    For i = 1 To (w - 1) Step 1
        If ar_(i) >= x Then Exit For
    Next i
    ' y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
    interp1dim = INTERP(CLng(ar_(i - 1)), CLng(ar_(i)), CLng(ar_(i + w - 1)), CLng(ar_(i + w)), x)
End Function

Private Sub Command1_Click()
    Dim i, j As Long
    For i = 10 To 60
        j = interp1dim(CLng(i), ar, ar_w)
        Debug.Print CStr(i) + vbTab + CStr(j)
    Next i
End Sub

▼ 時間を値として線形補間

時間の分の単位は60で繰り上がります、つまり60進数みたいなものです。
そのまま、適用するとおかしなことになりそうですので、
時間を値として線形補間をするには、分の値が10進数である必要がありそうです。
分の部分を60で割り、100を掛ける操作をすると、0〜99の値を取るはずです。
そこで、次のようなコードを書いて10進数に変換して確認しました。
Option Explicit
Private Sub Timer1_Timer()
    Dim str As String
    str = Format(Now, "HH:MM:ss")
    Dim m, h, v As Long
    m = CLng((CLng(Mid(str, 4, 2)) / 60) * 100)
    h = CLng(Mid(str, 1, 2) & "00")
    v = h + m
    Call logWrite(CStr(v), "test.txt")
End Sub

Private Sub logWrite(str As String, filename As String)
On Error Resume Next
    Dim fileNo As Integer
    fileNo = FreeFile
    Open App.Path + "\" + filename For Append As #fileNo
    Print #fileNo, Format(Now, "yyyy/mm/dd,hh:mm:ss,") + str
    Close #fileNo
On Error GoTo 0
End Sub
下半分はログファイルに書き出す関数です。
中心となるのは赤文字部分のみです。
このプログラムを実行して必要な部分を切り出しました。
2023/06/25,11:59:58,1198
2023/06/25,11:59:59,1198
2023/06/25,11:59:59,1198
2023/06/25,12:00:00,1200
2023/06/25,12:00:00,1200
2023/06/25,12:00:01,1200
値としては0〜98の値を取っているのを確認できます。


値が確認できましたので、時間を使って線形補間をしようと次のようなプログラムを作成しました
Option Explicit
Dim map1() As Variant
Dim map1_w As Long

Private Sub Form_Load()
    '数列の作成
    map1 = Array( _
    0, 1300, 1350, 1400, 1500, 2400, _
    100, 100, 500, 1500, 100, 100 _
    )
    '数列の幅を計算
    map1_w = (UBound(map1) + 1) / 2
End Sub

'線形補間のマクロ
Private Function INTERP(xi As Long, xi1 As Long, yi As Long, yi1 As Long, x As Long) As Long
    INTERP = (yi + (((yi1 - yi) * (x - xi)) / (xi1 - xi)))
End Function

'線形補間 x:補間する値 ar:数列 w:数列の横の長さ
Private Function interp1dim(x As Long, ByRef ar_() As Variant, w As Long) As Long
    Dim i As Long
    'xの値が範囲外の場合はxが最大最小値の値を返す
    If x <= ar_(0) Then
        interp1dim = ar_(w)
        Exit Function
    ElseIf x >= ar_(w - 1) Then
        interp1dim = ar_(w * 2 - 1)
        Exit Function
    End If
    For i = 1 To (w - 1) Step 1
        If ar_(i) >= x Then Exit For
    Next i
    ' y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
    interp1dim = INTERP(CLng(ar_(i - 1)), CLng(ar_(i)), CLng(ar_(i + w - 1)), CLng(ar_(i + w)), x)
End Function

Private Sub Timer1_Timer()
    Dim str As String
    str = Format(Now, "HH:MM:ss")
    Dim m, h, v, pos As Long
    m = CLng((CLng(Mid(str, 4, 2)) / 60) * 100)
    h = CLng(Mid(str, 1, 2) & "00")
    v = h + m
    pos = interp1dim(CLng(v), map1, map1_w)
    Call logWrite(CStr(pos), "test.txt")
End Sub

Private Sub logWrite(str As String, filename As String)
On Error Resume Next
    Dim fileNo As Integer
    fileNo = FreeFile
    Open App.Path + "\" + filename For Append As #fileNo
    Print #fileNo, Format(Now, "yyyy/mm/dd,hh:mm:ss,") + str
    Close #fileNo
On Error GoTo 0
End Sub
このプログラムを12時から15時まで走らせれば予想では次のような値を取るはずです。


タイマーのインターバルを500msにして動作させて得られた値のグラフが次です。


予想と同じ値を取っており、時間による線形補間がうまくいったことを確認できました。


▼ 年初からの経過日数を値として線形補間

年初からの経過日数を値として線形補間をしたいと思います。
そこで、VB6での一年の最初からの日数を返す関数を調べるとします。
日付けの計算はDateDiffで出来るようです。
Debug.Print DateDiff("d", "01/01", Format(Now, "mm/dd"))
戻り値はVariantのLongのようです。

次に、一年の経過日数と月の関連を調べます。

1月1日 年始からの経過日数 0
2月1日 年始からの経過日数 31
3月1日 年始からの経過日数 59
4月1日 年始からの経過日数 90
5月1日 年始からの経過日数 120
6月1日 年始からの経過日数 151
7月1日 年始からの経過日数 181
8月1日 年始からの経過日数 212
9月1日 年始からの経過日数 243
10月1日 年始からの経過日数 273
11月1日 年始からの経過日数 304
12月1日 年始からの経過日数 334
年始からの経過日数 364

DateDiff関数にうるう年の2月29を入力すると前回のうるう年からの日数2223を返しますので注意が必要です。
Debug.Print DateDiff("d", "01/01", "02/29")
ちなみに、2月28日の場合には58が返ります。
365よりも大きな値の場合には58に変更する事でうるう年の問題は解決できそうです。

上記の仕様に従いプログラムを作成しました。
Option Explicit
Dim map1() As Variant
Dim map1_w As Long

Private Sub Form_Load()
   '数列の作成
   '1月2月 3月 4月 5月  6月  7月  8月  9月  10月 11月 12月
   '0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
   '1, 10, 20, 30,  50,  80, 100, 300, 200,  10, 100, 150
   
    map1 = Array( _
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, _
    1, 10, 20, 30, 50, 80, 100, 300, 200, 10, 100, 150 _
    )
    '数列の幅を計算
    map1_w = (UBound(map1) + 1) / 2
End Sub

'線形補間のマクロ
Private Function INTERP(xi As Long, xi1 As Long, yi As Long, yi1 As Long, x As Long) As Long
    INTERP = (yi + (((yi1 - yi) * (x - xi)) / (xi1 - xi)))
End Function

'線形補間 x:補間する値 ar:数列 w:数列の横の長さ
Private Function interp1dim(x As Long, ByRef ar_() As Variant, w As Long) As Long
    Dim i As Long
    'xの値が範囲外の場合はxが最大最小値の値を返す
    If x <= ar_(0) Then
        interp1dim = ar_(w)
        Exit Function
    ElseIf x >= ar_(w - 1) Then
        interp1dim = ar_(w * 2 - 1)
        Exit Function
    End If
    For i = 1 To (w - 1) Step 1
        If ar_(i) >= x Then Exit For
    Next i
    ' y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
    interp1dim = INTERP(CLng(ar_(i - 1)), CLng(ar_(i)), CLng(ar_(i + w - 1)), CLng(ar_(i + w)), x)
End Function

Private Sub Timer1_Timer()
    Dim str As String
    str = Format(Now, "mm/dd")
    If str = "02/29" Or str = "2/29" Then str = "02/28"
    Dim d, pos As Long
    d = DateDiff("d", "01/01", str)
    pos = interp1dim(CLng(d), map1, map1_w)
    
    Call logWrite(CStr(pos), "test.txt")
End Sub

Private Sub logWrite(str As String, filename As String)
On Error Resume Next
    Dim fileNo As Integer
    fileNo = FreeFile
    Open App.Path + "\" + filename For Append As #fileNo
    Print #fileNo, Format(Now, "yyyy/mm/dd,hh:mm:ss,") + str
    Close #fileNo
On Error GoTo 0
End Sub
上記プログラムを走らせたらこのようなグラフが書けるはずです。

しかし、デバックの為にうるう年の年まで待って1年中プログラムを走らせるわけにもいきません。
最近のうるう年(2月29日)がある年は2020年だそうで、2020年を1月1日から12月31日までぐるっとパソコンの日付の設定を変化させるプログラムを作成するとします。
まずは、パソコン自体の時刻を自動的に設定するをOFFにする必要があります。
2020年1月1日から日付けを加算して変化させるプログラムを作成しました。
標準モジュール
Public i As Integer
フォーム
Private Sub Timer1_Timer()
    d = DateAdd("d", i, "2020/01/01")
    Date = d
    i = i + 1
End Sub
上記プログラムでパソコンの日付を変化させて、デバックの為のデータを出力したグラフです。

グラフは酷似していて動作は正しいです。
しかし、ふと気が付いたのが12月以降のデータがないため12月は直線のグラフを表しています。
マップに12月以降のデータを追加してみます。
   '数列の作成
   '1月2月 3月 4月 5月  6月  7月  8月  9月  10月 11月 12月 年末
   '0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366,
   '1, 10, 20, 30,  50,  80, 100, 300, 200,  10, 100, 150, 1
   
    map1 = Array( _
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366, _
    1, 10, 20, 30, 50, 80, 100, 300, 200, 10, 100, 150, 1 _
    )
この追加により12月31日の値と1月1日の値が連続するはずです。
これにより作成したグラフが次です。

プログラムの動作が正しい事を確認できました。


■ VB6 での2次元の線形補間

上の方で
C++で書かれたプログラムをVB6に変換しました。
ar = Array( _
	-1,  10,  20,  30,  40,  50, _
	10, 110, 120, 130, 140, 150, _
	20, 210, 220, 230, 240, 250, _
	30, 310, 320, 330, 340, 350, _
	40, 410, 420, 430, 440, 450, _
	50, 510, 520, 530, 540, 550, _
	60, 610, 620, 630, 640, 650 _
)
ar_w = 5;
ar_h = 6;
マップも同一の物を使用しており、動作確認の為のファイルへの出力結果も同一でした。
Option Explicit

Dim ar() As Variant
Dim ar_w As Long
Dim ar_h As Long

Private Sub Command1_Click()
    Dim fileNo As Integer
    fileNo = FreeFile
    Open App.Path + "\" + "test_VB6.txt" For Output As #fileNo
    Dim i, j, pos As Long
    
    For j = 0 To 100 - 1 Step 1
        For i = 0 To 100 - 1 Step 1
            pos = interp2dim(CLng(i), CLng(j), ar, ar_w, ar_h)
            Print #fileNo, CStr(pos)
        Next i
    Next j
    Close #fileNo
End Sub

Private Sub Form_Load()
    '数列 最初の-1は数を合わせるためだけです どのような数字でも可能
    ar = Array( _
        -1, 10, 20, 30, 40, 50, _
        10, 110, 120, 130, 140, 150, _
        20, 210, 220, 230, 240, 250, _
        30, 310, 320, 330, 340, 350, _
        40, 410, 420, 430, 440, 450, _
        50, 510, 520, 530, 540, 550, _
        60, 610, 620, 630, 640, 650 _
    )
    
    '数列の横の長さ
    ar_w = 5
    ar_h = 6
End Sub

'線形補間のマクロ
Private Function INTERP(xi As Long, xi1 As Long, yi As Long, yi1 As Long, x As Long) As Long
    INTERP = (yi + (((yi1 - yi) * (x - xi)) / (xi1 - xi)))
End Function

'1次元の線形補間 x:補間する値 ar:数列 w:数列の横の長さ
'offset_x:x軸の左側の距離1次元配列の場合には常に0 offset_y:x軸とy軸との距離、1次元配列の場合には常に1
Private Function interp1dim(x As Long, ByRef ar_() As Variant, w As Long, Optional offset_x As Long = 0, Optional offset_y As Long = 1) As Long
    Dim i As Long
    'xの値が範囲外の場合はxが最大最小値の値を返す
    If x <= ar_(offset_x) Then
        interp1dim = ar_((w + offset_x) * (offset_y + 1) - w)
        Exit Function
    ElseIf x >= ar_(w + offset_x - 1) Then
        interp1dim = ar_((w + offset_x) * (offset_y + 1) - 1)
        Exit Function
    End If

    For i = 1 + offset_x To w + offset_x - 1 Step 1
        If ar_(i) >= x Then Exit For
    Next i
    ' y=yi + (yi+1-yi)(x-xi)/(xi+1-xi) を行い値を返す
    interp1dim = INTERP(CLng(ar_(i - 1)), CLng(ar_(i)), CLng(ar_(i + (w + offset_x) * offset_y - 1)), CLng(ar_(i + (w + offset_x) * offset_y)), x)
End Function

'2次元の線形補間 x,y:補間する値 ar:数列 w:数列の横の長さ h:数列の縦の長さ
Private Function interp2dim(x As Long, y As Long, ByRef ar_() As Variant, w As Long, h As Long) As Long
    Dim i As Long
    Dim old As Long
    old = 0
    Dim offset_x, offset_y As Long
    offset_x = 1
    offset_y = 1
    Dim ar_len, ar_row As Long
    ar_len = (w + offset_x) * (h + offset_y) '配列の大きさ
    ar_row = w + offset_x                    '配列の一行毎の要素数
    For i = ar_row To ar_len - 1 Step w + offset_x
        If ar_(i) >= y Then Exit For
        old = i
        'i = i + w + offset_x
    Next i
    If 0 = old Then
        old = i
        y = ar_(i) '値が範囲から外れていた場合には最小最大値に置換する
    ElseIf i > ar_len - ar_row Then
        i = old
        y = ar_(i) '値が範囲から外れていた場合には最小最大値に置換する
    End If
    'x軸 2軸の値を求める
    'i/ar_row  old/ar_row にてy軸オフセット値が計算できる
    Dim x1_label, x1, x2_label, x2 As Long
    x1_label = ar_(old)
    x1 = interp1dim(x, ar_, w, CLng(offset_x), old / ar_row)
    x2_label = ar_(i)
    x2 = interp1dim(x, ar_, w, CLng(offset_x), i / ar_row)

    If x1_label = x2_label Then 'ラベルが同じだと計算できないため
        interp2dim = x1
        Exit Function
    Else
        interp2dim = INTERP(CLng(x1_label), CLng(x2_label), CLng(x1), CLng(x2), y)
        Exit Function
    End If
End Function


▲トップページ > プログラミングの実験