十進 BASIC 「に」連立 1 次方程式を「解かせる」のではない。
電卓のように,十進 BASIC 「で」連立 1 次方程式を「解く」のである。
もちろん,フルオートで連立 1 次方程式を解くプログラムを作ることはできるし,再帰的な繰り返し処理の手ごろな演習になりそうなのでぜひやってみたいところでもある。
けれども,本記事の趣旨は,手計算では計算ミスが多発する掃き出し計算の労苦を肩代わりしてくれる電卓的なプログラムを作るところにある。
前回の投稿記事で,十進 BASIC の有理数モードを利用して,その長年(?)の夢をようやく実現できたという報告を書いた。
今回は,講義中において学生に演示することを企図し,そのプログラムを問題の係数行列と基本変形のコマンドを入力していくインタプリタ版へと改造してみた。
係数行列の入力方法としては,MAT INPUT 文というとても便利そうなコマンドの使用も検討したが,次のような理由で今回は見送った。
MAT INPUT 文で PROMPT オプションがあるかどうかはヘルプを見てもわからなかったが,他の INPUT 文について書かれた説明を参考に
LET K=1
MAT INPUT PROMPT "第" & STR$(K) & "行の成分を入力して下さい:" :A
のようなプログラムを試したところ,ちゃんと機能することが確認できた。
ただし,この例として挙げた各行ごとの成分の入力を促す雰囲気を醸した PROMPT メッセージは意味がなく,MAT INPUT 文を使用した場合は行列 A の成分を一度にすべて入力しなければならない。
むしろ逆にそっちの方が使い勝手が良さそうな気もするが,まずは1行ごとに成分入力を促していくスタイルを試してみることとした。
なお,計算終了のコマンドは "0" で,"-1" と入力すると係数行列のデータはそのままで,掃き出し計算だけを最初からやり直せるようにしてある。
こうしてみると,行列 A の成分を入力するところからのやり直しもできるようにした方がより使い勝手は良くなりそうにも思える。
まあ,このあたりは Undo や Redo の機能を持たせて使い勝手をより良いものにしていくという,ユーザビリティ向上の話であって,「とりあえず期待通りに動いた!わあい!」というレベルからするともっと先の課題である。そうしたこだわりは今回は見送った。
それにしても,「こういう機能のアプリがあったらいいな」と夢想し,ググってみてもうまく探し出せないとき,
無いのなら 作ってしまおう ホトトギス
という工学精神で所望のものが作れるのがプログラミングの魅力であろう。
そして,それを実現する作業は大変だが,楽しい。
今回の件をクリアしたおかげで,命令が
LOAD, STORE, SUBTRUCT, JUMP MINUS, STOP
の5種類しかない,おそらく最も貧弱なアセンブラ言語のシミュレータ(エミュレータ?)を作るというここ数年胸に抱いている夢の実現へ向け,大きな一歩を踏み出せたように思う。
今回のソースコードも参考に挙げておく。
なお,GOTO 文を三か所で使用したが,使わずに済ませる方法があるかどうか,考えていない。
電卓のように,十進 BASIC 「で」連立 1 次方程式を「解く」のである。
もちろん,フルオートで連立 1 次方程式を解くプログラムを作ることはできるし,再帰的な繰り返し処理の手ごろな演習になりそうなのでぜひやってみたいところでもある。
けれども,本記事の趣旨は,手計算では計算ミスが多発する掃き出し計算の労苦を肩代わりしてくれる電卓的なプログラムを作るところにある。
前回の投稿記事で,十進 BASIC の有理数モードを利用して,その長年(?)の夢をようやく実現できたという報告を書いた。
今回は,講義中において学生に演示することを企図し,そのプログラムを問題の係数行列と基本変形のコマンドを入力していくインタプリタ版へと改造してみた。
係数行列の入力方法としては,MAT INPUT 文というとても便利そうなコマンドの使用も検討したが,次のような理由で今回は見送った。
MAT INPUT 文で PROMPT オプションがあるかどうかはヘルプを見てもわからなかったが,他の INPUT 文について書かれた説明を参考に
LET K=1
MAT INPUT PROMPT "第" & STR$(K) & "行の成分を入力して下さい:" :A
のようなプログラムを試したところ,ちゃんと機能することが確認できた。
ただし,この例として挙げた各行ごとの成分の入力を促す雰囲気を醸した PROMPT メッセージは意味がなく,MAT INPUT 文を使用した場合は行列 A の成分を一度にすべて入力しなければならない。
むしろ逆にそっちの方が使い勝手が良さそうな気もするが,まずは1行ごとに成分入力を促していくスタイルを試してみることとした。
なお,計算終了のコマンドは "0" で,"-1" と入力すると係数行列のデータはそのままで,掃き出し計算だけを最初からやり直せるようにしてある。
こうしてみると,行列 A の成分を入力するところからのやり直しもできるようにした方がより使い勝手は良くなりそうにも思える。
まあ,このあたりは Undo や Redo の機能を持たせて使い勝手をより良いものにしていくという,ユーザビリティ向上の話であって,「とりあえず期待通りに動いた!わあい!」というレベルからするともっと先の課題である。そうしたこだわりは今回は見送った。
それにしても,「こういう機能のアプリがあったらいいな」と夢想し,ググってみてもうまく探し出せないとき,
無いのなら 作ってしまおう ホトトギス
という工学精神で所望のものが作れるのがプログラミングの魅力であろう。
そして,それを実現する作業は大変だが,楽しい。
今回の件をクリアしたおかげで,命令が
LOAD, STORE, SUBTRUCT, JUMP MINUS, STOP
の5種類しかない,おそらく最も貧弱なアセンブラ言語のシミュレータ(エミュレータ?)を作るというここ数年胸に抱いている夢の実現へ向け,大きな一歩を踏み出せたように思う。
解きたい方程式の未知数の個数 n を入力して下さい:n=3 方程式の本数 m を入力して下さい:m=3 拡大係数行列の第1行の成分を入力して下さい:1,2,-2,5 拡大係数行列の第2行の成分を入力して下さい:1,3,-2,7 拡大係数行列の第3行の成分を入力して下さい:-1,-2,5,-2 解きたい方程式の拡大係数行列は 1 2 -2 5 1 3 -2 7 -1 -2 5 -2 基本変形のコマンドを入力して下さい:1,r2,r1,-1 第 2 行に第 1 行の -1 倍を加える; 1 2 -2 5 0 1 0 2 -1 -2 5 -2 基本変形のコマンドを入力して下さい:1,r3,r1,1 第 3 行に第 1 行の 1 倍を加える; 1 2 -2 5 0 1 0 2 0 0 3 3 基本変形のコマンドを入力して下さい:2,r3,1/3 第 3 行を1/3 倍する; 1 2 -2 5 0 1 0 2 0 0 1 1 基本変形のコマンドを入力して下さい:1,r1,r3,2 第 1 行に第 3 行の 2 倍を加える; 1 2 0 7 0 1 0 2 0 0 1 1 基本変形のコマンドを入力して下さい:1,r1,r2,-2 第 1 行に第 2 行の -2 倍を加える; 1 0 0 3 0 1 0 2 0 0 1 1 基本変形のコマンドを入力して下さい:0 これですべて(方程式は)解けた!
今回のソースコードも参考に挙げておく。
なお,GOTO 文を三か所で使用したが,使わずに済ませる方法があるかどうか,考えていない。
REM 有理数の範囲で 3元3立1次方程式を基本変形のコマンドを与えながら解く。 OPTION ARITHMETIC RATIONAL REM 解きたい連立方程式の拡大係数行列 を A(m,n), REM そのコピーを B(m,n) とする。 REM 計算用の補助配列として行ベクトル C(n) を用意しておく。 INPUT PROMPT "解きたい方程式の未知数の個数 n を入力して下さい:n=":Unknown INPUT PROMPT "方程式の本数 m を入力して下さい:m=":Equations DIM A(Equations,Unknown+1) DIM B(Equations,Unknown+1) DIM C(Unknown+1) REM 拡大係数行列の成分は整数値で与えられるものとする。 FOR K=1 TO Equations LINE INPUT PROMPT "拡大係数行列の第" & STR$(K) & "行の成分を入力して下さい:": ROW$ FOR KK=1 TO Unknown LET POSC=POS(ROW$,",") LET A(K,KK)=VAL(ROW$(1:POSC-1)) LET ROW$=ROW$(POSC+1:LEN(ROW$)) NEXT KK LET A(K,Unknown+1)=VAL(ROW$) NEXT K 100 PRINT "解きたい方程式の拡大係数行列は" MAT PRINT A REM 拡大係数行列の成分を元データ A から計算用の配列 B へとコピーする。 FOR m=1 TO Equations FOR n=1 TO Unknown+1 LET B(m,n)=A(m,n) NEXT n NEXT m REM 基本変形のコマンドの書式は次の通り。 REM コマンドの最初の数字は基本変形の種類の識別子である。 REM "1,rI,rJ,L": 第 I 行に第 J 行の L 倍を加える。 REM "2,rI,L": 第 I 行を L 倍する。 REM "3,rI,rJ": 第 I 行と第 J 行を入れ替える。 REM "0": 計算を終了する。 REM "-1": 基本変形を最初からやり直す。 200 LINE INPUT PROMPT "基本変形のコマンドを入力して下さい:":S$ REM 第 I 行に第 J 行の L 倍を加える IF S$(1:1)="1" THEN LET I=VAL(S$(4:4)) LET J=VAL(S$(7:7)) REM 文字列ロンダリング (laundering)。 REM 係数 L を文字列から数値に変換する LET SUBS$=S$(9:LEN(S$)) LET FRAC=POS(SUBS$,"/") IF FRAC=0 THEN LET L=VAL(SUBS$) ELSE LET NUMERATOR=VAL(SUBS$(1:FRAC-1)) LET DENOMINATOR=VAL(SUBS$(FRAC+1:LEN(SUBS$))) LET L=NUMERATOR/DENOMINATOR END IF FOR n=1 TO Unknown+1 LET C(n)=L*B(J,n) LET B(I,n)=B(I,n)+C(n) NEXT n PRINT "第";I;"行に第";J;"行の ";S$(9:LEN(S$));" 倍を加える;" REM 第 I 行を L 倍する ELSEIF S$(1:1)="2" THEN LET I=VAL(S$(4:4)) REM 係数 L を文字列から数値に変換する LET SUBS$=S$(6:LEN(S$)) LET FRAC=POS(SUBS$,"/") IF FRAC=0 THEN LET L=VAL(SUBS$) ELSE LET NUMERATOR=VAL(SUBS$(1:FRAC-1)) LET DENOMINATOR=VAL(SUBS$(FRAC+1:LEN(SUBS$))) LET L=NUMERATOR/DENOMINATOR END IF FOR n=1 TO Unknown+1 LET B(I,n)=L*B(I,n) NEXT n PRINT "第";I;"行を";S$(6:LEN(S$));" 倍する;" ELSEIF S$(1:1)="3" THEN LET I=VAL(S$(4:4)) LET J=VAL(S$(7:7)) FOR n=1 TO Unknown+1 LET C(n)=B(I,n) LET B(I,n)=B(J,n) LET B(J,n)=C(n) NEXT n PRINT "第";I;"行と第";J;"列を入れ替える;" ELSEIF S$(1:1)="0" THEN GOTO 300 ELSEIF S$(1:1)="-" THEN PRINT "掃き出し計算をやり直します。" GOTO 100 END IF MAT PRINT B GOTO 200 300 PRINT "これですべて(方程式は)解けた!" END
※コメント投稿者のブログIDはブログ作成者のみに通知されます