私の数学ソフト作り(VB.NET)

 コンピュータ(プログラム作成者)から与えられた問題を解くより、「僕は、私はこれが解きたい」と、問題をコンピュータに入力し、まるでコンピュータと対話するかのように、解答を促されたり、タイムリーにヒントが出てきたり、少しでも、学ぶ側が能動的、自発的に学習できることに拘って数学ソフトを作成しています。

 拙作なのですが、1次方程式をコンピュータに解かせるプログラムを紹介したいと思います。

「2x+(x+3)=x-5」を念頭に置き、VisualBasicで解かせてみます。

私たちは、1次方程式を次の順序で解きます。

①括弧をはずす。

②文字を含む項は左辺に、定数項は右辺に移項する。

③同類項をまとめる。

④xの係数で両辺をわる。

また、同類項をまとめる段階で「2x+x-x=-5-3」となりますが、「2x+x」が「3x」となるためには、係数が必要です。

 人は数式を簡略してきました。「1×3=3」ですものね。「1x=x」としても問題はありません。でも、これがコンピュータで計算させるときにはやっかいです。例外は理解してもらえません。ですから、「2x+(x+3)=x-5」を文字列操作で「+2x+1+1(+1x+3)=+1x-5」にします。項は必ず符号と数と文字を持つです。(定数項は別ですが)

 括弧をはずすときには「+1(+1x+3)」で「+1」を「+1x」にかけ、さらに「+3」にもかけます。「+1x」や「+3」は何かというと項です。同類項をまとめるにしても、項同志の計算になりますので、式を項に分解することが必要です(項を配列に格納します)。

 ここまでで、「+」や「1」を復元するルーチン(プロシージャ?)と、項を配列に格納するプロシージャを作ります。さらに、括弧の付いた1次方程式は保留し、「3x+3=x-5」を解くことから始めたいと思います。

 まず、Form1にButton1とTextBox1を配置し、TextBox1をMultiLineにチェックを付け、下方に伸ばします。次に、変数ですが、式を「Formula」、項を「Term()」とし、プロシージャ外で宣言します。 Public Class Form1の下です。

    Private Formula As String
    Private Term(20) As String

 「+」や「1」を復元するプロシージャを「Restore」、項を配列に格納するプロシージャを「TakeToTerm」と名付け、以下のように記述します。

pivate Sub Restore()
    Dim i As Integer
    '①式の先頭が数なら「+」をつける
    If Strings.Left(Formula, 1) Like "[0-9]" Then
        Formula = "+" + Formula
    End If
    '②「=x」なら「=+1x」とする
    i = Strings.InStr(Formula, "=x")
    If i > 0 Then
        Formula = Strings.Mid(Formula,1, i - 1) & "=+1x" & Strings.Mid(Formula, i + 2)
    End If
    '③「+(」なら「+1(」とする、括弧付きのことも考えて
    i = Strings.InStr(Formula, "+(")
    If i > 0 Then
        Formula = Strings.Mid(Formula, 1, i - 1) & "+1(" & Strings.Mid(Formula, i + 2)
    End If
    '④「(x」なら「(+1x」とする、括弧付きのことも考えて
    i = Strings.InStr(Formula, "(x")
    If i > 0 Then
        Formula = Strings.Mid(Formula, 1, i - 1) & "(+1x" & Strings.Mid(Formula, i + 2)
    End If
End Sub
'
Private Sub TakeToTerm()
    Dim i, j, k As Integer
    Dim Ltr As String
    '
    i = 1 : j = 2 : k = 1
    Do Until Strings.Mid(Formula, i, 1) = Nothing 
        Ltr = Strings.Mid(Formula, j, 1)
        If Ltr Like "[-+()=]" Or Ltr = "" Then
            Term(k) = Strings.Mid(Formula, i, j - i)
            k = k + 1
            i = j
        End If
        j = j + 1
    Loop
End Sub
  

上記のプログラムが正しく動いていることを確かめます。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Formula = "3x+3=x-5"
    Call Restore()
    Me.TextBox1.Text = Formula
End Sub

でしたら、TextBox1には「+3x+3=+1x-5」が表示されます。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Dim i As Integer
    '
    Formula = "3x+3=x-5"
    Call Restore()
    Call TakeToTerm()
    i = 1
    Do Until Term(i) = Nothing
        Me.TextBox1.SelectedText = "Term(" & i & "):" & Term(i) & vbCrLf
        i = i + 1
    Loop
End Sub

 上記のようにTakeToTermを加えると、

Term(1):+3x
Term(2):+3
Term(3):=
Term(4):+1x
Term(5):-5

 項を配列に格納できたので、次は移項です。文字(変数)を含んでいる項は左辺に、定数項は右辺です。符号が変わることに注意しなければいけません。また、移項する段階で文字を含んでいるかどうかをプログラムすると複雑になるので、関数を作ります。「VarTrim()」と名付けました。これは、文字(変数)を取り出します。

Public Function VarTrim(ByVal Term As String) As String
        Dim i As Integer
        Dim Ltr As String
        '
        i = 1
        Do Until Strings.Mid(Term, i, 1) = Nothing
            Ltr = Strings.Mid(Term, i, 1)
            If Ltr Like "[-+/.0-9]" Then
                Term = Strings.Mid(Term, i + 1)
            Else
                i = i + 1
            End If
        Loop
        Return Term
End Function

 ご覧の通り、何のことはありません。項に含まれるであろう文字(変数)以外の記号や数字を取り除いているだけです。後々のこと(分数や小数が入る)も考慮して、「.」や「/」も取り除くようにしています。また、文字(変数)が含まれていなければ、「Nothing」が返ります。

 それでは、移項のプロシージャを書きます。その名の通り「Transpose」です。入力はFormulaで出力もFormulaにしています。このルーチンの中で前述の「Restore」と「TakeToTerm」が使われています。

Private Sub Transpose()
    Dim Eq As Integer
    Dim vTerm(20), cTerm(20) As String
    Dim i, j As Integer
    Dim Fm As String
    '
    Call Restore()
    Call TakeToTerm()
    '
    '「=」の位置
    i = 1
    Do Until Term(i) = "="
        i = i + 1
    Loop
    Eq = i
    '移項(文字の項を左辺)
    i = 1 : j = 1
    Do Until Term(i) = Nothing
        If VarTrim(Term(i)) <> Nothing And Term(i) <> "=" Then
            If i < Eq Then
                vTerm(j) = Term(i)
            Else
                If Strings.Mid(Term(i), 1, 1) = "+" Then
                    vTerm(j) = Strings.Replace(Term(i), "+", "-")
                Else
                    vTerm(j) = Strings.Replace(Term(i), "-", "+")
                End If
            End If
            j = j + 1
        End If
        i = i + 1
    Loop
    '
    '移項(定数項を右辺)
    i = Eq + 1 : j = 1 '先に右辺を格納
    Do Until Term(i) = Nothing
        If VarTrim(Term(i)) = Nothing Then
            cTerm(j) = Term(i)
            j = j + 1
        End If
        i = i + 1
    Loop
    '
    j = j '明示的
    i = 1 '左辺の格納
    Do Until i = Eq
        If VarTrim(Term(i)) = Nothing Then
            If Strings.Mid(Term(i), 1, 1) = "+" Then
                If Term(i) = "+0" Then
                    cTerm(j) = Term(i)
                Else
                    cTerm(j) = Strings.Replace(Term(i), "+", "-")
                End If
            Else
                cTerm(j) = Strings.Replace(Term(i), "-", "+")
            End If
            j = j + 1
        End If
        i = i + 1
    Loop
    '
    '式作りTerm()とcTerm()をFmにためる
    i = 1 : Fm = Nothing
    Do Until vTerm(i) = Nothing
        Fm = Fm & vTerm(i)
        i = i + 1
    Loop
    Fm = Fm & "="
    i = 1
    Do Until cTerm(i) = Nothing
        Fm = Fm & cTerm(i)
        i = i + 1
    Loop
    Formula = Fm
    Array.Clear(Term, 0, Term.Length)
End Sub

 このプロシージャは、

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Formula = "3x+3=x-5"
    Call Transpose()
    Me.TextBox1.Text = Formula
End Sub

によって確かめることができます。「Formula="+3x-1x=-5-3"」になると思います。

 次は同類項をまとめるルーチンです。名前は「SimilarTerm」としました。「Combine」のほうが適切かなと思いましたが、ぴんとこなくて…。ただ、ここで同類項をまとめるとき、「+3x」に「-1x」を加えるには「Val("+3x")+Val("-1x")」でもいいのですが、小数や分数のことも考えて、関数を作ります。Add()関数で、

Public Function Add(ByVal TermA As String, ByVal TermB As String) As String
    Add = Trim(Str(Val(TermA) + Val(TermB))) & VarTrim(TermA)
    If Strings.Mid(Add, 1, 1) <> "-" Then
        Add = "+" & Add
    End If
    Return Add
End Function

 これで、「SimilarTerm」は以下のようになります。

Private Sub SimilarTerm()
    Dim i, j As Integer
    Dim Fm As String
    '
    Call Restore()
    Call TakeToTerm()
    'Term(i)とTerm(j)が同類項ならTerm(i)に和を、Term(j)には「@」を入れる、あとで抜き取ります
    i = 1
    Do Until Term(i) = Nothing
        j = i + 1
        Do Until Term(j) = Nothing
            If Term(i) <> "@" And Term(j) <> "@" And Term(i) <> "="  _
               And Term(j) <> "=" And VarTrim(Term(i)) = VarTrim(Term(j)) Then
                If Add(Term(i), Term(j)) = Nothing Then
                    Term(i) = "@"
                Else
                    Term(i) = Add(Term(i), Term(j))
                End If
                Term(j) = "@"
            End If
            j = j + 1
        Loop
        i = i + 1
    Loop
    '式作り
    i = 1 : Fm = Nothing
    Do Until Term(i) = Nothing
        If Term(i) <> "@" Then
            Fm = Fm & Term(i)
        End If
        i = i + 1
    Loop
    Formula = Fm
    Array.Clear(Term, 0, Term.Length)
End Sub

これを確かめるには、

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Formula = "+3x-1x=-5-3"
    Call SimilarTerm()
    Me.TextBox1.Text = Formula
End Sub

「Formula="+2x=-8"」となります。

最後は、「x」の係数で割ります。

 以上、正しく動作するか、Button1.Clickイベントを下記のように記述し、実行してみてください。ただ、最後には「Val関数」を用いて処理していますが、これも、小数や分数のことを考えて工夫が必要です。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Dim ax As String
    Dim b As String
    Dim Eq As Integer
    '
    Formula = "3x+3=x-5"
    Call Transpose()
    Call SimilarTerm()
    Eq = Strings.InStr(Formula, "=")
    ax = Strings.Mid(Formula, 1, Eq - 1)
    b = Strings.Mid(Formula, Eq + 1)
    Formula = VarTrim(ax) & "=" & Val(b) / Val(ax)
    Me.TextBox1.Text = Formula
End Sub

 次に、括弧を含む1次方程式です。分配法則により括弧をはずすプロシージャ名を「Bracket」と名付けました。コードは

Private Sub Bracket()
    Dim i, j, k As Integer
    Dim CoB As String
    Dim BFm As String
    Dim FmL, FmM, FmR As String
    '
    Call Restore()
    '
    j = Strings.InStr(Formula, "(")
    k = Strings.InStr(Formula, ")")
    i = j - 1
    Do Until Strings.Mid(Formula, i, 1) Like "[+-]"
        i = i - 1
    Loop
    '
    FmL = Strings.Mid(Formula, 1, i - 1)
    FmR = Strings.Mid(Formula, k + 1)
    CoB = Strings.Mid(Formula, i, j - i)
    BFm = Strings.Mid(Formula, j + 1, k - j - 1)
    Formula = BFm
    Call TakeToTerm()
    i = 1
    Do Until Term(i) = Nothing
        Term(i) = Trim(Str(Val(CoB) * Val(Term(i)))) & VarTrim(Term(i))
        If Strings.Mid(Term(i), 1, 1) <> "-" Then
            Term(i) = "+" & Term(i)
        End If
        i = i + 1
    Loop
    i = 1 : FmM = Nothing
    Do Until Term(i) = Nothing
        FmM = FmM & Term(i)
        i = i + 1
    Loop
    Array.Clear(Term, 0, Term.Length)
    Formula = FmL & FmM & FmR
End Sub

 これを確かめるために

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Formula = "+2x+1(+1x+3)=+1x-5"
    Call Bracket()
    Me.TextBox1.Text = Formula
End Sub

 Formula="+2x+1x+3=+1x-5"になります。

 いよいよ、最後です。Formula="2x+(x+3)=x-5"とし、Button1.Clickイベントプロシージャに次のようにコードを書き込んでください。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)  _
    Handles Button1.Click
    Dim ax As String
    Dim b As String
    Dim Eq As Integer
    '
    Formula = "2x+(x+3)=x-5"
    Call Bracket()
    Call Transpose()
    Call SimilarTerm()
    Eq = Strings.InStr(Formula, "=")
    ax = Strings.Mid(Formula, 1, Eq - 1)
    b = Strings.Mid(Formula, Eq + 1)
    Formula = VarTrim(ax) & "=" & Val(b) / Val(ax)
    Me.TextBox1.Text = Formula
End Sub

 答は「x=-4」となります。

 さて、ここでは「Restore」を簡略しました。実際は文字(変数)は「x」だけではありませんし、長い式となると括弧が2つある場合もありますし、「(x」は式の中で1つとは限りません。また、「(3x」の場合もあります。それらに対応した「Restore」や「Bracket」をしっかりと作る必要があります。

以上2015/05/07