pythonで平方完成をしてくれるプログラムを作ろう
地味に面倒くさい2次関数のグラフを書く問題。ただ書くだけではなく、頂点の位置も求めてから書かなければいけない……今回は、そんな頂点の位置を求めてくれる(平方完成をしてくれる)プログラムを作っていきます。
そもそも平方完成って何?
さて、プログラムを作る前に平方完成がどのようなものか、またどのようにして平方完成するのかを解説しておきます。理解している方は飛ばしてもらっても構いません。
平方完成とは
平方完成とは、$${y=ax^2+bx+c}$$の形の2次関数を、$${y=a(x-p)^2+q}$$の形に式変形することです。そして、変形した後の形は2次関数のグラフを書くことに大きく関わってきます。なぜなら、この式のグラフは頂点の座標が(p,q)で傾きがaの2次関数だからです。次はそのやり方を見てみましょう。
平方完成のやり方
ここでは例として$${y=3x^2-18x+32}$$を平方完成してみます。
*左辺の$${y}$$は関係ないのでここからは省略します
①$${x^2}$$の項の係数をくくり出す
$${3x^2-18x+32=3(x^2-6x)+32}$$
このとき次数が0の項(数字だけの項)はくくり出しません。
また、次数が1の項(ここでは$${-6x}$$)の係数が分数にある場合もあります。
②カッコの中を$${x^2 \pm 2ax+a^2-a^2}$$の形にする
$${3(x^2-6x)+32=3(x^2-2×3×x+3^2-3^2)+32}$$
これを計算して$${3(x^2-6x+9-9)+32}$$の形にします。
③カッコ内を因数分解する
$${3(x^2-6x+9-9)+32=3\{(x-3)^2-9\}+32}$$
前の手順で作った$${x^2 \pm 2ax+a^2}$$の形を$${(x \pm a)^2}$$に変形します。
④外側のカッコを外し、式をまとめる
$${3\{(x-3)^2-9\}+32=3(x-3)^2-27+32=3(x-3)^2+5}$$
これで平方完成ができました。また、$${y=3x^2-18x+32}$$のグラフの頂点の座標が(3,5)であることも同時に分かります。
平方完成の注意点
さて、ちょっとしたことではありますが、間違えやすいポイントがあります。それは、$${y=(x+1)^2-3}$$まではいいものの、ここでグラフの頂点の座標を(1,-3)としてしまうことです。$${y=a(x-p)^2+q}$$のとき(p,q)なので、$${y=a(x+p)^2+q}$$のときは(-p,q)になります。よって、正しくは(-1,-3)です。
プログラム化の前に
さて、次はプログラム化…としたいところですが、この手順をプログラムで再現するのは大変です。そこで、公式を使います。式は下のようなものになります。
$$
ax^2+bx+c=a(x+\frac{b}{2a})^2-\frac{b^2-4ac}{4a}
$$
複雑に見えますが、文字式で先ほどの手順をやっているだけです。人間からすると代入がめんどくさいように感じますが、プログラム上では一発で結果が出せるこちらのほうが単純になります。この式を導き出すまでの途中式は一応下に載せておきますが、特にみる必要はないので飛ばしてもらって結構です。
$$
ax^2+bx+c=a(x^2+\frac{b}{a}x)+c\\
=a(x^2+2×\frac{b}{2a}x+(\frac{b}{2a})^2-(\frac{b}{2a})^2)+c\\
=a\{(x+\frac{b}{2a})^2-\frac{b^2}{4a^2}\}+c\\
=a(x+\frac{b}{2a})^2-\frac{b^2}{4a}+c\\
=a(x+\frac{b^2}{2a})^2-\frac{b^2-4ac}{4a}
$$
プログラム化
さて、ここまではほぼ数学の解説でしたが、ここからはPythonコードの解説をしていきます。といっても、解説するほどのものではなく、先ほどの公式に入力部で受け取った値を代入して計算するだけです。しかし、これだけでは分数を小数として出力してしまうため、それを防止する必要があります。また、$${\frac{25}{100}}$$のような出力をしないために、約分をするプログラムも必要です。
プログラムコード
ここではあまり詳しい解説はしませんが、少しやっていることがわかりにくい$${\verb|frac|}$$関数だけ解説しておきます。
from fractions import Fraction
###約分処理
def frac(n,m):
if n % m == 0:
return k(n / m)
n = int(n * 10 ** (len((str(n).split('.'))[1])))
m = int(m * 10 ** (len((str(m).split('.'))[1])))
f = Fraction(n,m)
return "%s/%s" % (f.numerator,f.denominator)
###小数のいらない.0を削除(1.0 → 1, 0.250 → 0.25など)
def k(number):
if isinstance(number,float) and number.is_integer():
return int(number)
return number
def n():
try:
###入力部分
x = list(input('a,b,cの値をカンマ(,)区切りで入力してください>>>'))
nlist = []
###カンマ区切りで入力された値をそれぞれ変数a,b,cに代入
X = ''
for Y in x:
if Y == ',':
nlist.append(X)
X = ''
else:
X += Y
nlist.append(X)
a = float(nlist[0])
b = float(nlist[1])
c = float(nlist[2])
###公式の利用
p = frac(b,2*a)
q = frac((b**2-4*a*c)*-1,4*a)
###座標出力用
v = frac(b*-1,2*a)
###float型の.0を上の関数kを使用して削除(1.0→1にする)
a = k(a)
b = k(b)
c = k(c)
###係数が1や-1のときは1を省略
if a == 1:
a = ""
elif a == -1:
a = "-"
if b == 1:
b = ""
elif b == -1:
b ="-"
###出力
print('%sx^2 + %sx + %s = %s(x + %s)^2 + %s' % (a,b,c,a,p,q))
print('このグラフはy=%sx^2のグラフの頂点を(%s,%s)に移動したものになります。' % (a,v,q))
except:
###エラー処理
print('有理数以外の数が入力されました。入力し直してください。')
print()
###説明表示
print('''
このプログラムは2次式ax^2 + bx + cをa(x + p)^2 + qの形に平方完成するプログラムです。
n()と入力すると平方完成プログラムが起動します。
起動したらa,b,cの値をそれぞれ入力してください。
*整数のみの対応です。
例:2,-1,3 出力:2x^2 + -x + 3 = 2(x + -1/4)^2 + 23/8
''')
ここでは、約分にpythonの組み込みモジュールである$${\verb|fractions|}$$モジュールの$${\verb|Fraction|}$$を使っています。しかし、これだけを利用するのは1つ問題があり、約分はしますが$${\frac{1}{1}}$$や$${\frac{3}{1}}$$などと分母1でも出力してしまうことです。これを避けるために$${\verb|frac|}$$関数の最初で分数にしない場合の出力(判定)をしています。
その次のところは少しわかりにくいですが、念のための小数用処理をしています(本来小数は入力されないはずですが…)。これは解説すると長いので省略します。その下で変数$${\verb|f|}$$に約分結果を割り当て、$${\verb|return|}$$で返しています。
小数・分数の入力方法(裏技?)
さて、このプログラムは整数のみの対応です(エラー対応用に小数処理も念のため組み入れていますが計算結果がおかしくなります)。しかし、工夫すれば小数も入力できないことはないです。ここからは完全に数学(算数かな?)の話になるので、プログラムとはほぼ無関係です。
例として$${y=\frac{1}{2}x^2-x+1}$$を考えます。
このままではプログラムに入力することができないため、どうにかしてすべての項を整数にする必要があります。そこで、次のように変形してみましょう。
$$
y=\frac{1}{2}x^2-x+1\\
2y = x^2-2x+2\\
2y = (x-1)^2+1\\
y=\frac{1}{2}\{(x-1)^2+1\}\\
y=\frac{1}{2}(x-1)^2+\frac{1}{2}
$$
両辺に一度$${2}$$を掛けることで分数を消せます。これは方程式を解くときによく使う手法なので分かる人は多いと思います。しかし、方程式と違うところは最後に元に戻さないといけないところです。ここさえ気を付ければ平方完成部分はプログラムにまかせっきりで大丈夫です!これで宿題を楽に終わらせましょう!