0Pricing
R Academy · 강의

solve()로 연립 선형 방정식 풀기

Ax = b 연립방정식의 해를 구하고 행렬의 역행렬을 계산합니다.

solve()로 연립 선형 방정식 풀기은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 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): 역행렬

인수를 하나만 사용해 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)에 가까워야 합니다. 하나의 잔차 크기를 확인하려면 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()

조건수는 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²) 대 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/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“solve()로 연립 선형 방정식 풀기”에서 뭘 배우나요?

Ax = b 연립방정식의 해를 구하고 행렬의 역행렬을 계산합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“solve()로 연립 선형 방정식 풀기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 행렬 곱셈과 행렬식
  2. solve()로 연립 선형 방정식 풀기
  3. 고유값과 고유벡터
  4. SVD, QR 및 촐레스키 분해
← R Academy(으)로 돌아가기