Pythonで多項式の展開をするプログラムを作ろう
終わらない夏休みの数学の宿題、特に多項式の展開の応用はめんどくさいし何しろ問題数が多い……
ここではそんな地獄を解決できるプログラムを作ります。
ページの最後にプログラムを載せておきましたので、ご自由にコピペ等でご使用ください。(結構長めだと思います)
そもそもの展開の仕組みとやり方
まずはほぼ授業のおさらいです。
結局人間もコンピューターも処理速度が違うだけでやっていることは同じなので、一度仕組みを理解していたほうがプログラムも読みやすいと思います。そこで、一度展開の仕組みとやり方をおさらいしておきましょう。
$$
(a+b)(c+d)=ab+ac+ad+bd
$$
これは数学の授業で聞き飽きるほど言われたことだと思います。当たり前のように覚えさせられますが、ではなぜこうなるのか?ということまで深堀りしてみましょう。
$$
X=(c+d) とおく。\\
(a+b)(c+d)=(a+b)X\\=aX+bX\\=a(c+d)+b(c+d)\\=ac+ad+bc+bd
$$
これは項が2つの場合ですが、3つ以上の場合も基本的に考え方は同じです。これをPythonプログラムに組み込んでいきます。
Pythonで展開の考え方を再現
(多項式A)(多項式B)の展開の考え方を簡略化すると次のようになります。
①多項式Aのn番目(n=1,2,3,…)を選ぶ
②多項式Bのm番目(m=1,2,3,…)を選ぶ
③①×②を計算
④②~③を多項式Bの項数回繰り返す
⑤①~④を多項式Aの項数回繰り返す
これをpythonプログラムにしましょう。
for文を使えば簡単にプログラムに起こすことができます。
polyA = ['a','b']
polyB = ['c','d']
#(polyA)(polyB)を計算(ここでは(a+b)(c+d))
result = []
for n in polyA:
for m in polyB:
result.append(n + m)
#結果の出力
print(' + '.join(result))
このプログラムはpolyA,polyBに文字列のリストで項を入力します。そして、このプログラムは2つの項同士だけでなく3つの項同士、また項の数が違っても展開ができます。Pythonでは文字列の足し算が実質数学の掛け算と同じなので簡単にプログラミングできます。
じゃあこれだけで完成だ、というわけにはいきません。これでは問題が山積みです。たとえば、n='a',m='-b'だったときに(n+m)は本来-abにならなければいけないのですが、a-bとなってしまいます。まさか$${-ab=a-b}$$にするわけにはいけないので、マイナスの処理も必要になります。また、n='2',m='3'のときは(n+m)は23になってしまい、$${2×3=6}$$という小学生でもわかる計算が成り立ちません。
また、これらの問題を解決できて、
$${(a+b)(a-b)=a^2-ab+ab-b^2}$$
という展開まではできたとしても、同類項をまとめなければいけませんよね。上の展開程度だったらまだ同類項がまとまっていなくても何とかなりますが、もっと複雑なものだとより見づらくなってしまいます。
累乗,係数,同類項のまとめ方の考え方
累乗
累乗は、同じ文字が項に何個あるかを判定して、それを指数にします。また、重複したものは指数の処理をしてから削除しなければいけません。リストの重複削除はlist(set(重複削除したいリスト名))でできるので、これを使います。重複要素が何個あるかは、collectionsモジュールの中にあるCounterを使えばできます。
係数
2xと3yの掛け算の場合、答えは23xyではなく6xyです。しかしPythonでは文字列の掛け算なので整列しても処理をしなければ23xyになってしまいます。じゃあ23xyを6xyにするプログラムを作ればいいではないかと思うかもしれませんが、本当に結果が23xyのパターン(xと23yとか)があるのでこれは無理です。よって掛け算する前に係数と文字を分けてそれぞれ掛け算すればいいです。
これは 要素 in リスト でリストのなかに要素が入っているか判定できるので、これを使って数字があるかを判定するとできます。
同類項
同類項に関しては、ほぼ係数と同じです。
同類項をまとめる際は係数を別にして計算するので、これも係数と文字を分離する必要があります。やり方は係数の時と同じです。
プログラムコード
ここに関してはあまり解説は載せません。ただし、multiply_coefficients関数だけはやってることがわかりにくいと思うので解説を入れています。また、expand関数のtry-except構文が多すぎて汚いのはほかの方法がパッと思いつかなかっただけです。(今考えたらtype関数をつかえばいけたかも)
from collections import Counter
import re
def multiply_coefficients(expression1, expression2):
# 数字部分と変数部分を抽出する
pattern = r'([-]?\d+)([a-z]*)'
# 式1から数字部分と変数部分を抽出
match1 = re.match(pattern, expression1)
num1 = int(match1.group(1)) if match1.group(1) else 1
var1 = match1.group(2)
# 式2から数字部分と変数部分を抽出
match2 = re.match(pattern, expression2)
num2 = int(match2.group(1)) if match2.group(1) else 1
var2 = match2.group(2)
# 数字部分を掛け算し、変数部分を連結して結果を返す
result = num1 * num2
return str(result) + var1 + var2
def n(string):
try:
return str(int(string))
except:
string = list(string)
C = 0
minus = 1
count = Counter(string)
string = list(set(string))
string.sort()
while (len(string) - 1) >= C:
if string[C] == '-':
minus = minus * ((-1)**count['-'])
del string[C]
else:
if count[string[C]] >= 2:
string[C] += '^%s' % count[string[C]]
C += 1
string = ''.join(string)
if minus == -1:
string = '-' + string
return string
def m(string):
string = list(string)
string.sort(reverse=True)
return ''.join(string)
def expand(poly1,poly2):
newlist = []
strlist = []
nlist = []
for A in poly1:
for B in poly2:
try:
newlist.append(str(int(A)*int(B)))
except:
try:
newlist.append(m(multiply_coefficients(A,B)))
except:
newlist.append(m(A+B))
nlist.append(None)
C = 0
newlist.sort()
for D in newlist:
newlist[C] = n(newlist[C])
C += 1
C = 0
for D in newlist:
strlen = list(D)
while len(strlen) > 0 and strlen[0] in ['0','1','2','3','4','5','6','7','8','9','-']:
if nlist[C] == None:
nlist[C] = strlen[0]
else:
nlist[C] += strlen[0]
del strlen[0]
newlist[C] = ''.join(strlen)
C += 1
strlen = newlist
for C in range(0,len(nlist)):
if nlist[C] == None:
nlist[C] = 1
elif nlist[C] == '-':
nlist[C] = -1
else:
nlist[C] = int(nlist[C])
C = 0
count = Counter(strlen)
newlist = []
while (len(strlen) - 1) >= C:
N = 0
if count[strlen[C]] >= 2:
for D in range(0,count[strlen[C]]):
N += nlist[C]
C += 1
C -= 1
else:
N = nlist[C]
if N == 0:
pass
elif N == 1:
newlist.append(strlen[C])
elif N == -1 and strlen[C] != '':
newlist.append('-'+strlen[C])
else:
newlist.append(str(N)+strlen[C])
C += 1
return newlist
def a():
"括弧を閉じてEnterを押してください"
p1 = list(input("多項式Aの項をカンマ(,)区切りで入力してください(例:2aa,-b)>>>"))
p2 = list(input("多項式Bの項をカンマ(,)区切りで入力してください(例:-a,5bbb)>>>"))
plist1 = []
plist2 = []
X = ''
try:
for Y in p1:
if Y == ',':
plist1.append(X)
X = ''
else:
X += Y
plist1.append(X)
X = ''
for Y in p2:
if Y == ',':
plist2.append(X)
X = ''
else:
X += Y
plist2.append(X)
result = expand(plist1,plist2)
print("(%s)(%s)" % (' + '.join(plist1),' + '.join(plist2)))
print("= %s" % ' + '.join(result))
except:
print("無効な値が入力されました。入力し直してください。")
print()
print('''
このプログラムは(多項式A)(多項式B)の展開をするプログラムです。
a()と入力するとプログラムが起動します。
【入力時の注意点】
・必ず各項をカンマ区切りで入力してください。
・小数は切り捨てで計算されるため、工夫して(2倍するなど)の入力をお願いします。
・累乗の入力はx^2 → xx,a^3 → aaa のように入れてください。
・係数は通常どおり2x,-5yのように入力してください。
上の注意点に従わずに入力した場合、計算がおかしくなる場合があります。
''')
まあまあ長いですがそれだけ実用性もあります。試しに某AI(ChatG○T)に展開の難問を出してもらって解かせてみたところ全問正解だったので普通に宿題を解く程度でしたら確実だと思います。コピペして宿題を一瞬で終わらせましょう!