0Pricing
R Academy · レッスン

solve()による連立一次方程式の解法

Ax = b の連立方程式を解き、行列の逆行列を計算します。

「solve()による連立一次方程式の解法」はCoddyKit上の無料R Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

線形システム:Ax = b

連立一次方程式は Ax = b と表せます。ここで A は係数行列、x は未知ベクトル、b は右辺ベクトルです。x を解析的に解くとは、x = A⁻¹b を計算することを意味します。

# System of equations:
# 2x + y = 5
# x + 3y = 7

# Matrix form: A %*% x = b
A <- matrix(c(2, 1,
              1, 3), nrow = 2, byrow = TRUE)
b <- c(5, 7)

# What are A and b?
print(A)
print(b)
cat('We want to find x such that A %*% x = b')

solve(A, b):直接解法

solve(A, b) は Ax = b を x について解きます。内部では LU 分解を使用します。これは、A⁻¹ を明示的に計算してから b を乗算するよりも、数値的に安定で効率的です。

A <- matrix(c(2, 1,
              1, 3), nrow = 2, byrow = TRUE)
b <- c(5, 7)

# Solve Ax = b
x <- solve(A, b)
print(x)  # x[1] = ?, x[2] = ?

# Verify: A %*% x should equal b
residual <- A %*% x - b
print(residual)  # Should be near zero

# Manual check:
# 2*(8/5) + (9/5) = 16/5 + 9/5 = 25/5 = 5 ✓
# 1*(8/5) + 3*(9/5) = 8/5 + 27/5 = 35/5 = 7 ✓

solve(A):逆行列

引数を 1 つだけ指定して solve(A) を呼び出すと、A の逆行列 A⁻¹(A %*% A⁻¹ = I を満たす行列)が返されます。Ax=b を解く場合には使用せず、直接 solve(A,b) を使ってください(より高速で安定しています)。

A <- matrix(c(4, 3,
              3, 2), nrow = 2, byrow = TRUE)

# Compute inverse
A_inv <- solve(A)
print(A_inv)

# Verify: A %*% A_inv = I
A %*% A_inv  # Should be identity matrix
round(A %*% A_inv, 10)

# Also A_inv %*% A = I
round(A_inv %*% A, 10)

# det(A) != 0 required for invertibility
det(A)  # -1 (nonzero, so invertible)

解の確認

必ず A %*% x - b を計算して解を検証してください。浮動小数点演算のため残差は厳密には 0 になりませんが、マシンイプシロン(~1e-15)に近い値になるはずです。残差の大きさを 1 つの値で確認するには norm() を使います。

A <- matrix(c(3, -1, 2,
              1,  4, 0,
             -2,  1, 5), nrow = 3, byrow = TRUE)
b <- c(1, 2, 3)

# Solve
x <- solve(A, b)
cat('Solution x:\n'); print(x)

# Residual check
residual <- A %*% x - b
cat('Residual vector:\n'); print(residual)

# Residual norm (should be near 0)
resid_norm <- sqrt(sum(residual^2))
cat('Residual norm:', resid_norm, '\n')
# Expected: something like 2e-16

条件数:kappa()

A の条件数は、b の摂動に対して解がどの程度敏感かを表します。条件数が大きいと、b の小さな誤差が x の大きな誤差につながります。このようなシステムは悪条件です。

# Well-conditioned matrix
A_good <- matrix(c(2, 1, 1, 3), nrow = 2, byrow = TRUE)
kappa(A_good)  # Small -> good

# Ill-conditioned (nearly singular) matrix
A_bad <- matrix(c(1.000, 1.001,
                  1.001, 1.002), nrow = 2, byrow = TRUE)
kappa(A_bad)  # Very large -> bad!

# Rule of thumb: kappa > 1/machine_epsilon is trouble
.Machine$double.eps  # ~2.2e-16

# For A_bad: you lose about log10(kappa) digits of precision
cat('Digits lost:', log10(kappa(A_bad)), '\n')

上三角行列に対する backsolve()

backsolve(R, b) は、R が上三角行列の場合に、後退代入を使って Rx = b を解きます。三角行列のシステムでは一般的な solve() よりもはるかに高速で、計算量は O(n²) です(solve() は O(n³))。

# Upper triangular system: Rx = b
# 2x + 3y + z = 14
#      5y + 2z = 13
#           4z = 8
R <- matrix(c(2, 3, 1,
              0, 5, 2,
              0, 0, 4), nrow = 3, byrow = TRUE)
b <- c(14, 13, 8)

# Solve using back-substitution
x <- backsolve(R, b)
print(x)  # z=2, y=(13-4)/5=9/5, x=(14-3*9/5-2)/2

# Verify
all.equal(as.vector(R %*% x), b)  # TRUE

# Compare with general solve
x_general <- solve(R, b)
all.equal(x, x_general)  # TRUE (same result)

下三角行列に対する forwardsolve()

forwardsolve(L, b) は、L が下三角行列の場合に、前進代入を使って Lx = b を解きます。backsolve() と相補的な関係にあり、両者を組み合わせることで LU 分解によるソルバーの基盤となります。

# Lower triangular system: Lx = b
# 3x            = 6
# 2x + 4y       = 10
# x  + 2y + 5z  = 16
L <- matrix(c(3, 0, 0,
              2, 4, 0,
              1, 2, 5), nrow = 3, byrow = TRUE)
b <- c(6, 10, 16)

# Solve using forward-substitution
x <- forwardsolve(L, b)
print(x)  # x=2, y=(10-4)/4=1.5, z=(16-2-3)/5=2.2

# Verify
all.equal(as.vector(L %*% x), b)  # TRUE

# Use case: solving L*U*x = b
# forwardsolve(L, b) -> y, then backsolve(U, y) -> x

複数の右辺

B が行列である solve(A, B) は、B のすべての列について AX = B を同時に解きます。各列に対して solve(A, b) を個別に呼び出すより効率的です。

A <- matrix(c(2, 1,
              1, 3), nrow = 2, byrow = TRUE)

# Solve for two right-hand sides simultaneously
B <- matrix(c(5, 7,   # first system
              3, 1),  # second system
            nrow = 2, byrow = TRUE)

# X[:,1] solves Ax = B[:,1]
# X[:,2] solves Ax = B[:,2]
X <- solve(A, B)
print(X)

# Verify both solutions
A %*% X  # Should equal B
all.equal(A %*% X, B)  # TRUE

特異行列の検出

特異行列に対して solve(A) を呼び出すとエラーが発生します。解く前に det(A) または rcond(A)(条件数の逆数)を確認してください。堅牢なコードにするには tryCatch() を使います。

# Singular matrix (rows are linearly dependent)
S <- matrix(c(1, 2,
              2, 4), nrow = 2, byrow = TRUE)
det(S)     # 0 -> singular
kappa(S)   # Inf

# Safe solve with tryCatch
safe_solve <- function(A, b) {
  tryCatch(
    solve(A, b),
    error = function(e) {
      cat('Matrix is singular or nearly so:\n')
      cat(e$message, '\n')
      return(NULL)
    }
  )
}

result <- safe_solve(S, c(1, 2))
print(result)  # NULL

solve() による最小二乗法

過剰決定系(未知数より方程式の数が多い場合)には、厳密な解が存在しません。最小二乗解は ||Ax - b||² を最小化します。正規方程式 A'Ax = A'b を解くことで求められます。

# Overdetermined: 4 equations, 2 unknowns (y = a + b*x)
set.seed(1)
x_vals <- c(1, 2, 3, 4)
y_vals <- c(2.1, 4.0, 5.9, 8.2)  # approx y = 0 + 2x
A <- cbind(1, x_vals)  # Design matrix (4x2)
b <- y_vals

# Normal equations: (A'A) beta = A'b
AtA <- crossprod(A)
Atb <- crossprod(A, b)
beta_ols <- solve(AtA, Atb)
print(beta_ols)  # Intercept ~0.1, slope ~2.0

# Residual sum of squares
y_hat <- A %*% beta_ols
rss <- sum((b - y_hat)^2)
cat('RSS:', rss)

安定性のための qr.solve()

悪条件または過剰決定のシステムでは、qr.solve(A, b) は solve() より数値的に安定です。LU 分解の代わりに QR 分解を使用します。lm() は内部でこの方法を使用します。

# For overdetermined system, qr.solve is preferred
x_vals <- c(1, 2, 3, 4, 5)
y_vals <- c(1.9, 4.1, 6.0, 7.8, 10.1)
A <- cbind(1, x_vals)
b <- y_vals

# qr.solve handles overdetermined systems directly
beta_qr <- qr.solve(A, b)
print(beta_qr)  # intercept, slope

# Equivalent to:
beta_lm <- coef(lm(y_vals ~ x_vals))
all.equal(beta_qr, beta_lm, check.names = FALSE)  # TRUE

# For well-determined square systems, solve() is fine
# For overdetermined or ill-conditioned: use qr.solve()

理解度チェック

R における線形システムの解法について理解度を確認しましょう。

まとめ:線形システムの解法

重要なポイント: solve(A, b) は Ax=b を直接解くため、推奨されます。solve(A) は A⁻¹ を計算するため、解法には使用しないでください。backsolve(R,b) と forwardsolve(L,b) は三角行列のシステムに適しています。kappa(A) は条件の良さを測定し、値が大きいほど解が不安定です。過剰決定系には qr.solve() を使います。必ず A %*% x - b で解を検証してください。

# Summary of solve() functions:
A <- matrix(c(3, 1, 1, 2), nrow = 2)
b <- c(9, 8)

# Solve Ax = b
x <- solve(A, b)
print(x)  # c(2, 3)

# Check conditioning
kappa(A)  # Small -> well-conditioned

# Verify
max(abs(A %*% x - b))  # Near zero

# For triangular systems:
R <- matrix(c(2, 3, 0, 4), nrow = 2, byrow = TRUE)
backsolve(R, c(8, 4))  # x=c(1, 1)

# For least squares (overdetermined):
# qr.solve(design_matrix, y)

よくある質問

「solve()による連立一次方程式の解法」レッスンは無料ですか?

はい。「solve()による連立一次方程式の解法」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「solve()による連立一次方程式の解法」で何を学びますか?

Ax = b の連立方程式を解き、行列の逆行列を計算します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「solve()による連立一次方程式の解法」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 行列の乗算と行列式
  2. solve()による連立一次方程式の解法
  3. 固有値と固有ベクトル
  4. SVD、QR、コレスキー分解
← R Academyに戻る