ページ 1 / 1
数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月03日(土) 13:19
by 関数男
任意の数学関数、例えばY=AX+Bの式をエディトボックスに書き、さらに
第2のエディトボックスにXを与えると、Yの値を計算して表示する、
ということをやりたいのですが、プログラムをどう書けばよいのか全く見当
がつきません。どなたか教えて下さい。使いたい数学関数はべき乗,sin,cos,
など関数電卓にある程度の初等関数の範囲です。
Re: 数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月03日(土) 13:59
by Sinryow
関数男 さんが書きました:任意の数学関数、例えばY=AX+Bの式をエディトボックスに書き、さらに
第2のエディトボックスにXを与えると、Yの値を計算して表示する、
ということをやりたいのですが、プログラムをどう書けばよいのか全く見当
がつきません。どなたか教えて下さい。使いたい数学関数はべき乗,sin,cos,
など関数電卓にある程度の初等関数の範囲です。
これはかなり難しい問題です。私も挑戦していますが何度も挫折しています。
ただ,これができれば最終的にはインタプリタやコンパイラへの発展も可能ですがね。
Posted: 2005年12月03日(土) 14:49
by Uhsp
一年ほど前逆ポーランドの解説用に適当に作ったHSP3のサンプルですが
ActiveBasicではスタックなども楽に扱えそうなので参考になれば幸いです。
コード: 全て選択
;==============================================================================
sdim Sinp
screen 0,320,200
ss="" :objsize 40,19
pos 10,10 :input Sinp,260,20 :pos 270,09 :button "CALC",*Scalc
pos 10,30 :input ss ,260,20 :pos 10,50 :input ss ,260,20
stop
;------------------------------------------------------------------------------
*Scalc
gosub *Chrpn :ss=""
foreach Sout :if Sout(cnt) != "" {ss+=Sout(cnt)+" "} :loop :objprm 2,ss
gosub *Rcalc :ss=str(double(refstr))
if ss == "9999999999.999998" {ss="[0] 除算"} :objprm 3,ss
stop
;==============================================================================
*Chrpn
sdim Sout :dim pn :sdim Stak :dim sn :sdim Tken :dim pp :jj=0 :sw=0
repeat strlen(Sinp)
ss=strmid(Sinp,cnt,1) :ii=peek(ss,0)
if (ii == 46) | ((ii >= 48) & (ii <= 57)) {
Tken(jj )+=ss :sw=0}
else {if ss != " " {if sw=0 {Tken(jj+1) =ss :jj+=2 :sw=1}
else {Tken(jj ) =ss :jj++ :sw=1}}}
loop
foreach Tken
ss=Tken(cnt) :gosub *Ckpri :pp(0)=stat:
switch pp(0)
case 5 :Sout(pn)=Tken(cnt) :pn++ :swbreak
case 2 :Stak(sn)=Tken(cnt) :sn++ :swbreak
case 1 :if sn == 0 {break}
For jj,sn-1,-1,-1
if Stak(jj) == "(" {_break}
Sout(pn)=Stak(jj) :Stak(jj)="" :pn++ :sn-- :next
:Stak(jj)="" :sn-- :swbreak
default :sw=0
For jj,sn-1,-1,-1
if (sn == 0) | (sw == 1) {_break}
ss=Tken(cnt) :gosub *Ckpri :pp(1)=stat
ss=Stak(jj ) :gosub *Ckpri :pp(2)=stat
if (pp(1) > pp(2)){sw=1}
else {Sout(pn)=Stak(jj) :pn++
Stak(jj)="" :sn--}:next
sn++:Stak(sn)=Tken(cnt) :sn++ :swbreak
swend
loop
repeat sn-1 :Sout(pn+cnt+1)=Stak(((sn-1)-cnt)) :loop
return
;------------------------------------------------------------------------------
*Ckpri
switch peek(ss,0)
case 46 : ;.
case 48 :case 49 :case 50 :case 51 :case 52
case 53 :case 54 :case 55 :case 56 :case 57 :kk=5 :swbreak ;0-9
case 42 :case 47 :kk=4 :swbreak ;*/
case 43 :case 45 :kk=3 :swbreak ;+/
case 40 :kk=2 :swbreak ;(
case 41 :kk=1 :swbreak ;)
default :kk=0
swend
return kk
;------------------------------------------------------------------------------
*Rcalc
foreach Sout
ss=Sout(cnt)
switch ss
case "*"
gosub *Pop
Sout(cnt-ii)=str(double(Sout(cnt-ii))*double(Sout(cnt-jj)))
Sout(cnt )="" :Sout(cnt-jj)="" :swbreak
case "/"
gosub *Pop
if double(Sout(cnt-jj)) == 0 {
Sout(0)="9999999999.999998" :break}
Sout(cnt-ii)=str(double(Sout(cnt-ii))/double(Sout(cnt-jj)))
Sout(cnt )="" :Sout(cnt-jj)="" :swbreak
case "+"
gosub *Pop
Sout(cnt-ii)=str(double(Sout(cnt-ii))+double(Sout(cnt-jj)))
Sout(cnt )="" :Sout(cnt-jj)="" :swbreak
case "-"
gosub*Pop
Sout(cnt-ii)=str(double(Sout(cnt-ii))-double(Sout(cnt-jj)))
Sout(cnt )="" :Sout(cnt-jj)="" :swbreak
default
swend
loop
return str(double(Sout(0)))
*Pop
for ii,1 ,cnt+1 :if Sout(cnt-ii) != "" {_break} :next :jj=ii
for ii,jj+1,cnt+1 :if Sout(cnt-ii) != "" {_break} :next
return
Re: 数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月03日(土) 23:36
by KICO
初めまして 関数男様、KICOと申します。
今後共、宜しくお願いします。
数学関数とは、言いがたいですが算数式程度なら出来そうです。
括弧有りの計算は出来ませんが・・・。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
エディトボックス3個 と ボタン1個 を貼り付けて下さい。
[例]
EditBox1:式 Y=4X+2
EditBox2:パラメータ X=2
EditBox3:答え Y=10
Button1 :計算実行
使用できる算術演算子は、"+ - * / \ ^ SIN COS TAN"です。
入力文字は、全てANK(半角)文字で。
コード: 全て選択
Sub MainWnd_CommandButton1_Click()
Dim str As String
Dim buf[50] As Byte
Dim pra[50] As Byte
Dim ans[50] As Double
Dim operand[50] As String
Dim operator[30] As String
Dim odc As Byte
Dim orc As Byte
Dim orn As Byte
Dim ac As Byte
Dim f1 As Byte
Dim f2 As Byte
Dim f3 As Byte
Dim i As Byte
Dim p As Byte
Dim a As Long
Dim b As Long
Dim c As Long
Dim d As String
Dim e As String
GetDlgItemText(hMainWnd, EditBox1, buf, 50)
' If CHK_Parenthesis() Then Exit Sub 'Syntax Error
i=InStr(1, buf, "="): str=Left$(buf, i)
While buf
p=i
a=InStr(1, "SinsinCoscosTantan", Chr$(buf)+Chr$(buf[i+1])+Chr$(buf[i+2]))
If a Then
i=i+3
p=i
While InStr(1, "0123456789.", Chr$(buf)): i=i+1: f1=odc+1: WEnd
If f1=0 Then Exit Sub 'Syntax Error
operand[odc]=Mid$(buf, p+1, i-p)
d=operand[f1-1]
Select Case a\6
Case 0
ans[ac]=Sin(Val(d))
Case 1
ans[ac]=Cos(Val(d))
Case 2
ans[ac]=Tan(Val(d))
End Select
ans[ac]=Sin(Val(d))
ac=ac+1: odc=odc+1
Else
p=i
While InStr(1, "0123456789.", Chr$(buf)): i=i+1: f1=odc+1: WEnd
If f1 Then operand[odc]=Mid$(buf, p+1, i-p): odc=odc+1
p=i
While ((buf>&H40 And buf<&H5b) Or (buf>&H60 And buf<&H7b)): i=i+1: f2=1: WEnd
If f2 Then
While InStr(1, "0123456789", Chr$(buf)): i=i+1: WEnd
operand[odc]=Mid$(buf, p+1, i-p)
GetDlgItemText(hMainWnd, EditBox2, pra, 50)
lstrcat(pra, Ex"\r\n")
a=1: b=0: c=0
While InStr(a, pra, "=")
b=InStr(a, pra, "="): a=InStr(b, pra, Ex"\r\n")
If lstrcmp(Mid$(pra, c+1, b-c-1), operand[odc])=0 Then operand[odc]=Mid$(pra, b+1, a-b-1)
c=a+1
WEnd
If b=0 Then Beep 'Error
d=operand[f1-1]: e=operand[odc]
If f1>0 Then ans[ac]=Val(d)*Val(e) Else ans[ac]=Val(e)
ac=ac+1
odc=odc+1
Else
If f1 Then d=operand[f1-1]: ans[ac]=Val(d): ac=ac+1
End If
End If
Select Case orn
Case 1
ans[orc]=ans[orc-1] + ans[orc]
Case 2
ans[orc]=ans[orc-1] - ans[orc]
Case 3
ans[orc]=ans[orc-1] * ans[orc]
Case 4
ans[orc]=ans[orc-1] / ans[orc]
Case 5
ans[orc]=ans[orc-1] \ ans[orc]
Case 6
ans[orc]=ans[orc-1] ^ ans[orc]
End Select
orn=0
p=i
orn=InStr(1, "+-*/\^", Chr$(buf))
If orn Then
f3=1: i=i+1
operator[orc]=Mid$(buf, p+1, i-p): orc=orc+1
End If
If f1 Or f2 Or f3 Then f1=0: f2=0: f3=0 Else i=i+1
WEnd
SetDlgItemText(hMainWnd, EditBox3, str+Str$(ans[orc]))
End Sub
お返事をありがとうございました。
Posted: 2005年12月06日(火) 16:04
by 関数男
皆様、お返事をありがとうございました。これは、難しい問題なのですね。 アルゴリズムが全く分からないのですが、世の中にいろいろ数学ソフトがあり、数式を入れれば関数値を計算してプロットしてくれますよね。 一体どうやっているのでしょうか。
こんなことするのかなあと、思いつくのは、文字列で与えられた関数式から順に"+"とか"-"とか"sin"とか"("とかを検索してそれらの引数も検索して、各関数の値を求め、最後にそれらを合成する… ですが、かなり大変そうに見えます。 どう考えればよいのか、アルゴリズムを知りたいです。
Posted: 2005年12月06日(火) 17:04
by こうき
私も昔、VisualBasicでWindows付属の関数電卓と同じようなものを作ってました。
最後にそれらを合成する・・・そのアルゴリズムといえるかどうかは不明ですが式を書いてその結果を出力するものは、式を数字と記号をわけて配列にいれ、配列の前のほうから順次計算していきます。(記号があればそのすぐあとの数字をその記号にしたがって計算します)ただし、+-のときは次に+-があるまで待ちます。
たとえば、2+4×3-10のときは、配列に代入しながら内容を見ていくと
配列の番号:内容
1:2
2:+
はじめの記号は+なので+-があるまで計算しない。
3:4
4:×
5:3
次の記号は×なので次の数3を4にかける。答えを配列3に代入
3:12
4:-
-がきたので前の+を処理する。配列1に2+12の結果を代入直後の配列2に-を代入
1:14
2:-
-なので次に+-がくるまで処理しない
3:10
あとに式がないので計算し値を表示
簡単にこんな流れでしょうか。
関数電卓かなんかをお持ちでしたら、その取り扱い説明書に括弧が入ったときの計算するアルゴリズムの説明(結局は逆ポーランド式のひとことで終わるかもしれませんが)がもっと詳しく書かれていると思います。Xを用いた関数であればそのXの部分に、入力された数字や関数を置換すればよいと思います。参考になれば幸いです。
Re: 数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月06日(火) 17:32
by KICO
すみません、消し忘れていました。
それと、書き忘れていましたがEditBox2は、
複数行にチェックし、適当な縦サイズを確保して下さい。
括弧演算も含め演算優先順位のルーチェンやトークンチェックは、関数男様の方で入れて下さい。
コード: 全て選択
Select Case a\6
Case 0
ans[ac]=Sin(Val(d))
Case 1
ans[ac]=Cos(Val(d))
Case 2
ans[ac]=Tan(Val(d))
End Select
ans[ac]=Sin(Val(d)) <---- 削除して下さい。
ac=ac+1: odc=odc+1
Else
世の中にいろいろ数学ソフトがあり、数式を入れれば関数値を計算してプロットしてくれますよね。 一体どうやっているのでしょうか。
定形型と任意型の二つに大別出来るかと?
定形型の場合、数式を選択して変数(x,y,a…)に値を入れて計算する。
任意型の場合、計算する前に、括弧が対に成っているかスペルは間違っていないか
トークンチェックをし演算の優先順位を考えて計算する。
関数男様の仰る通りかなり大変です。
Posted: 2005年12月06日(火) 18:01
by 関数男
みなさま助言をありがとうございます。
KICO様、もう少し詳しく教えて頂けると助かります。
<
定形型と任意型の二つに大別出来るかと?
定形型の場合、数式を選択して変数(x,y,a…)に値を入れて計算する。
任意型の場合、計算する前に、括弧が対に成っているかスペルは間違っていないか
トークンチェックをし演算の優先順位を考えて計算する。
(トークンチェック、って?)
Posted: 2005年12月06日(火) 18:15
by Uhsp
> (トークンチェック、って?)
割り込みですが本格的には「yacc・パーサ」で検索
(ActiveBasicやVisualBasicで簡単に利用したいならGOLDが便利です)
参考URL
http://kmaebashi.com/index.html
Re: 数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月06日(火) 21:26
by KICO
(トークンチェック、って?)
字句解析・語句解析の事で、任意に入力した数式の先頭から一文字ずつ文末まで解析する事です。
例えば、"Sin"が関数なのか変数なのか使用出来る「関数テーブル」を作りそこに無ければ、「変数テーブル」をみる、
あれば変数、無ければ "タイプミス"エラー というふうに語句解析していきます。
フローチャートを書いて解析していくと解り易いですよ。
Posted: 2005年12月07日(水) 10:47
by 関数男
なるほど分かりました。ありがとうございました。
Posted: 2005年12月07日(水) 16:06
by Yoshi
インタープリターもどきです。
といっても代入文のみ。
使用できるのは,(),+,-,/,*,Sin,Cosです。
変数はA-Zで、代入文を実行した瞬間buffer2に答えが入ります。
EditBox1とEditBox2とCommandButton1を使った例です。
EditBox1に
X=10
A=5
Y=A*X+10
としてCommandButton1を押すと
EditBox2に
X=10
A=5
Y=60
と表示されると思います。
ここをクリックすると字句解析が表示されます [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
コード: 全て選択
'字句解析
Function IsCharNumeric(ByVal c As Byte) As Long
If Asc("0") <= c And c <= Asc("9") Then
IsCharNumeric = TRUE
Else
IsCharNumeric = FALSE
Endif
End Function
Dim read_char_p As Long
Dim buffer[1024] As Byte
Function read_char() As Byte
read_char = buffer[read_char_p]
read_char_p = read_char_p + 1
End Function
'------------------------------------
Enum LEX_KIND
FUNC = 1'関数
REG = 2'変数
NUM = 3'数値
KIGOU = 4'その他
End Enum
Enum FUNC_KIND
SIN'Sin
COS'Cos
End Enum
Enum STATE'内部状態
INIT_STATE
EXIT_STATE
KIGOU_STATE
NUM_STATE
SYOUSU_STATE
NAME_STATE
FUNC_STATE
End Enum
Dim c As Byte
Function Init()
read_char_p = 0
c = read_char()
End Function
Dim text[100] As Byte'単語がそのまま入る
Dim val As Double'単語が数字なら数値が入る
Dim reg As Long'単語が変数なら添え字が入る
Dim symbol As Byte'単語がその他の文字ならその文字が入る。
Dim func As *Function(x As Double) As Double'単語が関数なら関数ポインターが入る。(引数1つのものだけ)
'単語を読む
Function read_symbol() As LEX_KIND
Dim state As STATE
Dim text_p As Long
state = INIT_STATE
text_p = 0
While state <> EXIT_STATE
Select Case state
Case INIT_STATE
If IsCharNumeric(c) = TRUE Then
state = NUM_STATE
Elseif IsCharAlpha(c) = TRUE Then
state = NAME_STATE
Else
state = KIGOU_STATE
End If
Case KIGOU_STATE
read_symbol = KIGOU
state = EXIT_STATE
Case NUM_STATE
If IsCharNumeric(c) = TRUE Then
state = NUM_STATE
ElseIf c = Asc(".") Then
state = SYOUSU_STATE
Else
state = EXIT_STATE
read_symbol = NUM
End If
Case SYOUSU_STATE
If IsCharNumeric(c) = TRUE Then
state = SYOUSU_STATE
Else
state = EXIT_STATE
read_symbol = NUM
End If
Case NAME_STATE
if IsCharAlphaNumeric(c) Then
state = FUNC_STATE
Else
state = EXIT_STATE'一文字の場合
read_symbol = REG
End If
Case FUNC_STATE
If IsCharAlphaNumeric(c) Then
state = FUNC_STATE
Else
state = EXIT_STATE
read_symbol = FUNC
End If
End Select
If state <> EXIT_STATE Then
text[text_p] = c
text_p = text_p + 1
c = read_char()
End If
Wend
text[text_p] = 0
Select Case read_symbol
Case FUNC
If lstrcmp(text,"Sin") = 0 Then
func = AddressOf(Sin)
Elseif lstrcmp(text,"Cos") = 0 Then
func = AddressOf(Cos)
End If
Case NUM
val = Val(text)
Case REG
If IsCharUpper(text[0]) = TRUE Then
reg = text[0] - Asc("A")
ElseIf IsCharLower(text[0]) = TRUE Then
reg = text[0] - Asc("a") + 26
End If
Case KIGOU
symbol = text[0]
End Select
End Function
ここをクリックすると構文解析が表示されます [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
コード: 全て選択
'構文解析
Dim t As LEX_KIND
Dim buffer2[1024] As Byte
Dim regs[54]
Sub START()
Init()
buffer2[0] = 0
t = read_symbol()
S()
If t = KIGOU And symbol = 0 Then
'成功
Else
'失敗
lstrcpy(buffer2,"ERROR")
End If
End Sub
Sub S()'プログラムかどうかを解析
If t = KIGOU And symbol=0 Then
ExitSub
Else if t = REG then 'S->B\nS
B()
If t = KIGOU And symbol = 13 Then'LF
t = read_symbol()'
If t = KIGOU And symbol = 10 Then
t = read_symbol()
S()
End If
End If
End If
End Sub
Sub B()'文かどうか
Dim p As Long
Dim reg_name As Byte
If t = REG Then'B->REG=F(代入文のみです。)
p = reg
reg_name = text[0]
t = read_symbol()
if t = KIGOU And symbol = Asc("=") then
t = read_symbol()
regs[p] = F()
End If
lstrcat(buffer2,Chr$(reg_name) & "=" & Str$(regs[p])+Chr$(13)+Chr$(10))
End If
End Sub
Function F() As Double
Dim u As Double
u = T()
F = F1(u)
End Function
Function T() As Double
Dim u As Double
u = A()
T = T1(u)
End Function
Function T1(x As Double) As Double
Select Case t
Case KIGOU
Select Case symbol
Case Asc("*")
t = read_symbol()
T1 = T1(x * A())
Case Asc("/")
t = read_symbol()
T1 = T1(x / A())
Case Else' Asc("+"),Asc("-"),Asc(")"),0
T1 = x
End Select
End Select
End Function
Function F1(x As Double) As Double
Select Case t
Case KIGOU
Select Case symbol
Case Asc("+")
t = read_symbol()
F1 = F1(x + T())
Case Asc("-")
t = read_symbol()
F1 = F1(x - T())
Case Else' Asc(")"),0
F1 = x
End Select
End Select
End Function
Function A() As Double
Dim u As Double
Dim g As *Function(x As Double) As Double
If t = REG Then
A = regs[reg]
t = read_symbol()
Elseif t = FUNC Then
g = func
t = read_symbol()
If t = KIGOU And symbol = Asc("(") Then
t = read_symbol()
u = F()
If t = KIGOU And symbol = Asc(")") Then
A = func(u)
t = read_symbol()
End If
End If
Elseif t = NUM Then
A = val
t = read_symbol()
Elseif t = KIGOU Then
If symbol = Asc("(") Then
t = read_symbol()
u = F()
If t = KIGOU And symbol = Asc(")") Then
t = read_symbol()
A = u
End If
ElseIf symbol = Asc("-") Then
t = read_symbol()
u = - A()
End If
End If
End Function
Posted: 2005年12月07日(水) 16:52
by 関数男
サンプルをありがとうございました。
やはり、プログラムはかなりのものになるのですね。
勉強して頑張ってみます。
数学関数式Y=AX+Bなどを与えてYを計算させたい
Posted: 2005年12月07日(水) 20:25
by tetsu
白石先生による十進BASICでサンプルに入っているアルゴリズムです。
関数についても同様に拡張できます。
REM Full BASICの文法にしたがって数値式を評価するプログラム
REM 数値は整数のみが書ける。 組込み関数や変数は使えない。
REM 零除算エラーなどは考慮していない。
DECLARE EXTERNAL FUNCTION interpreter.expression ! 数値式を評価する関数
DECLARE EXTERNAL STRING interpreter.s$ ! 入力行
DECLARE EXTERNAL NUMERIC interpreter.i ! 入力行の文字位置
DECLARE EXTERNAL SUB interpreter.skip ! 空白文字を読み飛ばす副プログラム
LINE INPUT s$
LET i=1
CALL skip
PRINT expression
IF i<LEN(s$) THEN PRINT "Syntax error at """ ;s$(i: LEN(s$)); """"
END
MODULE interpreter
PUBLIC STRING s$
PUBLIC NUMERIC i
PUBLIC FUNCTION expression
PUBLIC SUB skip
SHARE FUNCTION term,factor,primary,numeric
EXTERNAL SUB skip
DO WHILE s$(i:i)=" "
LET i=i+1
LOOP
END SUB
EXTERNAL FUNCTION expression
DECLARE NUMERIC n
DECLARE STRING op$
SELECT CASE s$(i:i)
CASE "-"
LET i=i+1
CALL skip
LET n=-term
CASE "+"
LET i=i+1
CALL skip
LET n=term
CASE ELSE
LET n=term
END SELECT
DO WHILE s$(i:i)="+" OR s$(i:i)="-"
LET op$=s$(i:i)
LET i=i+1
CALL skip
IF op$="+" THEN LET n=n+term ELSE LET n=n-term
LOOP
LET expression =n
CALL skip
END FUNCTION
EXTERNAL FUNCTION term
DECLARE NUMERIC n
DECLARE STRING op$
LET n=factor
DO WHILE s$(i:i)="*" OR s$(i:i)="/"
LET op$=s$(i:i)
LET i=i+1
CALL skip
IF op$="*" THEN LET n=n*factor ELSE LET n=n/factor
LOOP
LET term=n
END FUNCTION
EXTERNAL FUNCTION factor
DECLARE NUMERIC n
LET n=primary
DO WHILE s$(i:i)="^"
LET i=i+1
CALL skip
LET n=n^primary
LOOP
LET factor=n
END FUNCTION
EXTERNAL FUNCTION primary
IF s$(i:i)="(" THEN
LET i=i+1
CALL skip
LET primary=expression
IF s$(i:i)=")" THEN
LET i=i+1
CALL skip
ELSE
PRINT "Syntax error"
STOP
END IF
ELSE
LET primary=numeric
END IF
END FUNCTION
EXTERNAL FUNCTION numeric
DECLARE NUMERIC i0
CALL skip
LET i0=i
DO WHILE s$(i:i)>="0" AND s$(i:i)<="9"
LET i=i+1
LOOP
LET numeric=VAL(s$(i0:i-1))
CALL skip
END FUNCTION
END MODULE
Posted: 2005年12月10日(土) 08:47
by カクラ
WSHを併用するというのはどうでしょうか?。