수치해석

재료공학도를 위한 수치해석

1. 학습 목표

이번 강의가 끝나면 다음을 할 수 있어야 한다.

  • 접선의 방정식으로 Newton-Raphson 반복식을 유도할 수 있다.
  • 함수와 도함수를 이용하여 다음 근삿값을 계산할 수 있다.
  • 초기값이 수렴 결과에 미치는 영향을 설명할 수 있다.
  • 잔차와 근의 위치 오차를 구분할 수 있다.
  • Newton-Raphson 방법을 Python으로 구현할 수 있다.

2. Newton-Raphson 방법

이분법은 근을 포함하는 구간을 항상 유지하므로 안정적이지만, 구간의 길이가 한 번에 절반씩만 줄어든다. Newton-Raphson 방법은 현재점에서 함수의 접선을 만들고, 접선이 $x$축과 만나는 점을 다음 근삿값으로 사용한다.

Newton-Raphson 방법의 반복 과정

2.1. 접선을 이용한 반복식 유도

현재 근삿값을 $x_{k}$이라고 하자. 점 $(x_k,f(x_k))$에서 접선의 기울기는 $f’(x_k)$이므로 접선의 방정식은 다음과 같다.

\[y-f(x_k)=f'(x_k)(x-x_k)\]

이 접선이 $x$축과 만나는 점을 $x_{k+1}$이라고 하면 $y=0$이므로

\[-f(x_k)=f'(x_k)(x_{k+1}-x_k)\]

이다. 이를 $x_{k+1}$에 대해 정리하면 Newton-Raphson 반복식을 얻는다.

\[\boxed{ x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)} }\]

현재점에서 계산한 보정량을 $\Delta x_k$이라고 쓰면

\[\Delta x_k=-\frac{f(x_k)}{f'(x_k)}, \qquad x_{k+1}=x_k+\Delta x_k\]

으로도 표현할 수 있다.

2.2. 알고리듬

  1. 함수 $f(x)$와 도함수 $f’(x)$를 준비한다.
  2. 초기값 $x_0$와 허용오차를 정한다.
  3. $f(x_k)$과 $f’(x_k)$을 계산한다.
  4. $f’(x_k)=0$이거나 0에 매우 가까우면 계산을 중단한다.
  5. 반복식으로 $x_{k+1}$을 구한다.
  6. 종료 조건을 만족하면 계산을 마치고, 그렇지 않으면 반복한다.

주로 다음과 같은 종료 조건을 사용한다.

\[|f(x_k)|\leq\mathrm{tol}_f\]

또는

\[|x_{k+1}-x_k|\leq\mathrm{tol}_x\]

** (중요) 무한 반복을 막기 위해 최대 반복 횟수도 함께 설정한다.

3. 예제: $x^2=2$

방정식 $x^2=2$의 두 해는 $x=\pm\sqrt{2}$이다. 이를 근 찾기 문제로 바꾸면

\[f(x)=x^2-2, \qquad f'(x)=2x\]

이다. 따라서 반복식은

\[x_{k+1} =x_k-\frac{x_k^2-2}{2x_k} =\frac{1}{2}\left(x_k+\frac{2}{x_k}\right)\]

이다.

3.1. 손으로 계산하기

초기값을 $x_0=1$로 정하고 반복해보자.

$k$ $x_k$ $ f(x_k) = x_k^2-2 $
0 $1.000000000$ $1.000000000$        
1 $1.500000000$ $0.250000000$        
2 $1.416666667$ $0.006944444$        
3 $1.414215686$ $6.0073\times 10^{-6}$        
4 $1.414213562$ $4.51\times 10^{-12}$        

첫 번째 반복은 다음과 같다.

\[x_1 =1-\frac{1^2-2}{2\times1} =1.5\]

두 번째 반복은 다음과 같다.

\[x_2 =1.5-\frac{1.5^2-2}{2\times1.5} =1.416666666\ldots\]

허용 잔차를 $10^{-10}$으로 정하면 네 번째 반복에서

\[|f(x_4)|<10^{-10}\]

이므로 계산을 종료할 수 있다.

3.2. 잔차와 근의 오차

$|f(x_n)|$은 근삿값을 함수에 대입했을 때 0에서 얼마나 떨어져 있는지를 나타내는 잔차(residual)이다.

\[\text{잔차}=|f(x_n)|\]

참근 $x^*$를 알고 있을 때 계산할 수 있는 근의 절대 오차는 다음과 같다.

\[\text{근의 절대 오차}=|x^*-x_n|\]

수치해석에서는 참근을 모르는 경우가 많으므로 잔차 또는 연속된 두 근삿값의 차이를 종료 조건으로 사용한다. 잔차와 근의 위치 오차는 서로 같은 값이 아니다.

3.3. Python 구현

다음 함수는 근의 근삿값, 반복 횟수와 반복 기록을 반환한다.

def newton_raphson(f, df, x0, tol=1e-10, max_iter=50):
    x = float(x0)
    history = [x]

    for iteration in range(max_iter+1):
        fx = f(x)

        if abs(fx) <= tol:
            return x, iteration, history

        if iteration == max_iter:
            break

        dfx = df(x)
        if abs(dfx) < 1e-14:
            raise ZeroDivisionError("도함수가 0에 너무 가까워 계산할 수 없습니다.")

        x = x-fx/dfx
        history.append(x)

    raise RuntimeError("최대 반복 횟수 안에 수렴하지 않았습니다.")

$x^2-2=0$에 적용해보자.

import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return x**2-2

def df(x):
    return 2*x

root, iterations, history = newton_raphson(f, df, x0=1.0)

print("root:", root)
print("f(root):", f(root))
print("iterations:", iterations)

반복에 따른 근삿값과 잔차의 변화를 그려보자.

history = np.array(history)
residuals = np.abs(f(history))
k = np.arange(len(history))

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

axes[0].plot(k, history, "o-")
axes[0].axhline(np.sqrt(2), color="black", linestyle="--",
                label=r"$\sqrt{2}$")
axes[0].set_xlabel("iteration")
axes[0].set_ylabel(r"$x_n$")
axes[0].grid()
axes[0].legend()

axes[1].semilogy(k, residuals, "o-")
axes[1].set_xlabel("iteration")
axes[1].set_ylabel(r"$|f(x_n)|$")
axes[1].grid()

plt.tight_layout()
plt.show()

4. 초기값과 수렴

Newton-Raphson 방법은 초기값이 필요하며, 초기값에 따라 결과가 달라질 수 있다.

  • $x_0=1$로 시작하면 $+\sqrt{2}$에 수렴한다.
  • $x_0=-1$로 시작하면 $-\sqrt{2}$에 수렴한다.
  • $x_0=0$이면 $f’(0)=0$이므로 반복식의 분모가 0이 되어 계산할 수 없다.
for x0 in [1.0, -1.0]:
    root, iterations, history = newton_raphson(f, df, x0)
    print("x0 =", x0, "root =", root, "iterations =", iterations)

Newton–Raphson 방법은 좋은 초기값에서 시작하면 단순근 근처에서 매우 빠르게 수렴한다. 그러나 다음과 같은 경우에는 수렴하지 않을 수 있다.

  • 반복 중 도함수가 0 또는 매우 작은 값이 되는 경우
  • 초기값이 근에서 너무 멀리 떨어진 경우
  • 반복값이 함수의 정의역을 벗어나는 경우
  • 여러 근 중 예상하지 않은 근으로 이동하는 경우

따라서 그래프를 먼저 그리거나 이분법으로 근이 있는 구간을 확인한 뒤 초기값을 정하면 도움이 된다.

5. 적용 예제

5.1. $x^2=3$

\[f(x)=x^2-3, \qquad f'(x)=2x\] \[x_{n+1}=x_n-\frac{x_n^2-3}{2x_n}\]

양의 근을 구하려면 양의 초기값을 사용해보자.

5.2. $x^4=2$

\[f(x)=x^4-2, \qquad f'(x)=4x^3\] \[x_{n+1}=x_n-\frac{x_n^4-2}{4x_n^3}\]

$x_0=0$에서는 도함수가 0이므로 시작할 수 없다.

5.3. $\ln x=1$

\[f(x)=\ln x-1, \qquad f'(x)=\frac{1}{x}\] \[x_{n+1} =x_n-\frac{\ln x_n-1}{1/x_n} =x_n-x_n(\ln x_n-1)\]

로그 함수의 정의역 때문에 모든 반복값은 $x_n>0$이어야 한다. 해는 $x=e$이다.

5.4. $\cos x=0.3$

\[f(x)=\cos x-0.3, \qquad f'(x)=-\sin x\] \[x_{n+1} =x_n-\frac{\cos x_n-0.3}{-\sin x_n}\]

이 방정식은 주기성 때문에 여러 근을 갖는다. 초기값에 따라 서로 다른 근으로 수렴할 수 있으며, $\sin x_n=0$인 점에서는 반복식을 계산할 수 없다.

6. 이분법과 비교

항목 이분법 Newton–Raphson 방법
필요한 정보 $f(x)$와 근을 둘러싼 구간 $f(x)$, $f’(x)$와 초기값
한 단계의 계산 중점과 함수값 함수값과 도함수
수렴 특성 느리지만 안정적 좋은 초기값에서는 매우 빠름
주요 주의점 연속성과 부호 변화가 필요 도함수 0과 초기값에 주의
근을 포함하는 구간 반복 내내 유지 일반적으로 유지하지 않음

7. 정리

  • Newton-Raphson 방법은 현재점의 접선과 $x$축의 교점을 다음 근삿값으로 사용한다.
  • 반복식은 $x_{k+1}=x_k-f(x_k)/f’(x_k)$이다.
  • 좋은 초기값에서 단순근에 가까우면 매우 빠르게 수렴할 수 있다.
  • 잔차 $ f(x_k) $와 근의 위치 오차 $ x^*-x_k $는 서로 다르다.
  • 도함수가 0에 가깝거나 반복값이 정의역을 벗어나면 실패할 수 있다.

8. 연습 문제

강의에서 배운 식을 직접 적용하는 기초 문제이다.

  1. 다음 빈칸을 채워라.

    \[x_{k+1}=x_k-\frac{(    )}{(    )}\]

    Newton-Raphson 방법은 현재점에서 함수의 (    )을 이용한다.

  2. $f(x)=x^2-2$일 때 $f’(x)$를 구하고 $x_0=1$에서 $x_1$을 계산하라.

  3. $f(x)=x^2-3$이고 $x_0=2$일 때 $x_1$과 $x_2$를 계산하라.

  4. 다음 함수의 도함수를 구하고 Newton-Raphson 반복식을 작성하라.

    1. $f(x)=x^3-8$
    2. $f(x)=\ln x-2$
  5. 다음 값 중 $f(x)=x^2-2$의 Newton-Raphson 초기값으로 바로 사용할 수 없는 값을 고르고 이유를 설명하라.

    \[-1,\qquad0,\qquad1\]
  6. $x^2-2=0$에서 $x=1.5$일 때 잔차 $ f(x) $와 참근을 이용한 절대 오차
    $ \sqrt{2}-x $를 각각 구하라. 두 값이 같은지도 확인하라.
  7. 강의의 newton_raphson 함수를 사용하여 $x^2-3=0$의 양의 근을 구하라.

    def f(x):
        return x**2-3
    
    def df(x):
        return 2*x
    
    root, iterations, history = newton_raphson(f, df, x0=2.0)
    print(root)
    print(iterations)
    

    계산 결과를 np.sqrt(3)과 비교하라.

  8. Newton-Raphson 방법과 이분법을 비교하여 다음 빈칸을 채워라.

    • 도함수가 필요한 방법: (        )
    • 근을 포함하는 시작 구간이 필요한 방법: (        )
    • 좋은 초기값에서 일반적으로 더 빠른 방법: (        )