【Python】連立方程式を一瞬で解く!掃き出し法の仕組みと実装をわかりやすく解説

こんにちは。ゆうせいです。

みなさんは、数学の授業で連立方程式を解くのに苦労した経験はありませんか。XやYの値を行ったり来たりさせながら計算するのは、とても大変ですよね。もし、その計算をコンピュータが一瞬でやってくれたら便利だと思いませんか。

今回は、連立一次方程式をプログラムで解くためのアルゴリズムである、掃き出し法(ガウス・ジョルダン法)について解説します。

数学が苦手な方でも大丈夫です。高校生でもわかるように、仕組みからPythonでの実装まで、丁寧に紐解いていきます。さあ、一緒にプログラミングと数学の世界へ飛び込んでみましょう。

そもそも掃き出し法ってなに

掃き出し法とは、一言で言えば連立方程式を機械的に解くための手順のことです。

みなさんが普段、手計算で行っている「代入法」や「加減法」を、コンピュータが扱いやすいように整理したルールブックのようなものだと思ってください。

この手法を理解するには、まず行列という道具を知る必要があります。

行列は数字のお弁当箱

行列とは、数字を長方形の形に並べて括弧で囲ったものです。

例えば、次のような連立方程式があるとします。

2x + y = 5

x + y = 3

この方程式の、XやYといった文字を取り払い、数字だけを抜き出して並べてみましょう。

2 1 5

1 1 3

このように数字だけを並べたものを拡大係数行列と呼びます。専門用語が出てきましたが、難しく考える必要はありません。変数の書かれたラベルを剥がして、中身の数字だけをお弁当箱のようにきれいに詰めた状態だとイメージしてください。

掃き出し法の仕組みを直感的に理解する

掃き出し法のゴールは、このお弁当箱の中身を整理整頓して、最終的に次のような形にすることです。

1 0 正解の数字

0 1 正解の数字

左側の部分が、斜めに1が並び、それ以外が0になっている状態を目指します。これを単位行列と呼びます。

なぜこの形を目指すのでしょうか。

この形をもう一度、方程式に戻して考えてみてください。

1x + 0y = 正解の数字

0x + 1y = 正解の数字

つまり、X = 正解、Y = 正解 という形になり、答えが求まっている状態になるのです。素晴らしい仕組みだと思いませんか。

手順はたったの2ステップ

このゴールにたどり着くために、私たちは行基本変形という操作を行います。名前は難しそうですが、やることは単純です。

ある行を何倍かする

ある行に、別の行を何倍かして足す

これだけです。具体的には、ピボットと呼ばれる基準となる数字を使って、縦の列をきれいに掃除していく作業を行います。

ステップ1:ピボットを1にする

まず、注目している行の対角線上にある数字(ピボット)を1にします。もし数字が2なら、行全体を2で割ればいいのです。

ステップ2:ピボットの上下を0にする

次に、そのピボットを使って、同じ列にある他の行の数字を0にします。

まるでオセロの石をひっくり返すように、次々と数字を1と0に変えていくパズルだと思ってください。この手順を繰り返すことで、コンピュータは答えを導き出します。

Pythonで実装してみよう

では、いよいよPythonを使ってこの手順をプログラムにしてみましょう。

ここでは、数値計算を効率よく行うためのライブラリであるNumPyを使用します。

まずはコード全体を見てください。

import numpy as np

def hakidashi(matrix):
    # 行列の大きさを取得します
    n = len(matrix)

    # 0列目から順番に計算していきます
    for i in range(n):
        
        # 1. ピボットを1にする処理
        pivot = matrix[i][i]
        matrix[i] = matrix[i] / pivot
        
        # 2. ピボット以外の行を0にする処理
        for j in range(n):
            if i != j:
                # 0にしたい行の係数を取得
                ratio = matrix[j][i]
                # ピボット行を定数倍して引く
                matrix[j] = matrix[j] - ratio * matrix[i]
                
    return matrix

# 方程式の係数をセットします
# 2x + y = 5
# x + y = 3
A = np.array([[2.0, 1.0, 5.0],
              [1.0, 1.0, 3.0]])

# 関数を実行して結果を表示します
result = hakidashi(A)
print(result)

コードのポイント解説

注目すべきはピボットの割り算

matrix[i] = matrix[i] / pivot の部分を見てください。

ここでは、行全体をピボット(対角成分)で割っています。NumPyを使うと、for文で一つずつ計算しなくても、行ごとまとめて割り算ができるので非常に便利です。

縦の列を掃除するループ

if i != j: という条件分岐がありますね。

これは、自分自身の行(ピボットのある行)以外のすべての行に対して、引き算を行うためのものです。

matrix[j] = matrix[j] - ratio * matrix[i]

この一行こそが、掃き出し法の真髄です。基準となる行(matrix[i])を ratio 倍して、対象の行(matrix[j])から引くことで、目的の場所を見事に0にしています。

掃き出し法のメリットとデメリット

プログラムは完成しましたが、この手法は完璧なのでしょうか。良い点と注意すべき点を整理しておきましょう。

メリット

最大のメリットは、手順が明確でプログラムに落とし込みやすいことです。

変数が2つでも、100個でも、やることは変わりません。ループ処理を使えば、どんなに巨大な連立方程式でも同じロジックで解くことができます。汎用性が非常に高いのです。

デメリット

一方で、割り算を多用するため、コンピュータ特有の誤差が生じることがあります。

コンピュータは無限に続く小数を完璧に表現できません。これを丸め誤差といいます。

例えば、1 \div 3 \times 3 が、厳密には 0.9999... となり、1に戻らないことがあります。変数が数千、数万と増えるにつれて、この小さな誤差が積み重なり、無視できないズレを生む可能性があるのです。

また、計算量も変数の数が増えると急激に増大します。大規模なデータ解析では、より高速な別のアルゴリズムが使われることもあります。

今後の学習の指針

いかがでしたでしょうか。

ただの数字の羅列に見えた行列が、実は方程式を解くための強力なツールであることが実感できたなら嬉しいです。

今回は基礎的な実装を紹介しましたが、ここから先にはさらに広大な線形代数という海が広がっています。

例えば、ピボットが0になってしまう場合はどうすればいいのでしょうか。あるいは、答えが存在しない場合はどう判定すればいいのでしょうか。

次は、そのような例外処理を含めた「ピボット選択」や、より高速な解法である「LU分解」について学んでみると良いでしょう。

プログラミングを通して数学に触れると、公式の意味が生きた知識として身につきます。ぜひ、ご自身の手でコードを書き換えて、色々な方程式を解かせてみてください。

それでは、また次の記事でお会いしましょう。

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。