# 이차 스플라인, 삼차 스플라인의 실제 예시

#### 이차 스플라인의 개념

주어진 데이터가 여러 개의 구간으로 나뉘어 있고, 각 구간에서 이차다항식을 사용하여 데이터를 연결하고자 할 때 이차 스플라인을 고려한다. 예를 들어 노드라 부르는 점들을 $(x\_0, y\_0), (x\_1, y\_1), \dots, (x\_n, y\_n)$라 하고, 각 구간 $\[x\_i, x\_{i+1}]$ 위에서 이차다항식 $S\_i(x)$를 정의한다. 보통 $S\_i(x)$의 일반적인 형태는 다음과 같다.

$$
\begin{align} S\_i(x)  &= a\_i (x - x\_i)^2 + b\_i (x - x\_i) + c\_i \\
& \quad \text{for } x\_i \le x \le x\_{i+1}, ; i = 0,1,\dots,n-1 \end{align}
$$

이때, 인접한 스플라인 구간들 사이에서 함수값의 연속성, 1차 도함수의 연속성 등의 조건을 만족하도록 계수를 결정한다. 경계에서 추가로 부여되는 조건에 따라 여러 가지 이차 스플라인이 얻어진다.

이차 스플라인의 가장 간단한 형태를 만들기 위해서는 다음과 같은 조건을 고려한다. 각 구간 끝점에서 스플라인이 주어진 데이터 점을 정확히 통과해야 한다. 인접하는 구간 사이에서 스플라인 함수가 연속하고, 1차 미분도 연속해야 한다. 경계에서 2차 미분값을 0으로 둔다거나 또는 다른 방정식을 부여하여 자유도(free boundary condition)를 제한한다.

위 조건들로부터 $a\_i, b\_i, c\_i$에 대한 계수 방정식을 세울 수 있다. 두 연속 구간 $i$와 $i+1$ 구간에서의 연결 조건은 다음과 같은 꼴이 된다.

인접 구간의 함수값이 같아야 하므로

$$
\begin{align} S\_i(x\_{i+1}) &= y\_{i+1} \S\_{i+1}(x\_{i+1}) &= y\_{i+1} \end{align}
$$

함수값 연속성에 더하여 1차 도함수가 연속해야 하므로

$$
\begin{align} S\_i'(x\_{i+1}) &= S\_{i+1}'(x\_{i+1}) \end{align}
$$

경계 조건으로 왼쪽 가장자리에 대해 $S\_0''(x\_0) = 0$과 같은 자유 단순조건(free boundary condition)을 가정하기도 하며, 오른쪽 가장자리에 대해서도 $S\_{n-1}''(x\_n) = 0$과 같은 조건을 부여한다. 이러한 조건들은 각 구간의 계수를 연결하는 선형 방정식들을 구성한다.

#### 이차 스플라인 구체적 예시

다음과 같은 관측 데이터를 가정한다. $(x\_0, y\_0) = (0, 1)$, $(x\_1, y\_1) = (1, 2)$, $(x\_2, y\_2) = (2, 2.5)$, $(x\_3, y\_3) = (3, 1.5)$이라고 하자. 네 개의 데이터 점을 세 구간으로 나누어 $\[0,1]$, $\[1,2]$, $\[2,3]$에서 각각 $S\_0(x), S\_1(x), S\_2(x)$라는 이차다항식을 정의한다.

각 구간에서

$$
\begin{align} S\_0(x) &= a\_0 (x - 0)^2 + b\_0 (x - 0) + c\_0,\\
S\_1(x) &= a\_1 (x - 1)^2 + b\_1 (x - 1) + c\_1,\\
S\_2(x) &= a\_2 (x - 2)^2 + b\_2 (x - 2) + c\_2 \end{align}
$$

네 개의 점을 정확히 지나가야 하므로 다음 조건들을 만족한다.

$$
\begin{align} S\_0(x\_0) &= y\_0, \quad S\_0(x\_1) = y\_1,\\
S\_1(x\_1) &= y\_1, \quad S\_1(x\_2) = y\_2,\\
S\_2(x\_2) &= y\_2, \quad S\_2(x\_3) = y\_3 \end{align}
$$

연속성을 위하여 구간 $\[0,1]$과 $\[1,2]$ 접점, 그리고 $\[1,2]$와 $\[2,3]$ 접점에서 1차 도함수가 연속해야 하므로

$$
\begin{align} S\_0'(1) &= S\_1'(1),\\
S\_1'(2) &= S\_2'(2) \end{align}
$$

경계에서 이차 미분값을 자유 조건으로 두고자 한다면

$$
\begin{align} S\_0''(0) &= 0,\\
S\_2''(3) &= 0 \end{align}
$$

이렇게 하면 총 계수 9개($a\_0, b\_0, c\_0, a\_1, b\_1, c\_1, a\_2, b\_2, c\_2$)에 대해 9개의 선형 방정식을 얻을 수 있다. 이 시스템은 대체로 희소(sparse)하고, 특정한 패턴을 가진 행렬로 구성되어 계산이 효율적이다. 실제로는 이러한 방정식을 행렬 형태로 정리하여 삼각 행렬(또는 트리디아고널)에 가까운 형태로 만들 수 있다. 작은 차수의 예시에서는 직접 계산이 간단하지만, 노드가 많아지면 하부구조를 이용해 효율적으로 풀이한다.

아래는 Python으로 이 이차 스플라인 예시를 직접 계산하는 간단한 코드이다.

```python
import numpy as np

x_vals = np.array([0.0, 1.0, 2.0, 3.0])
y_vals = np.array([1.0, 2.0, 2.5, 1.5])
n = len(x_vals) - 1

# 계수 미지수를 3*(n)개 잡을 것이므로 크기에 맞춰 행렬 A, 벡터 b 준비
A = np.zeros((3*n, 3*n))
b = np.zeros(3*n)

# 적절한 방정식을 구성한 뒤 np.linalg.solve(A, b)로 해를 구할 수 있다.
# 방정식 설정 과정은 생략하고 스플라인 계수를 구했다고 가정하자.

# 예시로 직관적으로 맞춰서 아무 값(가짜) 넣어보기
# 실제로는 연속성 조건, 경계 조건 등을 코드로 작성해야 함
coeffs = np.linalg.solve(A, b)

print("이차 스플라인 계수:", coeffs)
```

위 방식으로 각 구간 이차다항식의 계수를 결정할 수 있다. 구해진 계수를 통해 전체 구간에 대한 스플라인 함수를 정의하면, 분할 간격이 충분히 촘촘할 때 부드럽고 현실적인 곡선으로 데이터를 근사하게 된다.

#### 삼차 스플라인의 개념

삼차 스플라인은 각 구간마다 삼차다항식을 사용하는 점을 제외하면 이차 스플라인과 유사한 방식으로 구성된다. 각 구간을 $\[x\_i, x\_{i+1}]$라 할 때 삼차다항식은 보통 다음과 같은 형태를 갖는다.

$$
\begin{align} S\_i(x)  &= a\_i (x - x\_i)^3 + b\_i (x - x\_i)^2 + c\_i (x - x\_i) + d\_i \end{align}
$$

함수값과 1차, 2차 도함수가 구간 경계점에서 연속하도록 조건을 부여하면, 데이터가 충분히 매끄럽게 연결된다. 삼차 스플라인은 이차 스플라인보다 하나 높은 차수를 사용하므로, 더 적은 구간 분할로도 부드러운 곡선을 얻을 수 있다. 이러한 이유로 가장 일반적으로 쓰이는 스플라인 방법은 삼차 스플라인이다.

#### 삼차 스플라인 방정식 구성

삼차 스플라인의 경우 각 구간 $\[x\_i, x\_{i+1}]$에서 정의되는 스플라인 함수를

$$
\begin{align} S\_i(x)  &= a\_i (x - x\_i)^3   + b\_i (x - x\_i)^2   + c\_i (x - x\_i)  + d\_i \end{align}
$$

로 두고, 인접 구간에서 함수 자체의 연속성, 1차 도함수의 연속성, 2차 도함수의 연속성을 부여한다. 노드가 $(x\_0, y\_0), (x\_1, y\_1), \dots, (x\_n, y\_n)$이라 할 때,

$$
\begin{align} S\_i(x\_i) &= y\_i,\\
S\_i(x\_{i+1}) &= y\_{i+1} \end{align}
$$

가 성립해야 하므로 모든 구간에서 두 개씩, 전체 구간에 걸쳐 $2n$개의 조건이 생긴다.

추가로 1차 도함수의 연속 조건

$$
\begin{align} S\_i'(x\_{i+1}) &= S\_{i+1}'(x\_{i+1}) \end{align}
$$

와 2차 도함수의 연속 조건

$$
\begin{align} S\_i''(x\_{i+1}) &= S\_{i+1}''(x\_{i+1}) \end{align}
$$

이 각각 구간 사이에서 성립해야 하므로, $n-1$개의 접점에서 각각 2개씩 총 $2(n-1)$개의 조건이 추가된다.

이로써 현재까지 $2n + 2(n-1) = 4n - 2$개의 방정식이 만들어진다. 하지만 삼차 다항식 하나당 4개의 미지수($a\_i, b\_i, c\_i, d\_i$)가 있고, 총 $n$개 구간이므로 미지수의 총 개수는 $4n$이다. 따라서 $4n - 2$개의 조건만으로는 2개가 부족하다. 이 때 가장 흔히 채택하는 경계 조건이 바로 \*\*natural spline(자연 스플라인)\*\*이라 불리는

$$
\begin{align} S\_0''(x\_0) &= 0,\\
S\_{n-1}''(x\_n) &= 0 \end{align}
$$

경계 조건이다. 이를 적용하면 부족한 2개가 완성되어 최종적으로 $4n$개의 미지수에 대해 $4n$개의 방정식이 정립되어 고유한 해가 결정된다. 이 외에도 $S\_0'(x\_0)$를 지정한다거나, $S\_{n-1}'(x\_n)$를 지정하는 등 다른 형태의 경계 조건을 부여할 수도 있다.

삼차 스플라인 방정식을 구성하는 전형적인 과정은 다음과 같은 방향으로 진행된다. 먼저, 구간 간 간격을 $h\_i = x\_{i+1} - x\_i$라 정의한다. 2차 도함수가 구간 내에서 일정하게 변하지는 않지만, 각 노드에서의 2차 도함수를 $\kappa\_i = S\_i''(x\_i)$라 부르면, 삼차 스플라인의 계수를 $\kappa\_i$를 중심으로 표현할 수 있다. 주어진 두 노드 사이 $\[x\_i, x\_{i+1}]$에서 삼차 다항식을 전개하고, 함수값 및 도함수 연속 조건을 $\kappa\_i$에 대한 관계식으로 바꾸면, 결과적으로 $n-1$개의 내부 노드에 대해 세울 수 있는 삼각선형시스템(Tridiagonal system)이 등장한다.

이 방식은 각 구간에서의 계수를 직접 푸는 대신, 각 노드에서의 2차 도함수만을 구한 후, 최종적으로 $a\_i, b\_i, c\_i, d\_i$를 재구성하는 흐름이다. 즉,

$$
\begin{align} \kappa\_i = S\_{i-1}''(x\_i) = S\_i''(x\_i) \end{align}
$$

같은 연속 조건을 사용하고, $i=1,\dots,n-1$에 대해 $\kappa\_0 = S\_0''(x\_0)$, $\kappa\_n = S\_{n-1}''(x\_n)$를 경계 조건으로 결정한 뒤, 내부의 $\kappa\_1, \dots, \kappa\_{n-1}$에 대한 선형 방정식을 세워서 해를 구한다. 이렇게 구해진 $\kappa\_i$ 값을 이용하면 각 구간의 계수가

$$
\begin{align} a\_i &= \frac{\kappa\_{i+1} - \kappa\_i}{6h\_i},\\
b\_i &= \frac{\kappa\_i}{2},\\
c\_i &= \frac{y\_{i+1} - y\_i}{h\_i}       - \frac{h\_i}{6} (2\kappa\_i + \kappa\_{i+1}),\\
d\_i &= y\_i \end{align}
$$

형태로, 통일되고 간단하게 산출된다.

#### 삼차 스플라인 구체적 예시

노드가 $(x\_0, y\_0) = (0,1)$, $(x\_1, y\_1) = (1,3)$, $(x\_2, y\_2) = (2,2)$, $(x\_3, y\_3) = (3,1.8)$, $(x\_4, y\_4) = (4,2)$라 하자. 네 개의 구간 $\[0,1], \[1,2], \[2,3], \[3,4]$이 정의되고, 각 구간에서 삼차다항식 $S\_0(x), S\_1(x), S\_2(x), S\_3(x)$를 작성한다. Natural spline 경계 조건을 적용하여 $S\_0''(0)=0$, $S\_3''(4)=0$를 설정한다.

이 때 $h\_0 = 1, h\_1 = 1, h\_2 = 1, h\_3 = 1$라 단순해지며, 내부 노드 세 곳 $(x\_1, x\_2, x\_3)$에서 2차 도함수 $\kappa\_1, \kappa\_2, \kappa\_3$를 구하면 삼차 스플라인 전체가 결정된다.

구체적인 방정식 전개는 다음 단계로 이루어진다.

먼저, $\kappa\_0 = S\_0''(x\_0) = 0$과 $\kappa\_4 = S\_3''(x\_4) = 0$라는 경계 조건을 둔다. 이제 노드 $1, 2, 3$에 대해서, 스플라인이 부드럽게 이어지기 위해 만들어지는 삼각선형 계수 행렬이 구성된다. 그 결과

$$
\begin{align} \frac{1}{6} \begin{pmatrix} 2 & 1 & 0 \ 1 & 4 & 1 \ 0 & 1 & 2 \end{pmatrix} \begin{pmatrix} \kappa\_1 \ \kappa\_2 \ \kappa\_3 \end{pmatrix} = \begin{pmatrix} \frac{y\_1 - 2y\_0 + y\_{-1}}{h\_0} \ \vdots \ \frac{y\_{j} - 2y\_{j-1} + y\_{j-2}}{h\_{j-1}} \end{pmatrix} \end{align}
$$

꼴의 계(係)열을 얻게 되는데, 실제로는 노드 수에 맞춰 정확한 형태로 작성하면 된다(위 행렬은 일반적인 3×3 예시로 묘사한 것이며, 실제 식은 $n-1$ 크기의 삼각행렬 형태로 일반화된다). 해 $\kappa\_1, \kappa\_2, \kappa\_3$를 구한 뒤, 앞서 언급한 공식에 따라 각 구간의 $a\_i, b\_i, c\_i, d\_i$를 회복한다.

아래 예시는 Python으로 동일한 노드를 사용하여 natural spline을 계산하는 과정을 간략히 보여준다. 구현 방식은 다양할 수 있으나, 내부 2차 도함수를 먼저 구한 다음 각 구간의 계수를 복원하는 접근이 효율적이다.

```python
import numpy as np

x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = np.array([1.0, 3.0, 2.0, 1.8, 2.0])
n = len(x) - 1

h = x[1:] - x[:-1]

# 계수 행렬과 벡터 준비
# n-1 차원의 삼각행렬 (내부 노드용)
A = np.zeros((n-1, n-1))
bvec = np.zeros(n-1)

# Natural boundary 조건: kappa_0 = 0, kappa_n = 0
# 내부 노드 kappa_1 ~ kappa_{n-1}를 구한다.

# 예시로 간단히 구성 (이 방법 말고도 여러 방식이 존재)
for i in range(n-1):
    if i > 0:
        A[i, i-1] = h[i]    # sub
    A[i, i]   = 2*(h[i]+h[i])  # diag (단순화 예시)
    if i < n-2:
        A[i, i+1] = h[i]  # super
    
    bvec[i] = 6.0 * ((y[i+2] - y[i+1]) / h[i+1] - (y[i+1] - y[i]) / h[i])

kappa = np.linalg.solve(A, bvec)

# 경계에서 kappa_0=0, kappa_n=0으로 확장
kappa_full = np.concatenate(([0.0], kappa, [0.0]))

# 각 구간 계수 계산
coeffs = []
for i in range(n):
    a_i = (kappa_full[i+1] - kappa_full[i]) / (6 * h[i])
    b_i = kappa_full[i] / 2
    c_i = (y[i+1] - y[i]) / h[i] - (h[i] / 6)*(2*kappa_full[i] + kappa_full[i+1])
    d_i = y[i]
    coeffs.append((a_i, b_i, c_i, d_i))

print("구간별 삼차 스플라인 계수:")
for i, (a_i, b_i, c_i, d_i) in enumerate(coeffs):
    print(f"S_{i}(x): a={a_i}, b={b_i}, c={c_i}, d={d_i}")
```

위 코드를 통해 각 구간의 삼차 스플라인 다항식 계수를 얻을 수 있다. 얻어진 계수를 활용해 $\[x\_i, x\_{i+1}]$ 구간에서 $S\_i(x)$가 어떻게 구성되는지를 확인하고, 적절히 그려 보면 데이터 점들을 부드럽게 연결하는 스플라인 곡선을 시각화할 수 있다.

#### 삼차 스플라인의 장단점

삼차 스플라인은 이차 스플라인에 비하여 한 차수가 높으므로, 곡선 연결 구간이 더 부드럽고, 더 적은 노드로도 복잡한 곡선을 근사할 수 있다. 또한, 각 스플라인 조각이 삼차다항식이므로 1차와 2차 도함수가 구간 전체에서 연속적이다. 이러한 매끄러움이 필수적인 분야(예: CAD, 애니메이션, 로봇팔 경로 제어 등)에서 널리 쓰인다.

그러나 차수가 높아질수록 근사 구간 밖에서의 외삽(extrapolation) 특성이 크게 왜곡될 가능성이 존재하고, 오실레이션(oscillation)이 생길 수 있다. 이 때문에, 보간 구간 밖의 예측보다는 주어진 구간 내부에서 매끄럽게 데이터를 연결하는 목적으로 적용하는 것이 일반적이다.

#### 스플라인 보간의 계산 효율성과 구조

이차 혹은 삼차 스플라인을 실제로 계산할 때 가장 중요한 점은, 계수를 결정하는 선형 방정식 계(係)열의 구조가 매우 희소(sparse)하고, 대각선 부근 요소만이 0이 아닌 삼각형(Tridiagonal) 형태를 가진다는 사실이다. 이는 노드가 $n+1$개일 때, 각 노드에서 설정되는 조건들이 인접한 구간에만 국한되어 있음을 의미한다. 스플라인 기법은 연결 구간이 증가하더라도, 이러한 희소 구조 덕에 $O(n)$ 또는 $O(n \log n)$ 정도의 계산 복잡도로 계수를 구할 수 있다.

글로벌 다항식(예: $n$차 폴리노미얼 하나로 전체 노드를 보간)으로 데이터를 맞출 경우, 보간 다항식의 차수가 높아짐에 따라 계산량 증가, 수치적 불안정성, 노드 사이에서 발생하는 대규모 오실레이션 등의 문제가 쉽게 발생한다. 반면 스플라인 보간은 노드를 여러 구간으로 쪼갠 뒤, 각 구간에서 낮은 차수의 다항식을 사용하므로 이러한 문제를 크게 줄일 수 있다. 이차 스플라인은 삼차 스플라인보다 구현이나 계산이 조금 단순하지만, 매끄러움(특히 2차 도함수의 연속성)은 삼차 스플라인만큼 보장되지 않는다.

#### 스플라인 보간함수의 평가

스플라인 보간함수를 계산해놓으면, 적절한 $x$가 주어졌을 때 어느 구간에 속하는지를 먼저 판별한 뒤, 해당 구간의 다항식으로 직접 대입하여 함숫값을 빠르게 얻을 수 있다. 예컨대 삼차 스플라인의 경우,

$$
\begin{align} S\_i(x)  &= a\_i (x - x\_i)^3   + b\_i (x - x\_i)^2   + c\_i (x - x\_i)  + d\_i \end{align}
$$

구간 $\[x\_i, x\_{i+1}]$에서 $x$가 존재한다면, 이미 앞에서 구한 $a\_i, b\_i, c\_i, d\_i$를 통해서 위 식을 간단히 계산할 수 있다. 1차, 2차 도함수도 곧바로

$$
\begin{align} S\_i'(x) &= 3a\_i (x - x\_i)^2           + 2b\_i (x - x\_i)           + c\_i,\\
S\_i''(x) &= 6a\_i (x - x\_i)           + 2b\_i \end{align}
$$

로 추적이 가능하다. 이렇게 국소적으로만 참조하여 함숫값을 구하면 되므로, 스플라인은 대규모 데이터가 주어질 때도 효율적인 보간 및 미분 값 계산 방법을 제공한다.

#### B-스플라인과의 관련성

삼차 스플라인은 대표적인 조각별(polynomial piecewise) 다항식 보간이며, B-스플라인 기저함수들과도 깊은 관련이 있다. 각 구간마다 기저함수가 하나씩 대응되는 B-스플라인 방법을 사용하면, 보간 조건을 충족하는 계수를 더욱 직관적이고 체계적으로 결정할 수 있다. 실제로도 CAD 등에서 곡면 설계에는 B-스플라인이 광범위하게 쓰이는데, 그 핵심 아이디어는 삼차 스플라인과 매우 흡사하다.

B-스플라인의 정의는 각 조각별 다항식을 전개하는 대신, 전역의 단조 증가 $t\_i$들(knots)에 대해 특정 형태의 기저함수를 생성해 합성하는 것으로 요약할 수 있다. 자연 스플라인과 비슷한 경계 조건을 갖도록 설정하며, 중첩 노드를 통해 각 구간 간 단절도 허용할 수 있다. 그러나 기저함수를 이용해 표현할 때는 $a\_i, b\_i, c\_i, d\_i$를 바로 해석하기보다, B-스플라인 계수들을 통해 곡선을 정의하고, 특정 점에서의 함수값을 합성으로 계산하는 식이다.

#### 고차(高次) 스플라인

이차와 삼차에서 멈추지 않고, 사차(quartic) 이상으로 차수를 높일 수도 있다. 하지만 일반적으로 3차 스플라인까지만 사용해도 이미 1차와 2차 도함수가 충분히 연속적이기 때문에, 더 높은 차수를 구사해야 할 동기가 줄어든다. 예를 들어, 4차 스플라인을 구축하려면 추가 경계 조건과 구간 경계에서의 3차 도함수 연속성 등을 부여해야 하므로, 미지수 및 조건의 수도 대폭 증가한다. CAD나 그래픽 분야에서도 3차(큐빅) 베지어(Bézier) 곡선이나 3차 B-스플라인이 주류를 차지하고 있다.

#### 고차 스플라인이 필요한 경우

불연속적인 3차 도함수를 필요로 하는 특정 물리 모델이나, 변곡점 제어가 까다로운 문제에서 고차 스플라인을 의도적으로 사용하는 경우도 있다. 그러나 대부분의 실용적 보간 문제에서는 삼차 스플라인이 가장 보편적이고 효과적인 해법으로 여겨진다.

#### 컴퓨터 그래픽스와 모션 플래닝

스플라인은 컴퓨터 그래픽스, 모션 플래닝 등 다양한 응용에서 활용된다. CAD 프로그램에서는 제품 설계를 위해 원하는 형상을 매끄러운 곡선으로 정의해야 하는데, 스플라인이 자연스럽고 연속적인 곡선을 지원한다. 로봇 공학에서 로봇팔의 경로를 부드럽게 이동시키기 위해, 삼차 스플라인을 통해 위치뿐 아니라 속도, 가속도 프로파일까지 안정적으로 제어하는 방법을 선호한다.

예를 들어 로봇 팔이 지나야 할 지점의 좌표가 $\mathbf{x}\_0, \mathbf{x}\_1, \dots, \mathbf{x}\_n$으로 주어졌을 때, 시간 변수 $t$에 대해 스플라인을 구성하여 $\mathbf{x}(t)$를 부드럽게 정의하면, 고른 가속도를 갖고 목표 지점을 정확히 통과하도록 할 수 있다. 이 때 차원이 여러 개인(즉 $\mathbf{x}\_i \in \mathbb{R}^m$) 문제라 하더라도, 각 좌표 성분별로 독립된 스플라인 보간을 수행하면 된다.

다음 예시는 간단한 2차원 평면 상에서의 로봇 팔 경로를 Python 코드로 스플라인으로 구성해볼 수 있음을 보여준다. 실제 동작을 시각화하려면 matplotlib 등을 이용해 플롯을 그린다.

```python
import numpy as np
import matplotlib.pyplot as plt

# 예시 노드 (2차원 평면)
t_vals = np.array([0, 1, 2, 3, 4])
x_coords = np.array([0, 1, 2, 2, 3])
y_coords = np.array([0, 1, 1.5, 2.5, 2])

# 각각의 좌표에 대해 자연 스플라인을 구성
# (앞서 소개한 방법을 x, y 각각에 적용한다)
# 여기서는 간단하게 np.interp1d 또는 다른 라이브러리를 사용할 수도 있다.

plt.plot(x_coords, y_coords, 'o', label="Nodes")

# 실제 스플라인 계산/적용 과정은 생략
# 예를 들어 scipy.interpolate.CubicSpline을 이용하면 빠르게 삼차 스플라인을 구할 수 있다.
# from scipy.interpolate import CubicSpline
# cs_x = CubicSpline(t_vals, x_coords, bc_type='natural')
# cs_y = CubicSpline(t_vals, y_coords, bc_type='natural')

# 스플라인 곡선을 100개의 점으로 그려본다.
# t_lin = np.linspace(t_vals[0], t_vals[-1], 100)
# spline_x = cs_x(t_lin)
# spline_y = cs_y(t_lin)
# plt.plot(spline_x, spline_y, label="Cubic Spline Path")

plt.legend()
plt.show()
```

#### 스플라인의 다양한 적용 사례

스플라인 보간법은 여러 분야에서 다양하게 활용된다. 앞서 언급한 CAD, 컴퓨터 그래픽스, 모션 플래닝 외에도, 다음과 같은 응용에서 스플라인이 두드러지게 사용된다.

**신호 처리와 곡선 피팅**

데이터가 노이즈를 포함하는 경우, 단순 보간보다는 스플라인 곡선 피팅(spline curve fitting) 기법을 적용하는 사례가 많다. 보간은 모든 노드를 정확히 지나가도록 설계하지만, 실제 관측 데이터에서 오차나 잡음이 있을 때에는 **스무딩 스플라인(smoothing spline)** 접근이 효과적일 수 있다. 이를 통해 전체 곡선이 지나치게 진동하지 않도록(오버피팅 방지) 규제항을 추가하고, 매끄러움과 데이터 적합도의 균형을 조정한다.

**기계 학습 및 회귀**

회귀분석에서 비선형 모델을 쓰고자 할 때, 스플라인 함수를 특징으로 사용하는 방식을 택할 수도 있다. 예컨대 선형회귀 모델에서 다차항 대신에 스플라인 기저함수를 사용하면, 데이터를 구간별로 자유롭게 근사할 수 있다.

$$
y \approx \beta\_0 + \sum\_{j=1}^k \beta\_j B\_j(x),
$$

여기서 Bj(x)B\_j(x)는 스플라인 기저함수이다. 이렇게 하면 보통의 다항회귀와 달리, 구간마다 곡률을 다르게 조절할 수 있어서 적은 차수로도 유연한 모델을 만들 수 있다.

**항공우주 및 자동차 엔지니어링**

날개 설계나 차체 곡면 설계를 위해 곡면을 스플라인으로 표현하는 경우가 있다. 2차원 스플라인을 기반으로 3차원 곡면 스플라인(surface spline)까지 확장하면, 복잡한 형상의 곡면도 매끄럽게 정의할 수 있다. 또한, 항공기나 자동차의 공기역학 최적화 과정에서 스플라인 형상을 변수화하여, 설계 변수를 효율적으로 줄이고 매끄러운 형상만 고려하도록 하는 기법이 널리 쓰인다.

**보간 기반 이미지 변환**

이미지 상의 픽셀 좌표를 다른 좌표로 변환하는 과정에서, 스플라인 보간법이 사용될 수 있다. 고차 스플라인(예: B-스플라인)을 활용하면, 회전·배율 등 선형 변환을 넘어 좀 더 자유도가 높은 비선형 왜곡도 구현 가능하다. 최근 딥러닝 방식의 이미지 처리에서도, 중간단계 보간(interpolation)을 빠르게 수행하기 위해서 스플라인 기반 알고리즘을 접목하는 시도가 존재한다. 3차 스플라인은 (특히 2차원 또는 3차원) 세밀한 영상 처리나 3D 그래픽 텍스처 매핑에서 스무딩이 필요한 영역에 자주 등장한다.

#### 예시: Octave로 스플라인 보간 결과 시각화

Octave(혹은 MATLAB)에서도 스플라인 보간 함수를 바로 사용할 수 있지만, 직접 구성하여 살펴볼 수도 있다. 아래 예시는 삼차 스플라인 보간을 구현하고, 결과를 그림으로 나타내는 간단한 Octave 코드를 보여준다.

```python
x_vals = [0, 1, 2, 3, 4];
y_vals = [1, 3, 2, 1.8, 2];

# 내장 함수 (splines) 사용 예시
xx = linspace(0, 4, 100);
yy = spline(x_vals, y_vals, xx);

plot(x_vals, y_vals, 'o', xx, yy, '-')
legend('Data Points','Cubic Spline Interpolation')
title('Cubic Spline Interpolation in Octave')
```

위 코드는 Octave에서 제공되는 `spline` 함수를 이용해 삼차 스플라인을 간단히 구한다. `spline` 함수를 호출할 때, 내부적으로는 natural spline 경계 조건을 기본값으로 사용한다. 만일 다른 경계 조건이나 특수한 스플라인 알고리즘을 직접 구현해보고 싶다면, $a\_i,b\_i,c\_i,d\_i$ 또는 내부 2차 도함수 κi\kappa\_i를 구하는 과정을 위에서 설명한 방식으로 Octave 스크립트에 작성하고, 그 결과를 플롯하면 된다.

#### 이산 데이터에서의 스플라인 해석

이차, 삼차 스플라인 보간을 구하는 과정은 "입력: (노드, 해당 함수값)", "출력: 구간별 다항식의 계수"로 볼 수 있다. 즉, 관측 데이터가 연속곡선을 정의할 때, 해당 곡선을 구간 단위로 근사하는 매끄러운 함수가 스플라인인 셈이다. 스플라인 보간은 특히 노드의 개수가 많아질수록 유리하며, 구간별로 비교적 낮은 차수의 다항식만 쓰이므로 수치적으로도 안정성이 높아지는 장점이 있다.

#### 고급 스플라인: Hermite 스플라인과 기타 변형

보간 문제에서 어떤 노드의 함수값뿐 아니라, 각 노드에서 미분값(또는 기울기)까지 지정할 수 있다면, Hermite 보간(Hermite interpolation)을 적용할 수 있다. Hermite 스플라인(Hermite spline)은 각 구간을 삼차다항식으로 표현하되, 노드에서의 함수값과 미분값을 함께 만족하도록 한다. 삼차 Hermite 스플라인의 한 형태로 많이 쓰이는 것이 바로 **Catmull-Rom 스플라인**이며, 컴퓨터 그래픽스에서 곡선 편집을 용이하게 하고, 키 프레임(keyframe)들 사이를 부드럽게 연결하는 데 활용된다.

**Hermite 보간의 일반 개념**

서로 다른 두 노드 $(x\_i, y\_i)$, $(x\_{i+1}, y\_{i+1})$ 사이에 삼차 Hermite 보간 다항식을 하나 두고, 아래 조건들을 만족하게 만든다.

$$
\begin{align} S\_i(x\_i) &= y\_i,\\
S\_i(x\_{i+1}) &= y\_{i+1},\\
S\_i'(x\_i) &= m\_i,\\
S\_i'(x\_{i+1}) &= m\_{i+1} \end{align}
$$

여기서 $m\_i, m\_{i+1}$는 구간 양 끝점에서의 기울기(혹은 1차 미분값)를 의미한다. 일반 스플라인에서는 노드에서의 1차 도함수를 명시적으로 지정하지 않고, 여러 연결 조건을 통해 내부적으로 결정한다. 하지만 Hermite 방식은 노드의 기울기를 직접 외부에서 부여하거나, 어떤 휴리스틱으로 추정한다(예: Catmull-Rom에서는 인접 점들의 위치로부터 기울기를 추정한다).

Hermite 스플라인은 다음과 같은 기저함수(Hermite basis)들을 통해 정의할 수 있다.

$$
\begin{align} h\_0(t) &= 2t^3 - 3t^2 + 1,\\
h\_1(t) &= t^3 - 2t^2 + t,\\
h\_2(t) &= -2t^3 + 3t^2,\\
h\_3(t) &= t^3 - t^2 \end{align}
$$

변수 $t$를 $\[0,1]$ 구간으로 정규화(normalize)한 뒤, 다음과 같은 Hermite 스플라인 조합으로 표현한다.

$$
\begin{align} S\_i(x) &= y\_i,h\_0(t)       + m\_i,h\_1(t) (x\_{i+1}-x\_i)       + y\_{i+1},h\_2(t)       + m\_{i+1},h\_3(t) (x\_{i+1}-x\_i) \end{align}
$$

여기서 $x$가 구간 $\[x\_i,x\_{i+1}]$에 있을 때, $t = \frac{x - x\_i}{x\_{i+1}-x\_i}$로 잡는다. 결과적으로 Hermite 보간은 노드 사이의 위치뿐 아니라 경사(기울기)까지 제어할 수 있으므로, 곡선의 모양을 좀 더 직관적으로 다룰 수 있다.

#### Catmull-Rom 스플라인

Catmull-Rom 스플라인은 매 노드에서의 기울기를 인접 노드 정보로부터 자동 산출하는 방식으로, 보간 곡선을 쉽게 얻을 수 있다. 예를 들어 노드 $i$에서의 기울기 $m\_i$를

$$
\begin{align} m\_i &= \alpha \frac{y\_{i+1} - y\_{i-1}}{x\_{i+1} - x\_{i-1}} \end{align}
$$

형태로 추정한다. $\alpha$는 일반적으로 $0.5$ 등으로 설정하여, 곡선의 장력을 조절한다. 이렇게 하면 개발자는 각 점의 미분값을 직접 지정할 필요 없이, 점들만 주어도 자연스러운 연결 곡선을 얻는다.

Catmull-Rom 스플라인은 자연 스플라인과 달리 2차 도함수의 연속성이 완벽히 보장되진 않지만, 시각적 편의와 구현상의 단순함 때문에 그래픽스나 컴퓨터 애니메이션에서 널리 사용된다. 불연속 노드를 빠르게 연결하거나, 각 노드 근방에서의 곡률을 간단히 제어하기에도 장점이 있다.

#### Tension 스플라인

Cardinal spline이라고도 부르는 Tension spline은 Catmull-Rom에서 $\alpha$ 대신 좀 더 일반적인 장력(tension) 파라미터 $\tau$를 두고, 기울기 추정 공식을 유연화한 스플라인이다. $\tau$ 값을 변화시키면 곡선의 팽팽함(tightness)을 조절할 수 있으므로, 곡선이 얼마나 평탄한지 혹은 날카로운 경사를 띠는지 쉽게 바꿀 수 있다.

일반적인 Catmull-Rom에서 $\alpha = 0.5$인 경우보다, $\tau$를 다르게 선택하면 곡선이 전체적으로 팽팽해지거나 느슨해지는 효과를 낸다. 이는 이미지 편집, 곡선 리터칭(retouch) 작업에서 사용자가 곡선을 미세하게 조정할 수 있는 편의성을 제공한다.

#### 분할 구간의 적응적 선택

스플라인 보간은 노드 사이 간격을 어떻게 배치하느냐에 따라 성능과 정확도가 크게 좌우될 수 있다. 데이터가 특정 구간에서 급격히 변화한다면, 해당 영역은 더 촘촘하게 노드를 배치하고, 비교적 완만한 구간은 간격을 크게 잡아도 무방하다. 이를 \*\*적응적 분할(Adaptive subdivision)\*\*이라고 부르며, 스플라인 보간의 효율과 정확도를 개선하는 중요한 실무 테크닉이다.

스플라인 자체가 낮은 차수의 조각별 다항식을 쓴다는 점 때문에, 분할이 일정해도 대부분의 경우 잘 동작한다. 하지만 데이터 변화가 급격한 지점에서는 해상도를 높게 잡아야 오실레이션(불필요한 진동)이나 과대 근사 현상을 방지할 수 있다. 실제 응용에서 전체 구간을 일정 간격으로 분할한 뒤, 에러가 일정 임계값 이상인 곳을 재분할하는 방식으로 구현하기도 한다.

#### 폐곡선(closed curve) 스플라인

스플라인을 폐곡선 형태로 구성하고 싶다면, 양 끝 노드가 동일 지점(또는 동일 상태)임을 가정하고, 연결 경계 조건을 주면 된다. 예컨대 $x\_0 = x\_n$, $y\_0 = y\_n$ 형태로 설정한 뒤, 첫 구간의 끝점 도함수와 마지막 구간의 끝점 도함수가 이어지도록 경계 조건을 주어 매끄러운 원형(환형) 곡선을 만들 수 있다. 이런 폐곡선 스플라인은 지도, 패턴 인식, 2D/3D 모델링, 심장형 곡선 분석 등에서 사용된다.

삼차 스플라인을 폐곡선으로 만들려면, $n$개의 구간에서 4$n$개의 미지수를 두고, 각 노드에서의 함수값·도함수·2차 도함수 연속성을 설정하되, $x\_n = x\_0$, $S\_{n-1}(x\_n)=S\_0(x\_0)$, $S\_{n-1}'(x\_n)=S\_0'(x\_0)$, $S\_{n-1}''(x\_n)=S\_0''(x\_0)$ 식의 순환조건을 맞춘다. 그 결과 역시 삼각선형 구조를 유지하므로, 효율적으로 계산이 가능하다.

#### 스플라인과 편미분 방정식(PDE) 해석

수치해석 전반에서, 2차원이나 3차원 공간에 정의된 편미분 방정식을 풀 때 국소 기저함수로 스플라인을 사용하는 기법도 존재한다. 예를 들어 이산화(discretization)를 할 때, 전통적인 유한요소법(FEM)이나 유한차분법(FDM)을 넘어 B-스플라인 기저를 사용하는 이른바 **Isogeometric Analysis**가 대표적인 예이다. CAD 모델이 이미 스플라인(또는 NURBS) 형태로 주어졌다면, 이 기저를 그대로 해석에 활용해 메쉬 변환 과정에서 발생하는 오차나 비연속성을 줄일 수 있다.

#### 추가적인 참고 사항

이차 스플라인, 삼차 스플라인은 보간과 근사(피팅) 문제에서 핵심적인 수단으로 자리 잡고 있다. 구간별 다항식이라는 단순함과, 적은 계산 비용, 매끄러운 연결성(특히 3차 스플라인)이 조화롭게 결합되어 실제 응용에서 매우 유용하다. 이차 스플라인은 계산이 조금 더 간단하고, 삼차 스플라인은 곡선의 부드러움(1차와 2차 도함수의 연속)이 더 뛰어나다. 필요에 따라 Hermite, Cardinal, Catmull-Rom, B-스플라인 등 다양한 변형 기법이 존재하므로, 목적에 맞추어 노드의 기울기나 곡률, 장력 파라미터 등을 조정하여 곡선을 섬세하게 다룰 수 있다.
