連立方程式 完全マスターガイド – 基礎から実用まで徹底解説

数学

「りんご3個とみかん2個で500円、りんご2個とみかん3個で450円。それぞれの値段は?」

こんな問題を解くのが連立方程式です。複数の未知数がある問題を、複数の式を組み合わせて解く数学の重要な手法です。

実は連立方程式は、買い物の計算から、GPS の位置測定、経済予測、AIの学習まで、現代社会のあらゆる場面で使われています。

この記事では、連立方程式の基本から様々な解法、そして実生活での応用まで、具体例を交えながら分かりやすく解説していきます。

スポンサーリンク

連立方程式とは?基本を理解しよう

連立方程式の定義

連立方程式とは、複数の未知数を含む複数の方程式を同時に満たす値を求める問題です。

一次連立方程式の例:

2x + 3y = 12  ... ①
x - y = 1     ... ②

この2つの式を同時に満たす x と y の値を求めます。

なぜ連立方程式が必要?

1つの方程式だけでは解けない問題:

x + y = 10

この式だけでは、x=3, y=7 も、x=6, y=4 も答えになってしまいます。

もう1つ条件を加えると:

x + y = 10
x - y = 2

これで x=6, y=4 と一意に決まります!

グラフで見る連立方程式

連立方程式の解は、グラフ上では2つの直線の交点として表されます。

import numpy as np
import matplotlib.pyplot as plt

# 連立方程式をグラフで可視化
def visualize_linear_system():
    """
    2x + 3y = 12
    x - y = 1
    をグラフ化
    """
    x = np.linspace(-2, 8, 100)
    
    # 各方程式を y について解く
    y1 = (12 - 2*x) / 3  # 2x + 3y = 12 → y = (12-2x)/3
    y2 = x - 1            # x - y = 1 → y = x - 1
    
    plt.figure(figsize=(10, 8))
    plt.plot(x, y1, 'b-', label='2x + 3y = 12', linewidth=2)
    plt.plot(x, y2, 'r-', label='x - y = 1', linewidth=2)
    
    # 交点を計算(解)
    # 代入法で解くと x = 3, y = 2
    x_solution = 3
    y_solution = 2
    
    plt.plot(x_solution, y_solution, 'go', markersize=10, label=f'解: ({x_solution}, {y_solution})')
    
    plt.grid(True, alpha=0.3)
    plt.axhline(y=0, color='k', linewidth=0.5)
    plt.axvline(x=0, color='k', linewidth=0.5)
    plt.xlabel('x', fontsize=12)
    plt.ylabel('y', fontsize=12)
    plt.title('連立方程式の解(交点)', fontsize=14)
    plt.legend(fontsize=11)
    plt.xlim(-2, 8)
    plt.ylim(-2, 6)
    
    # 解の確認を表示
    plt.text(3.5, 2.5, f'検証:\n2(3) + 3(2) = 12 ✓\n3 - 2 = 1 ✓', 
             bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.7))
    
    plt.show()

visualize_linear_system()

基本的な解法1:代入法

代入法の手順

代入法は、一つの式から一つの変数を他の変数で表し、それを別の式に代入する方法です。

例題:

x + 2y = 7  ... ①
3x - y = 4  ... ②

Step 1: ①式から x を y で表す

x = 7 - 2y  ... ③

Step 2: ③を②式に代入

3(7 - 2y) - y = 4
21 - 6y - y = 4
21 - 7y = 4
-7y = -17
y = 17/7

Step 3: y の値を③に代入して x を求める

x = 7 - 2(17/7) = 7 - 34/7 = 49/7 - 34/7 = 15/7

答え: x = 15/7, y = 17/7

代入法のメリット・デメリット

メリット:

  • 考え方がシンプルで理解しやすい
  • 計算ミスを見つけやすい

デメリット:

  • 分数が出てくると計算が複雑になる
  • 変数が多いと手間がかかる

基本的な解法2:加減法(消去法)

加減法の手順

加減法は、式を足したり引いたりして、一つの変数を消去する方法です。

例題:

2x + 3y = 13  ... ①
4x - 3y = 5   ... ②

Step 1: y の係数の絶対値が同じなので、①+②を計算

(2x + 3y) + (4x - 3y) = 13 + 5
6x = 18
x = 3

Step 2: x = 3 を①に代入

2(3) + 3y = 13
6 + 3y = 13
3y = 7
y = 7/3

答え: x = 3, y = 7/3

係数を揃える技巧

係数が揃っていない場合は、式全体に数を掛けて揃えます。

例題:

3x + 2y = 12  ... ①
2x + 5y = 13  ... ②

x を消去するために:

  • ①×2: 6x + 4y = 24
  • ②×3: 6x + 15y = 39

引き算すると:

-11y = -15
y = 15/11

3元連立方程式への拡張

3つの未知数、3つの式

x + y + z = 6     ... ①
2x - y + 3z = 9   ... ②
x + 2y - z = 2    ... ③

解法:段階的に変数を減らす

Step 1: ①と②から y を消去

① + ②: 3x + 4z = 15  ... ④

Step 2: ①×2 + ③から y を消去

3x + z = 8  ... ⑤

Step 3: ④と⑤から z を求める

④ - ⑤: 3z = 7
z = 7/3

以下、逆算して x, y を求めます。

行列を使った解法(線形代数)

行列表現

連立方程式は行列で表現できます。

2x + 3y = 12
x - y = 1

これを行列で表すと:

[2  3] [x]   [12]
[1 -1] [y] = [1]

つまり、Ax = b の形式です。

逆行列を使った解法

import numpy as np

def solve_with_matrix():
    """行列を使って連立方程式を解く"""
    
    # 2x + 3y = 12
    # x - y = 1
    
    # 係数行列
    A = np.array([[2, 3],
                  [1, -1]])
    
    # 定数項
    b = np.array([12, 1])
    
    # 方法1: 逆行列を使う
    A_inv = np.linalg.inv(A)
    x_solution = A_inv @ b
    
    print("逆行列を使った解:")
    print(f"x = {x_solution[0]:.2f}")
    print(f"y = {x_solution[1]:.2f}")
    
    # 方法2: numpy.linalg.solve を使う(推奨)
    x_solution2 = np.linalg.solve(A, b)
    
    print("\nnp.linalg.solve を使った解:")
    print(f"x = {x_solution2[0]:.2f}")
    print(f"y = {x_solution2[1]:.2f}")
    
    # 検証
    result = A @ x_solution2
    print("\n検証:")
    print(f"2x + 3y = 2({x_solution2[0]:.2f}) + 3({x_solution2[1]:.2f}) = {result[0]:.2f}")
    print(f"x - y = {x_solution2[0]:.2f} - {x_solution2[1]:.2f} = {result[1]:.2f}")
    
    return x_solution2

solution = solve_with_matrix()

クラメルの公式

2元連立方程式の解の公式:

ax + by = e
cx + dy = f

のとき、

x = (ed - bf) / (ad - bc)
y = (af - ec) / (ad - bc)

ただし、ad – bc ≠ 0(行列式が0でない)

実生活での応用例

例1:買い物の問題

def shopping_problem():
    """
    問題:
    コーヒー3杯とケーキ2個で1,400円
    コーヒー2杯とケーキ3個で1,350円
    それぞれの値段は?
    """
    
    # 3x + 2y = 1400
    # 2x + 3y = 1350
    
    A = np.array([[3, 2],
                  [2, 3]])
    b = np.array([1400, 1350])
    
    prices = np.linalg.solve(A, b)
    
    print("買い物の問題:")
    print(f"コーヒー1杯: {prices[0]:.0f}円")
    print(f"ケーキ1個: {prices[1]:.0f}円")
    
    # 検証
    total1 = 3 * prices[0] + 2 * prices[1]
    total2 = 2 * prices[0] + 3 * prices[1]
    print(f"\n検証:")
    print(f"コーヒー3杯 + ケーキ2個 = {total1:.0f}円")
    print(f"コーヒー2杯 + ケーキ3個 = {total2:.0f}円")

shopping_problem()

例2:投資の配分問題

def investment_problem():
    """
    問題:
    100万円を2つの投資商品A、Bに分けて投資
    Aの利回りは年3%、Bの利回りは年5%
    1年後の利益が3.8万円になるような配分は?
    """
    
    # x + y = 1000000 (投資総額)
    # 0.03x + 0.05y = 38000 (利益)
    
    A = np.array([[1, 1],
                  [0.03, 0.05]])
    b = np.array([1000000, 38000])
    
    allocation = np.linalg.solve(A, b)
    
    print("投資配分問題:")
    print(f"商品A: {allocation[0]:,.0f}円")
    print(f"商品B: {allocation[1]:,.0f}円")
    
    # 利益の確認
    profit_A = allocation[0] * 0.03
    profit_B = allocation[1] * 0.05
    total_profit = profit_A + profit_B
    
    print(f"\n利益内訳:")
    print(f"商品Aからの利益: {profit_A:,.0f}円")
    print(f"商品Bからの利益: {profit_B:,.0f}円")
    print(f"合計利益: {total_profit:,.0f}円")

investment_problem()

例3:化学反応の平衡

def chemical_balance():
    """
    化学反応式のバランスを取る
    例: ?CH4 + ?O2 → ?CO2 + ?H2O
    
    炭素(C): a = c
    水素(H): 4a = 2d
    酸素(O): 2b = 2c + d
    """
    
    # a=1と仮定して他の係数を求める
    # 4a = 2d → d = 2a = 2
    # a = c → c = 1
    # 2b = 2c + d → 2b = 2(1) + 2 = 4 → b = 2
    
    print("化学反応式のバランス:")
    print("CH4 + 2O2 → CO2 + 2H2O")
    
    # 検証
    print("\n原子数の確認:")
    print("左辺: C=1, H=4, O=4")
    print("右辺: C=1, H=4, O=4")
    print("バランスが取れています!")

chemical_balance()

特殊なケース:解が存在しない・無限にある

解が存在しない場合(不能)

2つの直線が平行で交わらない場合:

x + 2y = 5
x + 2y = 8

これは「x + 2y が同時に5でも8でもある」という矛盾です。

def no_solution_example():
    """解が存在しない連立方程式の例"""
    
    # グラフで可視化
    x = np.linspace(-5, 5, 100)
    y1 = (5 - x) / 2
    y2 = (8 - x) / 2
    
    plt.figure(figsize=(10, 6))
    plt.plot(x, y1, 'b-', label='x + 2y = 5', linewidth=2)
    plt.plot(x, y2, 'r-', label='x + 2y = 8', linewidth=2)
    
    plt.grid(True, alpha=0.3)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('解が存在しない連立方程式(平行線)')
    plt.legend()
    plt.xlim(-5, 5)
    plt.ylim(-2, 5)
    
    plt.text(0, 3, '平行線のため\n交点が存在しない', 
             bbox=dict(boxstyle="round,pad=0.3", facecolor="red", alpha=0.3),
             fontsize=12, ha='center')
    
    plt.show()

no_solution_example()

解が無限にある場合(不定)

2つの式が実質的に同じ場合:

x + 2y = 6
2x + 4y = 12  (①の2倍)
def infinite_solutions_example():
    """解が無限にある連立方程式の例"""
    
    # グラフで可視化
    x = np.linspace(-2, 8, 100)
    y = (6 - x) / 2
    
    plt.figure(figsize=(10, 6))
    plt.plot(x, y, 'b-', label='x + 2y = 6 (両方の式)', linewidth=3)
    
    # いくつかの解を点で表示
    x_solutions = [0, 2, 4, 6]
    y_solutions = [(6 - xi) / 2 for xi in x_solutions]
    
    plt.scatter(x_solutions, y_solutions, color='red', s=100, zorder=5)
    for xi, yi in zip(x_solutions, y_solutions):
        plt.annotate(f'({xi}, {yi})', (xi, yi), 
                    xytext=(5, 5), textcoords='offset points')
    
    plt.grid(True, alpha=0.3)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('解が無限にある連立方程式(同一直線)')
    plt.legend()
    plt.xlim(-2, 8)
    plt.ylim(-1, 4)
    
    plt.text(4, 3, '同じ直線上の\nすべての点が解', 
             bbox=dict(boxstyle="round,pad=0.3", facecolor="green", alpha=0.3),
             fontsize=12, ha='center')
    
    plt.show()

infinite_solutions_example()

ガウスの消去法(大規模な連立方程式)

アルゴリズムの実装

def gaussian_elimination(A, b):
    """
    ガウスの消去法で連立方程式 Ax = b を解く
    """
    n = len(b)
    # 拡大係数行列を作成
    Ab = np.column_stack([A.astype(float), b.astype(float)])
    
    # 前進消去
    for i in range(n):
        # ピボット選択(部分ピボット)
        max_row = i + np.argmax(np.abs(Ab[i:, i]))
        Ab[[i, max_row]] = Ab[[max_row, i]]
        
        # ピボットが0の場合はスキップ
        if Ab[i, i] == 0:
            continue
            
        # i行目以下を消去
        for j in range(i + 1, n):
            factor = Ab[j, i] / Ab[i, i]
            Ab[j, i:] -= factor * Ab[i, i:]
    
    # 後退代入
    x = np.zeros(n)
    for i in range(n - 1, -1, -1):
        x[i] = (Ab[i, -1] - np.dot(Ab[i, i+1:n], x[i+1:n])) / Ab[i, i]
    
    return x

# 使用例
def test_gaussian():
    """ガウスの消去法のテスト"""
    
    # 3元連立方程式
    # 2x + y - z = 8
    # -3x - y + 2z = -11
    # -2x + y + 2z = -3
    
    A = np.array([[2, 1, -1],
                  [-3, -1, 2],
                  [-2, 1, 2]])
    b = np.array([8, -11, -3])
    
    print("連立方程式:")
    print("2x + y - z = 8")
    print("-3x - y + 2z = -11")
    print("-2x + y + 2z = -3")
    
    # ガウスの消去法で解く
    x_gauss = gaussian_elimination(A, b)
    
    print(f"\nガウスの消去法の解:")
    print(f"x = {x_gauss[0]:.2f}")
    print(f"y = {x_gauss[1]:.2f}")
    print(f"z = {x_gauss[2]:.2f}")
    
    # NumPyの解と比較
    x_numpy = np.linalg.solve(A, b)
    print(f"\nNumPyの解(検証用):")
    print(f"x = {x_numpy[0]:.2f}")
    print(f"y = {x_numpy[1]:.2f}")
    print(f"z = {x_numpy[2]:.2f}")
    
    # 検証
    result = A @ x_gauss
    print(f"\n検証(Ax = b):")
    print(f"計算結果: {result}")
    print(f"期待値: {b}")
    print(f"誤差: {np.abs(result - b)}")

test_gaussian()

実践問題と解答

問題1:速さの問題

def speed_problem():
    """
    問題:
    AさんとBさんが同時に出発し、12km離れた地点で出会った。
    Aさんの速さはBさんの1.5倍。
    2人が出会うまでに1時間かかった。
    それぞれの速さは?
    """
    
    # x: Aさんの速さ(km/h)、y: Bさんの速さ(km/h)
    # x = 1.5y ... ①
    # x + y = 12 ... ② (1時間で合計12km)
    
    # ①を②に代入
    # 1.5y + y = 12
    # 2.5y = 12
    # y = 4.8
    
    y = 12 / 2.5
    x = 1.5 * y
    
    print("速さの問題:")
    print(f"Aさんの速さ: {x:.1f} km/h")
    print(f"Bさんの速さ: {y:.1f} km/h")
    
    # 検証
    print(f"\n検証:")
    print(f"速さの比: {x:.1f} ÷ {y:.1f} = {x/y:.1f}")
    print(f"1時間で進む距離の合計: {x:.1f} + {y:.1f} = {x+y:.1f} km")

speed_problem()

問題2:混合問題

def mixture_problem():
    """
    問題:
    濃度30%の食塩水と濃度10%の食塩水を混ぜて、
    濃度18%の食塩水を500g作りたい。
    それぞれ何g必要か?
    """
    
    # x: 30%食塩水の量(g)、y: 10%食塩水の量(g)
    # x + y = 500 ... ①
    # 0.3x + 0.1y = 0.18 × 500 = 90 ... ②
    
    A = np.array([[1, 1],
                  [0.3, 0.1]])
    b = np.array([500, 90])
    
    amounts = np.linalg.solve(A, b)
    
    print("混合問題:")
    print(f"30%食塩水: {amounts[0]:.0f}g")
    print(f"10%食塩水: {amounts[1]:.0f}g")
    
    # 検証
    total_salt = 0.3 * amounts[0] + 0.1 * amounts[1]
    concentration = total_salt / 500 * 100
    
    print(f"\n検証:")
    print(f"混合後の食塩の量: {total_salt:.1f}g")
    print(f"混合後の濃度: {concentration:.1f}%")

mixture_problem()

問題3:仕事算

def work_problem():
    """
    問題:
    ある仕事をAさん一人では12日、Bさん一人では18日かかる。
    2人で一緒に働くと何日で終わるか?
    また、Aさんが4日働いた後、残りをBさんが引き継ぐと
    全部で何日かかるか?
    """
    
    # Aさんの1日の仕事量: 1/12
    # Bさんの1日の仕事量: 1/18
    
    work_rate_A = 1/12
    work_rate_B = 1/18
    
    # 2人で働く場合
    combined_rate = work_rate_A + work_rate_B
    days_together = 1 / combined_rate
    
    print("仕事算の問題:")
    print(f"Aさんの1日の仕事量: 1/12 = {work_rate_A:.4f}")
    print(f"Bさんの1日の仕事量: 1/18 = {work_rate_B:.4f}")
    print(f"2人で働くと: {days_together:.1f}日で完了")
    
    # Aさん4日 + Bさんで残り
    work_by_A = 4 * work_rate_A
    remaining_work = 1 - work_by_A
    days_by_B = remaining_work / work_rate_B
    total_days = 4 + days_by_B
    
    print(f"\nAさん4日 + Bさんの場合:")
    print(f"Aさんが4日で終わらせる仕事: {work_by_A:.3f}")
    print(f"残りの仕事: {remaining_work:.3f}")
    print(f"Bさんが残りを終わらせる日数: {days_by_B:.1f}日")
    print(f"合計: {total_days:.1f}日")

work_problem()

連立方程式を解くプログラム

汎用的な連立方程式ソルバー

class LinearEquationSolver:
    """連立方程式を様々な方法で解くクラス"""
    
    def __init__(self, A, b):
        """
        Ax = b の形式の連立方程式
        A: 係数行列
        b: 定数ベクトル
        """
        self.A = np.array(A, dtype=float)
        self.b = np.array(b, dtype=float)
        self.n = len(b)
    
    def solve_by_inverse(self):
        """逆行列を使って解く"""
        try:
            A_inv = np.linalg.inv(self.A)
            return A_inv @ self.b
        except np.linalg.LinAlgError:
            print("逆行列が存在しません")
            return None
    
    def solve_by_cramer(self):
        """クラメルの公式で解く(2元のみ)"""
        if self.n != 2:
            print("クラメルの公式は2元連立方程式のみ対応")
            return None
        
        det_A = np.linalg.det(self.A)
        if abs(det_A) < 1e-10:
            print("行列式が0のため解けません")
            return None
        
        x = np.zeros(2)
        for i in range(2):
            A_temp = self.A.copy()
            A_temp[:, i] = self.b
            x[i] = np.linalg.det(A_temp) / det_A
        
        return x
    
    def solve_by_substitution(self):
        """代入法で解く(2元のみ)"""
        if self.n != 2:
            print("この実装は2元連立方程式のみ対応")
            return None
        
        # a1*x + b1*y = c1
        # a2*x + b2*y = c2
        a1, b1 = self.A[0]
        a2, b2 = self.A[1]
        c1, c2 = self.b
        
        if abs(a1) < 1e-10:
            if abs(a2) < 1e-10:
                return None
            # 式を入れ替える
            a1, b1, c1, a2, b2, c2 = a2, b2, c2, a1, b1, c1
        
        # 式1から x = (c1 - b1*y) / a1
        # これを式2に代入
        # a2*(c1 - b1*y)/a1 + b2*y = c2
        # a2*c1/a1 - a2*b1*y/a1 + b2*y = c2
        # y*(b2 - a2*b1/a1) = c2 - a2*c1/a1
        
        y_coef = b2 - a2*b1/a1
        y_const = c2 - a2*c1/a1
        
        if abs(y_coef) < 1e-10:
            print("解が一意に定まりません")
            return None
        
        y = y_const / y_coef
        x = (c1 - b1*y) / a1
        
        return np.array([x, y])
    
    def check_solution(self, x):
        """解を検証"""
        if x is None:
            return False
        
        result = self.A @ x
        error = np.abs(result - self.b)
        
        print("\n解の検証:")
        print(f"Ax = {result}")
        print(f"b  = {self.b}")
        print(f"誤差: {error}")
        
        return np.all(error < 1e-10)
    
    def analyze_system(self):
        """連立方程式の性質を分析"""
        rank_A = np.linalg.matrix_rank(self.A)
        rank_Ab = np.linalg.matrix_rank(np.column_stack([self.A, self.b]))
        
        print("連立方程式の分析:")
        print(f"係数行列のランク: {rank_A}")
        print(f"拡大係数行列のランク: {rank_Ab}")
        print(f"変数の数: {self.n}")
        
        if rank_A == rank_Ab == self.n:
            print("→ 解が一意に存在します")
        elif rank_A == rank_Ab < self.n:
            print("→ 解が無限に存在します")
        else:
            print("→ 解が存在しません")

# 使用例
def test_solver():
    """ソルバーのテスト"""
    
    print("=" * 50)
    print("連立方程式ソルバーのテスト")
    print("=" * 50)
    
    # テスト1: 通常の2元連立方程式
    print("\n【テスト1: 2x + 3y = 12, x - y = 1】")
    solver1 = LinearEquationSolver(
        [[2, 3], [1, -1]], 
        [12, 1]
    )
    
    solver1.analyze_system()
    
    print("\n逆行列法:")
    x1 = solver1.solve_by_inverse()
    print(f"解: x = {x1[0]:.2f}, y = {x1[1]:.2f}")
    
    print("\nクラメルの公式:")
    x2 = solver1.solve_by_cramer()
    print(f"解: x = {x2[0]:.2f}, y = {x2[1]:.2f}")
    
    print("\n代入法:")
    x3 = solver1.solve_by_substitution()
    print(f"解: x = {x3[0]:.2f}, y = {x3[1]:.2f}")
    
    solver1.check_solution(x1)
    
    # テスト2: 解が存在しない場合
    print("\n" + "=" * 50)
    print("【テスト2: x + 2y = 5, x + 2y = 8(解なし)】")
    solver2 = LinearEquationSolver(
        [[1, 2], [1, 2]], 
        [5, 8]
    )
    solver2.analyze_system()
    x4 = solver2.solve_by_inverse()

test_solver()

まとめ:連立方程式は問題解決の強力なツール

連立方程式について、基礎から応用まで解説してきました。

重要なポイント:

  1. 連立方程式は複数の条件を同時に満たす解を求める方法
  2. 代入法と加減法が基本的な解法
  3. 行列を使うと大規模な問題も効率的に解ける
  4. 解が存在しない・無限にある場合もある
  5. 実生活の様々な問題に応用できる

連立方程式が使われる場面:

  • 経済(需要と供給の均衡点)
  • 工学(電気回路の解析)
  • 物理(力の釣り合い)
  • 統計(回帰分析)
  • AI(機械学習のパラメータ最適化)

学習のステップ:

  1. 2元連立方程式の手計算をマスター
  2. グラフで意味を理解
  3. 3元以上に拡張
  4. 行列での解法を学ぶ
  5. プログラミングで実装

連立方程式は、複雑な現実世界の問題を数学的に解決する基本ツールです。ぜひマスターして、様々な問題解決に活用してください!

コメント

タイトルとURLをコピーしました