見出し画像

pythonで2次方程式計算機(2次方程式の解を求めるプログラム)を作ろう

2次方程式には大きく分けて2つの解き方があります。1つ目は因数分解、そして2つ目は解の公式。(平方完成?ナニソレオイシイノ)
このうち解の公式は地味に代入が面倒です。また、2次方程式を解く以外でも2次式の因数分解に利用したりします(結局解くから一緒かな?)。今回のプログラムはそんな面倒な解の公式への代入・計算を一瞬で済ませてくれるプログラムです。いちおう虚数範囲にも対応させました(ほぼ需要なしかな)

今回は説明は長いですが結構楽なプログラムです。というのも、今回は今までに作成したプログラムの丸写しが多いです。また、今回も記事の一番下にプログラムコードを書いておきますので、コピペ等でご利用ください。宿題には有用かも


使う公式

使う公式といっても1つだけです。そう、あのいやというほど覚えさせられた超有名な式です。

$$
ax^2+bx+c=0    (a\neq0)のとき、
x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}
$$

なぜこうなるかということですが、前回の記事で紹介した平方完成の公式を$${y=0}$$で$${x}$$について解くと導き出すことができます。証明はもっと簡単で、$${ax^2+bx+c=0}$$に$${x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}}$$を代入して等式が成り立つことを確認するだけです。この方法が一番単純ですが、ものすごく面倒なので変人(私はその部類に入る)以外はやらないことをおすすめします(体験談)。

虚数とは?

このプログラムでは念のため虚数にも対応させます。中学であれば「解なし」で済まされるところですが、高校では虚数単位$${i}$$が登場します。プログラムを組む人間としては隅々まで対応させたいところなので、虚数は「解なし」で済まさずに対応していこうと思います。
まずは「虚数」とは何かについて理解しておきましょう。虚数の定義は下のようになります。

$${虚数とは、2乗すると負の数になる数のことである。}$$

普通は2乗すればどんな数でも正の数になります。$${2^2=4,(-3)^2=9,(-1.1)^2=1.21,(-\frac{3}{5})^2=\frac{9}{25}}$$などのように。この常識を真っ向から否定してくる数が虚数です。
虚数には基準として虚数単位というものがあります。虚数単位は$${i}$$(imaginary numberの頭文字)を使って次のように表されます。

$$
i^2=-1
$$

つまり、$${i=\sqrt{-1}}$$です。この数は正の数でも負の数でも0でもありません。ここでは証明は本題から逸れるので省きますが、気になる方は検索などしてみてください。また、$${i}$$の演算は基本的に$${a}$$や$${b}$$といった文字と同じです。ただし最終的には$${i^2=-1,i^3=-i,i^4=1}$$などのように変換しなければなりません。
さて、今回$${i}$$の利用では次のような変形を利用します。

$$
\sqrt{-n}=\sqrt{-1×n}=\sqrt{-1}×\sqrt{n}=i×\sqrt{n}=\sqrt{n}i   (n>0)\\
例:\sqrt{-3}=\sqrt{3}i,\sqrt{-\frac{3}{4}}=\sqrt{\frac{3}{4}}i=\frac{\sqrt{3}}{\sqrt{4}}i=\frac{\sqrt{3}i}{2}
$$

もはや普通の√内整理よりも簡単(マイナスをくくり出すだけ)なので、特に暗記などが必要なものではないと思います。というか必要ないです。
今回はこれを使ってプログラムを作っていきます。

内部処理

入力を受け取ってからの処理は、基本的には人間の考えることと同じようにします。$${a,b,c}$$を公式に代入して、計算して…ということです。しかし、pythonにそのまま入力したのでは、√や分数もすべて小数にして計算されてしまいます。これを避けるために、分母、分子、√をすべて別にして計算します。具体的には下のように組みます。

①$${a,b,c}$$を公式に代入する
②√内の$${b^2-4ac}$$を計算する
③②を整理する($${\sqrt{12}→2\sqrt{3}}$$など)
④全体を約分する

ただし、pythonには$${\pm}$$なんていう便利な物はないので、$${+}$$の場合と$${-}$$の場合に分けて計算します。

√の計算と約分

√内の整理

まずは√の整理ですが、これは前の三角形の面積を求めるプログラムの時に作ったものを利用します。まだ見てない方はぜひご覧ください。

この仕組みを簡単に説明しておくと、√内を素因数分解し、2乗が含まれている数字を外にくくり出す、という手順で実行しています。しかし、今回はこの整理に虚数も使うので少しプログラムは変えます。

約分

約分ですが、今回は前回の平方完成とは違い下のような形を約分します。

$$
\frac{a+b\sqrt{n}}{m}
$$

√は約分には関係ないので、今回は$${\frac{a+b}{m}}$$を$${a+b}$$が計算できないものとして約分することになります。そこで、pythonの組み込みモジュールである$${\verb|math|}$$モジュールの$${\verb|math.gcd()|}$$を使っていきます。これは()の中に入力した引数の最大公約数を求めてくれる関数です。つまり、先ほどの式の分母、分子ともに$${\verb|math.gcd()|}$$で割れば約分できるということです。

プログラムコード

最後にプログラムコードです。私の調べた限りではちゃんと動きますが、雑なプログラムなのでもしかしたら計算結果が異なる場合があるかもしれません。

import math

### math.sqrt()をsqrt()で実行できるようにする
def sqrt(number):
    return math.sqrt(number)

# 1.0 のようないらない.0を消す
def k(number):
    if isinstance(number,float) and number.is_integer():
        return int(number)
    return number

### ルート内整理
def root(m):
    ###虚数の判定
    if m < 0:
        imaginary = True
        m = m * -1
    else:
        imaginary = False
    if sqrt(m) % 1 == 0:
        ###√内が平方数の場合は√を外す
        return [k(sqrt(m)),"Password",imaginary]
    else:
        ###√内を素因数分解して整理する
        nlist = []
        n = 1
        for i in range(2,100):
            while m % i == 0 and m / 1 != 1:
                if (m/i) % i == 0:
                    n = n * i
                    m = m / (i**2)
                else:
                    nlist.append(i)
                    m = m / i
            i += 1
        for l in nlist:
            m = m * l
        return [n,k(m),imaginary]

###約分処理((num1+num2)/denの約分)
def frac(num1,num2,den):
    dn = math.gcd(num1,num2,den)
    num1 = k(num1/dn)
    num2 = k(num2/dn)
    den = k(den/dn)
    return [num1,num2,den]

###a,b,cの代入
def solve(a,b,c):
    ###ルート部分の計算
    sqrtans = root(b**2-4*a*c)
    ###分数部分の計算
    if sqrtans[1] == 'Password' and sqrtans[2] == False:
        ###√が外れる場合
        ans1 = frac(-b+sqrtans[0],0,2*a)
        ans2 = frac(-b-sqrtans[0],0,2*a)
        if ans1[2] == 1:
            ans1 = str(ans1[0])
        else:
            ans1 = '%s/%s' % (ans1[0],ans1[2])
        if ans2[2] == 1:
            ans2 = str(ans2[0])
        else:
            ans2 = '%s/%s' % (ans2[0],ans2[2])
        if sqrtans[2] == True:
            ###虚数が含まれる場合
            ans1 += 'i'
            ans2 += 'i'
        newans2 = list(ans2)
        del newans2[0]
        if ans1 == ans2:
            last_ans = str(ans1)
        elif ans1 == ''.join(newans2):
            last_ans = '±' + ans1
            if last_ans == '±1i':
                last_ans ='±i'
        else:
            last_ans = [ans1,ans2]
    else:
        ###√が残る場合
        ans1 = frac(-b,sqrtans[0],2*a)
        if ans1[1] == 1:
            ans1[1] = ''
        if sqrtans[1] == 'Password':
            sqrtans[1] = ''
        elif sqrtans[2] == True:
            sqrtans[1] = '(√' + str(sqrtans[1]) + ')'
        else:
            sqrtans[1] = '√' + str(sqrtans[1])
        if ans1[0] == 0:
            ### b = 0の場合
            if sqrtans[2] == True:
                ###虚数が含まれる場合
                last_ans = '(±%s%si)/%s' % (ans1[1],sqrtans[1],ans1[2])
                if ans1[2] == 1:
                    last_ans = '±%s%si' % (ans1[1],sqrtans[1])
            else:
                ###虚数が含まれない場合
                last_ans = '(±%s%s)/%s' % (ans1[1],sqrtans[1],ans1[2])
                if ans1[2] == 1:
                    last_ans = '±%s%s' % (ans1[1],sqrtans[1])
        else:
            ### b ≠ 0 の場合
            if sqrtans[2] == True:
                ###虚数が含まれる場合
                last_ans = '(%s ± %s%si)/%s' % (ans1[0],ans1[1],sqrtans[1],ans1[2])
                if ans1[2] == 1:
                    last_ans = '%s ± %s%si' % (ans1[0],ans1[1],sqrtans[1])
            else:
                ###虚数が含まれない場合
                last_ans = '(%s ± %s%s)/%s' % (ans1[0],ans1[1],sqrtans[1],ans1[2])
                if ans1[2] == 1:
                    last_ans = '%s ± %s%s' % (ans1[0],ans1[1],sqrtans[1])
    return [last_ans,sqrtans[2]]
    
###入出力部分
def n():
    try:
        ###入力部分
        input_n = input("a,b,cの値を整数のカンマ(,)区切りで入力してください>>>").split(',')
        ina = int(input_n[0])
        inb = int(input_n[1])
        inc = int(input_n[2])
        ###計算
        answer = solve(ina,inb,inc)
        answers = answer[0]
        ###方程式の出力
        if ina == 1:
            ina = ''
        if inb == 1:
            inb = ''
        if inb == 0:
            if inc == 0:
                print("2次方程式 %sx^2 = 0 の解は、" % (ina))
            else:
                print("2次方程式 %sx^2 + %s = 0 の解は、" % (ina,inc))
        elif inc == 0:
            print("2次方程式 %sx^2 + %sx = 0 の解は、" % (ina,inb))
        else:
            print("2次方程式 %sx^2 + %sx + %s = 0 の解は、" % (ina,inb,inc))
        ###解の出力
        if type(answers) == list:
            result = "x = %s,%s" % (answers[0],answers[1])
        else:
            result = "x = %s" % answers
        if answer[1] == True:
            result += " (実数の範囲では解なし)"
        print(result)
    ###エラー処理
    except:
        print("指定値以外の値が入力されました。入力し直してください。")
    print()

###説明文
print("""
このプログラムは2次方程式 ax^2 + bx + c = 0 を解くプログラムです。
n()と入力するとプログラムが起動します。
起動したらa,b,cの値を整数のカンマ区切りで入力してください。
入力例1:1,5,6 出力:x=3,2
入力例2:1,0,4  出力:x=±2i
""")



ここまでで大体の解説はしてきたのでここでの詳しい解説はしません。これで2次方程式を解くプログラムの完成です!これで宿題が楽になります!


この記事が気に入ったらサポートをしてみませんか?