◆ザウルスで最小二乗近似曲線の評価をしよう
(2002.3.30)
ダウンロードは下と同じ
近似ができたら評価もしないと
近似式を求めるプログラムを掲載しましたが、実際にこの近似結果がどれくらい正しいのかを調べる方法があります。それは決定係数R^2を用いる方法です。
この決定係数R^2は 0〜1の値をとり、値が1に近づくほど近似式がもとのデータに近いことを表します。
決定係数は以下の式で求めます。
決定係数=Σ((Yi-Ya)*(Yi-Ya)) / Σ((yi-Ya)*(yi-Ya))
Yiは各データ値、Yaはデータの平均値、yiは近似式にデータXiを代入して計算した値で、決定係数は、元データから平均値を引いたものの2乗の合計を、近似式から求めたyの値から平均値を引いたものの2乗の合計で、割ったものです。
以下はプログラムです。
rem 決定係数R^2の計算
GAve=0
元データYの平均値を求めます。
For i=1 To N
GAve=GAve+Yi(i)
Next
GAve=GAve/N
Grbo=0
Grsi=0
このforループでデータ数分の合計値を求めます
For i=1 To N
Gf=0
Px=1
このforループで近似式にデータXを入れたその計算結果を求めています
For j=0 To GauN
Gf=Gf+Gaux(j+1,GauN+2)*Px
Px=Px*Xi(i)
Next
それぞれの2乗和を求めます
Grbo=Grbo+(Yi(i)-GAve)*(Yi(i)-GAve)
Grsi=Grsi+(Gf-GAve)*(Gf-GAve)
Next
ここで決定係数の計算しています
Gr=Grsi/Grbo
boxmove 0,0,0,12,240,144
locate 0,11:print 'R^2= ';
print Gr
また、決定係数の平方根を相関係数と呼びます。
決定係数 = 相関係数×相関係数
△
◆ザウルスで最小二乗近似曲線を求めよう
その2 (2002.3.30)
ダウンロード:trsclea0.lzh
その2ということで、プログラムの説明をしたいと思います。
前に書いた規則性は、
(1)1次曲線は式が2つで項が2個、2次曲線は式が3つで項が3個、3次曲線は式が4つで項が4個ということは、N次曲線は式がN+1で項がN+1個である。
(2)1式目の1項目は個数nである。
(3)N次曲線の1式目の2項目からN-2項目までの項の係数は、Xiの1乗,2乗,3乗・・・N-1乗である。
(4)N次曲線の各式のN番目の項の係数は、1式目がXiのN乗、2式目がN+1乗,
最後のN+1式目がN+N乗である。
(5)N次曲線の2式目以降の1項目からN-2項目までの係数は、1つ前の式の1つ後の項と同じである。
(6)N次曲線のとき、各式の右辺はYi*Xi^0〜Yi*Xi^Nである。
の6つでした。
それではプログラムしましょう。
規則性で書いたN次曲線のNはプログラムでは用いず、GauNという変数を項の数(N+1)として使っています。
Nはデータの個数になります。
GauX(1,1)=N
このループでXiやYiのΣ(和)の計算を行います
for i=1 to N
このif文は944BASICではfor文のループ条件が偽でも1回はループを通ってしまうため、それをさけています
if GauN-2<1 then goto *loops10
Px=1
このforループでは規則性(3)番にしたがって係数を求めています
for j=1 to GauN-2
Px=Px*Xi(i)
GauX(1,j+1)=GauX(1,j+1)+Px
next
*loops10
Px=1
このif文は944BASICではfor文のループ条件が偽でも1回はループを通ってしまうため、それをさけています
if GauN<=2 then goto *loops11
このforループでは規則性(4)番の「1式目がXiのN乗(N次曲線)」の前計算をしています
for j=3 to GauN
Px=Px*Xi(i)
next
*loops11
このforループでは規則性(4)番にしたがって係数を求めています
for j=1 to GauN
Px=Px*Xi(i)
GauX(j,GauN)=GauX(j,GauN)+Px
next
Px=1
このforループでは規則性(6)番にしたがって右辺の係数を求めています
for j=0 to GauN-1
GauX(j+1,GauN+1)=GauX(j+1,GauN+1)+Yi(i)*Px
Px=Px*Xi(i)
next
next
このforループでは規則性(5)番にしたがって係数の代入をしています
for i=2 to GauN
for j=1 to GauN-1
GauX(i,j)=GauX(i-1,j+1)
next
next
連立方程式をとくプログラムに飛んでガウス消去を行います。
gosub *Gaus
ここから以下は、計算結果を表示するプログラムです。
GauN=GauN-1
boxmove 0,0,0,12,240,144
locate 0,11:print ' ';
print GauN;
print ' 次曲線の近似式'
boxmove 0,0,0,12,240,144
locate 0,11:print 'Y=%0';
for i=1 to GauN
print '+%';
print i;
print '*X^';
print i;
next
for i=0 to GauN
boxmove 0,0,0,12,240,144
locate 0,11:print '%';
print i;
print '= ';
print GauX(i+1,GauN+2)
next
以上で、線形の方程式の最小二乗近似を行うプログラムの説明を終ります。
線形にさえ式を変更できれば、expやlogがあってもこのプログラムを用いて最小二乗近似を行うことができます。
今回は、944BASICがまだ単精度実数までしか対応していないため、4次曲線の近似までしか対応していません。現在倍精度実数に対応した944BASICができつつあるそうです。そうすればもっと高次の曲線まで対応できるかもしれません。
△
◆ザウルスで最小二乗近似曲線を求めよう
その1 (2002.3.23)
連立方程式を解くプログラムを作ったけれど、あまり使い道が無いので、折角だから使えるように、技術系なら必ず通る最小二乗法近似のプログラムを作りました。最小二乗近似を行うときに、実は連立方程式を解くんです。はい。
ところで、最小二乗近似とは?から説明したいと思います。
いくつかのデータ、まぁXとYがペアになっているものがいくつかあるときに、このXとYの関係があるかどうか、もしあるのなら、それはどういう関係か?XとYでグラフを書いたときに、もし、1直線に並ぶなら、それは直線に比例の関係であるといえます。
じゃあ、その直線はどんな直線か?一本の線上にXとYが並ぶのか?少しずれているのか?ずれているとしたら、どれくらいずれているのか?じゃぁ、一番ずれていないのはどういった直線か?XとYのデータに一番近い直線があるのか?
これらの問いかけに答えを出すのが、最小二乗近似です。
ようは、データXとYがあったときに、一番XとYの関係近い直線(もしくは曲線)を計算で求めましょ。というのが最小二乗近似です。
では、どうやって求めるか?早速やってみましょう。
最初は直線の話しをしましょう。直線とは1次曲線ともいえます。式はY=b+aXという式が一般式です。ここで、aとbが直線を決める係数です。aとbが求まると直線の式が決まります。
データX0とY0があったときに、直線Y=b+aXに入れてみます。Y0=a+bX0と書くことができますが、実はY0とa+bX0が=でつながるようなaとbになることはまれです。実際には、差が出てきます。つまり、Y0-a-bX0=0ではないことが多いです。しかも、この組み合わせはXとYの数だけあります。Y1-a-bX1,
Y2-a-bX2......。
じゃあ、0にならないにしろ、一番小さくなる場合の組み合わせを探せば、XとYの関係に一番近いY=a+bX式を求めることができるじゃないか・・・。一番小さくなるものが一番近いということで、最小二乗近似という所以です。二乗の説明ができてませんので、もうちょっと・・・。
Y0-a-bX0の式をXとYのデータがi個あるということで、Yi-a-bXiという風にまとめて書きます。これらの計算結果が最小になるといっても、引き算なので、結果が正になったり負になったりします。これでは最小が決まらないので、必ず結果が正になるように工夫します。そう二乗するのです(Yi-a-bXi)^2こうすると、必ず正の数になります。そこで、このXとYのデータを用いて、上の引き算を全て行って二乗して、それらを全部足してその結果が一番小さいものを求めてやれば、それが一番XとYの関係に近いY=a+bXの式になります。
すなわち、Σ(Yi-a-bXi)^2が一番小さくなれば良いということです。ΣはXとYのデータがn個あるときに、i=1からnまでのデータをΣで括られた式を計算して和をとることを示します。
ここで、考えます。
2乗の式が一番小さいところは何か? それは極小の所ですね。この二乗の式はデータが増えるほど値が大きくなっていきますから、下に凸を持つすなわち極小を持つ2次曲線だと言うことが解ります。ということは、極小のところ、そう、微分すると0になるところが上の二乗の式の一番小さいところです。ここまでくれば、微分すれば良いだけですね(^^)v。
S=Σ(Yi-a-bXi)^2として、二乗を展開すると、
S=Σ(Yi^2+a^2+b^2Xi^2-2Yia-2XiYib+2Xiab)となります。
この式をaとbで偏微分します。
∂S/∂a = Σ(2a-2Yi+2Xib)
∂S/∂b = Σ(2Xi^2b-2XiYi+2Xia)
そして、極小を求めるので、微分した結果が0になるところを探します。だから、上の2つの微分値が両方とも0になるところを探せば良いのです。これで方程式を作ります。
Σ(2a-2Yi+2Xib) = 0 シグマを展開して、
2Σna-2ΣYi+2ΣXib = 0
2Σna + 2ΣXib = 2ΣYi
Σna + ΣXib = ΣYi ・・・(1)
Σ(2Xi^2b-2XiYi+2Xia)=0 シグマを展開して、
2ΣXi^2b-2ΣXiYi+2ΣXia=0
2ΣXia + 2ΣXi^2b = 2ΣXiYi
ΣXia + ΣXi^2b = ΣXiYi ・・・(2)
Σn a + ΣXi b = ΣYi
・・・(1)
ΣXia + ΣXi^2b = ΣXiYi ・・・(2)
ここで、Σn, ΣXi, ΣYi, ΣXi, ΣXi^2, ΣXiYi はXとYのデータから計算して求めれば良いので、上の(1)(2)式の2元連立方程式をとけば、aとbを求めることができます。しかも、求めたaとbを用いてできる直線の式
Y=a+bX は、もとのデータXとYに最も近い直線ということになります。これが、最小二乗近似の基本です。
そして、ここで連立方程式を解くことになるので、先に作った連立方程式の解法のプログラムが思いっきり役に立つのです。わーいわーい(^^)/。
それでは、さらに、話しを進めます。何をすすめるかって、直線(1次曲線)の次は2次曲線と決まっています。ではでは、2次曲線は
Y=a+bX+cX^2です。やっぱり同じで、
Y-a-bX-cX^2を二乗して総和をとる式を作ります。
S=Σ(Yi-a-bXi-cXi^2)^2
S=Σ(Yi^2+a^2+Xi^2b^2+Xi^4c^2-2Yia-2YiXib-2YiXi^2c+2Xiab+2Xi^2ac+2Xi^3bc)
この式をaとbとcで偏微分します。
∂S/∂a = Σ(2a-2Yi+2Xib+2Xi^2c)
∂S/∂b = Σ(2Xi^2b-2YiXi+2Xia+2Xi^3c)
∂S/∂c = Σ(2Xi^4c-2YiXi^2+2Xi^2a+2Xi^3b)
これらを=0として展開します。
Σ(2a-2Yi+2Xib+2Xi^2c) = 0
2Σna-2ΣYi+2ΣXib+2ΣXi^2c = 0
2Σna+2ΣXib+2ΣXi^2c = 2ΣYi
Σna + ΣXib + ΣXi^2c = ΣYi ・・・(1)
Σ(2Xi^2b-2YiXi+2Xia+2Xi^3c) = 0
2ΣXi^2b-2ΣYiXi+2ΣXia+2ΣXi^3c = 0
2ΣXi^2b+2ΣXia+2ΣXi^3c = 2ΣYiXi
ΣXi^2b + ΣXia + ΣXi^3c = ΣYiXi
ΣXia + ΣXi^2b + ΣXi^3c = ΣYiXi ・・・(2)
Σ(2Xi^4c-2YiXi^2+2Xi^2a+2Xi^3b) = 0
2ΣXi^4c-2ΣYiXi^2+2ΣXi^2a+2ΣXi^3b = 0
2ΣXi^4c+2ΣXi^2a+2ΣXi^3b = 2ΣYiXi^2
ΣXi^4c + ΣXi^2a + ΣXi^3b = ΣYiXi^2
ΣXi^2a + ΣXi^3b + ΣXi^4c = ΣYiXi^2 ・・・(3)
すると、3つの式ができます。
Σna + ΣXib
+ ΣXi^2c = ΣYi ・・・(1)
ΣXia + ΣXi^2b + ΣXi^3c = ΣYiXi
・・・(2)
ΣXi^2a + ΣXi^3b + ΣXi^4c = ΣYiXi^2 ・・・(3)
なるほど・・・。じゃあ3次曲線(Y=a+bX+cX^2+dX^3)は?
4つの式ができます。
Σna + ΣXib
+ ΣXi^2c + ΣXi^3d = ΣYi ・・・(1)
ΣXia + ΣXi^2b + ΣXi^3c + ΣXi^4d
= ΣYiXi ・・・(2)
ΣXi^2a + ΣXi^3b + ΣXi^4c + ΣXi^5d = ΣYiXi^2 ・・・(3)
ΣXi^3a + ΣXi^4b + ΣXi^5c + ΣXi^6d = ΣYiXi^3 ・・・(4)
ここまで来ると、方程式の規則性が解ってきますよね。この規則性をプログラミングしてしまえば、何次式であろうと、XとYのデータに関して、最小二乗近似を行うことができます。
規則性とは、
(1)1次曲線は式が2つで項が2個、2次曲線は式が3つで項が3個、3次曲線は式が4つで項が4個ということは、N次曲線は式がN+1で項がN+1個である。
(2)1式目の1項目は個数nである。
(3)N次曲線の1式目の2項目からN-2項目までの項の係数は、Xiの1乗,2乗,3乗・・・N-1乗である。
(4)N次曲線の各式のN番目の項の係数は、1式目がXiのN乗、2式目がN+1乗,
最後のN+1式目がN+N乗である。
(5)N次曲線の2式目以降の1項目からN-2項目までの係数は、1つ前の式の1つ後の項と同じである。
(6)N次曲線のとき、各式の右辺はYi*Xi^0〜Yi*Xi^Nである。
これをプログラムすれば良い。
疲れたので、今日はここまでぇ・・・・。
△
◆ザウルスで連立方程式を解こう
(2002.3.20)
ダウンロード:trscren1.lzh
祝MI-E25DC購入記念!ということで、ザウルスで連立方程式を解くプログラムを作りました。
何の役に立つかって・・、ううん。これだけでは役に立たないでしょう(^^;。
プログラムを組むときに、2元の方程式は役に立つかも・・・。ということで、このプログラムはもし、ザウルスで実験のデータ整理等を行うときに最小二乗法で線形近似を行うときに一番使われるでしょう・・・。たぶん?
前のバージョンと基本的には変わっていませんが、方程式の確認を1つの式ごとに行うように変えました。これで、入力ミスがあったときの訂正が楽になるはずです。
ところで、連立方程式とは、例えば3つの変数a,b,cがあった時に、このa,b,cに当てはまる答えを求めるための式のことを言います。3つ変数があるときには、式は3ついります。下のような式があったとします。
1a+1b+1c=60
3a+2b+1c=40
6a+3b-2c=50
これを3元連立一次方程式といいます。この式を満足するa,b,cの値は、
a = -135
b = 250
c = -55
となります。
これをザウルスで解きます。方法はガウス消去法を用います。
*loops0 sync
boxmove 0,0,0,12,240,144
最初に何元の連立方程式を解くかを入れます。
locate 0,11:print '何元連立方程式ですか?( 2 〜 6 )'
NumInX=3:NumInY=13:NvN=10:gosub *NumInput
boxmove 0,0,0,12,240,144
locate 0,11:print '> ';
print ValV
if (ValV=1)+(ValV>6) then goto *loops0
0だったらプログラム終了
if ValV<=0 then end
n=ValV:m=n+1
係数をx(i,j)の配列に代入していきます。
for i=1 to n
boxmove 0,0,0,12,240,144
*loops6
for j=1 to m
boxmove 0,0,0,12,240,144
locate 1,11:print i;
print '式の ';
print j;
print '項目の係数入力'
NumInX=3:NumInY=13:NvN=10:gosub *NumInput
x(i,j)=ValV
boxmove 0,0,0,12,240,144
locate 0,11:print ' =';
print ValV
next
boxmove 0,0,0,12,240,144
boxmove 0,0,0,12,240,144
入力した連立方程式を表示し、確認します。
locate 0,11
print x(i,1);
print a(1);
for j=2 to m
if j<>m then else goto *loops4
if x(i,j)<0 then else goto *loops2
print x(i,j);
goto *loops3
*loops2
print '+';
print x(i,j);
*loops3
print a(j);
goto *loops5
*loops4
print '=';
print x(i,j)
*loops5
next
boxmove 0,0,0,12,240,144
locate 0,11:print 'この式でいいですか?(1:はい,2:いいえ)'
NumInX=3:NumInY=13:NvN=10:gosub *NumInput
if ValV=1 then goto *loops7
goto *loops6
*loops7
next
もう一度、方程式を表示します
rem 方程式の表示
boxmove 0,0,0,12,240,144
boxmove 0,0,0,12,240,144
locate 0,11:print '連立方程式'
for i=1 to n
boxmove 0,0,0,12,240,144
locate 0,11
print x(i,1);
print a(1);
for j=2 to m
if j<>m then else goto *loops14
if x(i,j)<0 then else goto *loops12
print x(i,j);
goto *loops13
*loops12
print '+';
print x(i,j);
*loops13
print a(j);
goto *loops15
*loops14
print '=';
print x(i,j)
*loops15
next
next
ガウス消去を行うメインルーチンです。
rem ガウス消去ルーチン
For k=1 To n
p=x(k,k)
For j=k To m
x(k,j)=x(k,j)/p
Next
For i=1 To n
If (i-k)<>0 Then else goto *loops1
y=x(i,k)
For j=k To m
x(i,j)=x(i,j)-y*x(k,j)
Next
*loops1
Next
Next
ガウス消去の終了、x(i,m)に各変数の答えが入っています。
rem 答えの表示
boxmove 0,0,0,12,240,144
locate 0,11:print 'の解答'
for i=1 to n
boxmove 0,0,0,12,240,144
locate 0,11:print ' ';
print a(i);
print ' = ';
print x(i,m)
next
boxmove 0,0,0,12,240,144
goto *loops0
END
・多々ある計算誤差は、お許し下さい。m(_ _)m
△
◆入力を考える5
キーボードからも入力 (2002.3.20)
MI-E25DCを買って使っていると、透明フタを開けて使うのが面倒になって来る。でもフタを開けないと数字の入力ができない・・・。これは不便だ!ということで、数字の入力について、ペンだけでなく、キーボードからも入力ができるように、数字キー入力ルーチンを改造した。
キーボタンの対応
数字キー・・・・・キーボード上段の数字キー
マイナス符号・・・下段左のマイナスキー
小数点・・・・・・3段目右の?キー
後退キー・・・・・2段目右の後退キー
入力キー・・・・・下段右の改行キーと入力キー
クリアキー・・・・3段目の英字Cキー
Expキー・・・・・ 3段目の英字Xキー
REM *******************************
REM * 数字キー入力
*
REM * 戻り値 Ncode >文字コード *
REM *******************************
REM NumKeyX,NumKeyY NumKeyの表示位置座標
REM ↓ペンまたはキーがタッチされるまで待つ
*INPUTNUMKEY SYNC
IF PEN=2 THEN GOTO *INPUTNK01
ペンタッチを待ちながらINKEYでキー入力も待つようにした。
INPUK=INKEY
IF (INPUK<>0)*(INPUK<>'') THEN
ELSE GOTO *INPUTNK04
キーが押されたら押されたキーのチェックに行く。そして、Ncodeが代入されて戻る
GOSUB *KEYBTCHECK
RETURN
*INPUTNK04
GOTO *INPUTNUMKEY
これ以降は改造無し
*INPUTNK01
TAPX=PENX-NUMKEYX
TAPY=PENY-NUMKEYY
GOSUB *TAPCHECK
NCODE=TCODE:BOT=1:GOSUB *BOTACT
REM ↓ペンが上げられるまで待つ
*INPUTNK02 SYNC
IF PEN=3 THEN GOTO *INPUTNK03
GOTO *INPUTNK02
*INPUTNK03
BOT=0:GOSUB *BOTACT
TAPX=PENX-NUMKEYX
TAPY=PENY-NUMKEYY
GOSUB *TAPCHECK
REM タッチ位置からずれてペンが上がったら、無効:Ncode=255
IF TCODE<>NCODE THEN NCODE=255
RETURN
新規にキーボタンチェックを作った。
REM *******************************
REM * キーボタンチェック
*
REM * 戻り値 Ncode
*
REM * 押された場所の文字コード *
REM *******************************
*KEYBTCHECK
それぞれ、キーコードに対して数字のNcodeを返しているだけ
IF INPUK=85 THEN NCODE=55:RETURN
IF INPUK=82 THEN NCODE=52:RETURN
IF INPUK=81 THEN NCODE=49:RETURN
IF INPUK=80 THEN NCODE=48:RETURN
IF INPUK=73 THEN NCODE=56:RETURN
IF INPUK=84 THEN NCODE=53:RETURN
IF INPUK=87 THEN NCODE=50:RETURN
IF INPUK=46 THEN NCODE=46:RETURN
IF INPUK=79 THEN NCODE=57:RETURN
IF INPUK=89 THEN NCODE=54:RETURN
IF INPUK=69 THEN NCODE=51:RETURN
IF INPUK=45 THEN NCODE=45:RETURN
IF INPUK=8 THEN NCODE=8:RETURN
IF INPUK=67 THEN NCODE=12:RETURN
IF INPUK=88 THEN NCODE=69:RETURN
IF (INPUK=767)+(INPUK=13) THEN NCODE=13:RETURN
NCODE=255
RETURN
これで、透明フタを開けずに済むので、とても便利です。
コードは、連立方程式の解v0.01のプログラム中にあります。
△
-
◆数字表示とスクロール
(2002.3.16)
-
ダウンロード:trscdsp0.LZH
-
数字の桁区切り表示をするルーチンを作りました。それと、画面のスクロールもさせて見ました。
-
-
*loops0 sync
-
NumInX=3:NumInY=13:NvN=10:gosub *NumInput
-
boxmove 0,0,0,12,240,144 ←これで画面のスクロールをしています。
-
locate 0,11:print '>';
-
NumcV=ValV:gosub *NUMCPRINT
-
goto *loops0