Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

노트북은 예측 → 계산 → 대조 세 부분으로 고정한다. 새 개념은 도입하지 않는다. 본문에서 이미 유도한 것을 수치로 확인할 뿐이다.

1. 예측 (코드를 쓰기 전에)

아래 셋을 먼저 채운다. 계산하지 않는다.

  1. 기준 파라미터에서 kk^{*}, cc^{*}, λ1\lambda_1, 반감기는 얼마인가. 같은 α,δ\alpha,\delta의 솔로우(W10, n=0n=0)보다 빠른가 느린가.

    예측: ____

  2. k0=3k_0=3에서 c0c_0을 안장경로 값의 ±3%\pm3\%로 잡고 정방향 적분하면 각각 어디로 가는가. 안장경로 값 그대로 정방향으로 쏘면 상대오차 10-6이 언제 O(1)O(1)이 되는가.

    예측: ____

  3. ρ\rho0.030.020.03\to0.02로 영구히 내리면 옛 정상상태에서 소비는 어느 방향으로 얼마나 뛰는가.

    예측: ____

2. 계산

패키지 호출로 답을 내지 않는다. 정상상태·야코비안·적분기·역행 사격을 차례로 직접 쌓는다. 결과는 표와 그림 둘로 낸다.

# 기준 파라미터와 생산함수를 쌓는다.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager
from matplotlib.ticker import FixedLocator, FuncFormatter, LogLocator, NullFormatter, NullLocator

# 한국어 글꼴 설정
_have = {fo.name for fo in font_manager.fontManager.ttflist}
for _cand in ['Apple SD Gothic Neo', 'AppleGothic', 'NanumGothic',
              'Noto Sans CJK KR', 'Malgun Gothic']:
    if _cand in _have:
        plt.rcParams['font.family'] = _cand
        break
plt.rcParams['axes.unicode_minus'] = False

# 로그축 눈금은 10의 거듭제곱으로 직접 적는다. 기본 로그 포매터는 U+2212(−)를 쓰는데
# 위에서 고른 한글 글꼴에 그 글리프가 없어 10^-2 의 부호가 네모로 깨진다.
pow10 = FuncFormatter(lambda v, _: '$10^{%d}$' % round(np.log10(v)))

ALPHA, RHO, DELTA, SIGMA = 1 / 3, 0.03, 0.05, 2.0   # 연 단위 기준 파라미터


def f(k):        # 집약형 생산함수
    return k ** ALPHA


def fp(k):       # 자본의 한계생산
    return ALPHA * k ** (ALPHA - 1)


def fpp(k):      # 한계생산의 도함수
    return ALPHA * (ALPHA - 1) * k ** (ALPHA - 2)


def phi(k):      # 유지가능 소비 — 자본이 멈추는 곡선
    return f(k) - DELTA * k


print('alpha=%.4f  rho=%.2f  delta=%.2f  sigma=%.1f' % (ALPHA, RHO, DELTA, SIGMA))
alpha=0.3333  rho=0.03  delta=0.05  sigma=2.0
# 정상상태를 두 길로 구해 대조한다 — 닫힌 형과 뉴턴 반복.
def steady_closed(rho=RHO):
    ks = (ALPHA / (rho + DELTA)) ** (1 / (1 - ALPHA))
    return ks, phi(ks)


def steady_newton(rho=RHO, k_init=1.0, tol=1e-13, itmax=60):
    k, steps = k_init, 0
    for _ in range(itmax):
        step = (fp(k) - rho - DELTA) / fpp(k)   # g(k)=f'(k)-rho-delta 의 뉴턴 걸음
        k, steps = k - step, steps + 1
        if abs(step) < tol:
            break
    return k, phi(k), steps


KS, CS = steady_closed()
kn, cn, nstep = steady_newton()
K_GOLD = (ALPHA / DELTA) ** (1 / (1 - ALPHA))
K_BAR = DELTA ** (-1 / (1 - ALPHA))
S_STAR = DELTA * KS / f(KS)
print('닫힌 형   k*=%.6f  c*=%.6f' % (KS, CS))
print('뉴턴 반복 k*=%.6f  c*=%.6f  (반복 %d회)' % (kn, cn, nstep))
print('두 길의 차이 |dk*|=%.2e' % abs(KS - kn))
print('k_gold=%.3f  k_bar=%.3f  y*=%.4f  s*=delta k*/y*=%.4f (1/s*=%.2f)'
      % (K_GOLD, K_BAR, f(KS), S_STAR, 1 / S_STAR))
닫힌 형   k*=8.505173  c*=1.615983
뉴턴 반복 k*=8.505173  c*=1.615983  (반복 9회)
두 길의 차이 |dk*|=1.78e-15
k_gold=17.213  k_bar=89.443  y*=2.0412  s*=delta k*/y*=0.2083 (1/s*=4.80)
# 야코비안과 고유값을 (eq-w11-13)·(eq-w11-14) 공식으로 손으로 쌓는다.
def jacobian(ks, cs, sigma=SIGMA, rho=RHO):
    return np.array([[fp(ks) - DELTA, -1.0],
                     [cs * fpp(ks) / sigma, (fp(ks) - rho - DELTA) / sigma]])


def eigen_hand(ks, cs, sigma=SIGMA, rho=RHO):
    tr, det = rho, cs * fpp(ks) / sigma
    disc = np.sqrt(tr ** 2 - 4 * det)
    return tr, det, (tr - disc) / 2, (tr + disc) / 2


J = jacobian(KS, CS)
TR, DET, LAM1, LAM2 = eigen_hand(KS, CS)
chk = np.sort(np.linalg.eigvals(J).real)          # np.linalg.eig 는 검산에만
HALF = np.log(2) / abs(LAM1)
HALF_SOLOW = np.log(2) / ((1 - ALPHA) * DELTA)    # W10 솔로우(n=0, s 고정)
print('J =\n%s' % np.array2string(J, precision=5))
print('trJ=%.5f  detJ=%.6f  판별식=%.6f  f\'\'(k*)=%.6f' % (TR, DET, TR ** 2 - 4 * DET, fpp(KS)))
print('손 계산  lambda1=%.5f  lambda2=%.5f  (lambda1+lambda2=%.5f=rho)' % (LAM1, LAM2, LAM1 + LAM2))
print('eig 검산 lambda1=%.5f  lambda2=%.5f  차이 %.1e' % (chk[0], chk[1], abs(chk[0] - LAM1)))
print('[예측 1] k*=%.3f  c*=%.3f  lambda1=%.4f  반감기=%.2f년'
      % (KS, CS, LAM1, HALF))
print('[예측 1] 솔로우 반감기=%.2f년 -> 램지가 %s' % (HALF_SOLOW, '빠르다' if HALF < HALF_SOLOW else '느리다'))
J =
[[ 3.00000e-02 -1.00000e+00]
 [-5.06667e-03  6.93889e-18]]
trJ=0.03000  detJ=-0.005067  판별식=0.021167  f''(k*)=-0.006271
손 계산  lambda1=-0.05774  lambda2=0.08774  (lambda1+lambda2=0.03000=rho)
eig 검산 lambda1=-0.05774  lambda2=0.08774  차이 6.9e-18
[예측 1] k*=8.505  c*=1.616  lambda1=-0.0577  반감기=12.00년
[예측 1] 솔로우 반감기=20.79년 -> 램지가 빠르다
# RK4 적분기를 직접 쓴다 — 벡터장은 (eq-w11-1).
def field(x, rho=RHO, sigma=SIGMA):
    k, c = x
    return np.array([f(k) - c - DELTA * k, c * (fp(k) - rho - DELTA) / sigma])


def rk4_step(x, h, rho=RHO, sigma=SIGMA):
    a = field(x, rho, sigma)
    b = field(x + 0.5 * h * a, rho, sigma)
    c = field(x + 0.5 * h * b, rho, sigma)
    d = field(x + h * c, rho, sigma)
    return x + h / 6 * (a + 2 * b + 2 * c + d)


def integrate(x0, h, tmax, rho=RHO, sigma=SIGMA, stop=None):
    x = np.array(x0, float)                 # h<0 이면 시간을 거꾸로 간다
    ts, xs, t = [0.0], [x.copy()], 0.0
    while t < tmax - 1e-12:
        x = rk4_step(x, h, rho, sigma)
        t += abs(h)
        if not np.all(np.isfinite(x)) or x[0] <= 0 or x[1] <= 0:
            break
        ts.append(t)
        xs.append(x.copy())
        if stop is not None and stop(x):
            break
    return np.array(ts), np.array(xs)


print('RK4 한 걸음 검산: field(k*,c*)=%s' % np.array2string(field([KS, CS]), precision=8))
RK4 한 걸음 검산: field(k*,c*)=[5.55111512e-17 1.12131333e-17]
# 역행 사격으로 안장경로를 쌓는다 — 시간을 뒤집으면 안정 다양체가 불안정 다양체가 된다.
def arm_branches(rho=RHO, sigma=SIGMA, eps=1e-3, h=0.05, kmin=0.3, kmax=40.0):
    """왼쪽·오른쪽 두 가지를 각각 (뒤로 간 시간, 궤적) 으로 돌려준다."""
    ks, cs = steady_closed(rho)
    lam2 = eigen_hand(ks, cs, sigma, rho)[3]
    v = np.array([1.0, lam2]) / np.hypot(1.0, lam2)   # 안정 고유벡터 방향 (eq-w11-15)
    return [integrate(np.array([ks, cs]) + s * eps * v, -h, 4000.0, rho, sigma,
                      stop=lambda x: not (kmin < x[0] < kmax))
            for s in (-1.0, +1.0)]


def arm_table(branches):
    """두 가지를 k 순으로 이어 붙여 표 c_arm(k) 로 만든다."""
    xs = np.vstack([b[1] for b in branches])
    order = np.argsort(xs[:, 0])
    return xs[order, 0], xs[order, 1]


ARM = arm_branches()
KA, CA = arm_table(ARM)
K0 = 3.0
C_ARM0 = float(np.interp(K0, KA, CA))
C_LIN0 = CS + LAM2 * (K0 - KS)
print('안장경로 표: k in [%.2f, %.2f], 점 %d개' % (KA[0], KA[-1], KA.size))
print('k0=%.0f 에서  c_arm=%.4f   선형 근사 c*+lambda2(k-k*)=%.4f   차이 %+.1f%%'
      % (K0, C_ARM0, C_LIN0, 100 * (C_LIN0 / C_ARM0 - 1)))
print('안장경로 위 저축률 1-c_arm/f(k0)=%.4f  >  정상상태 s*=%.4f' % (1 - C_ARM0 / f(K0), S_STAR))
안장경로 표: k in [0.29, 40.04], 점 6728개
k0=3 에서  c_arm=1.0142   선형 근사 c*+lambda2(k-k*)=1.1329   차이 +11.7%
안장경로 위 저축률 1-c_arm/f(k0)=0.2968  >  정상상태 s*=0.2083
# 그림 1 — 두 널클라인, 안장경로, k0=3 에서 ±3% 로 출발한 두 경로.
up = integrate([K0, C_ARM0 * 1.03], 0.05, 400.0, stop=lambda x: x[0] < 0.05)
dn = integrate([K0, C_ARM0 * 0.97], 0.05, 600.0)
kk = np.linspace(0.05, 30.0, 400)
fig, ax = plt.subplots(figsize=(6.6, 4.3))
ax.plot(kk, phi(kk), color='0.45', lw=1.2, label=r'$\dot k=0$ (유지가능 소비)')
ax.axvline(KS, color='0.45', lw=1.0, ls='--', label=r'$\dot c=0$ ($k=k^*$)')
ax.plot(KA, CA, color='C0', lw=2.2, label='안장경로 (역행 사격)')
ax.plot(up[1][:, 0], up[1][:, 1], color='C3', lw=1.4, label='+3% 경로')
ax.plot(dn[1][:, 0], dn[1][:, 1], color='C1', lw=1.4, label='-3% 경로')
ax.plot([KS], [CS], 'ko', ms=5)
ax.plot([K0, K0], [C_ARM0 * 0.97, C_ARM0 * 1.03], color='0.2', lw=0.9, ls=':')
ax.set_xlim(0, 30)
ax.set_ylim(0, 2.6)
ax.set_xlabel('1인당 자본 k')
ax.set_ylabel('1인당 소비 c')
ax.set_title('램지 위상평면 — 안장경로와 두 발산 경로')
ax.legend(fontsize=8, loc='lower right')
plt.show()
<Figure size 660x430 with 1 Axes>
# [예측 2] 세 경로의 종착지와 횡단조건 양 e^{-rho t} mu_t k_t 를 쌓는다.
def tvc(ts, xs, rho=RHO, sigma=SIGMA):
    return np.exp(-rho * ts) * xs[:, 1] ** (-sigma) * xs[:, 0]   # mu = c^{-sigma}


def arm_path(branches, k0, ks=KS):
    """안장경로의 시간 경로. 안장경로 위에서 정방향으로 쏘면 다음 셀이 재는 속도로
    다양체를 이탈하므로, 역행 사격 궤적에서 k0 쪽 구간을 잘라 t -> t_max - t 로
    시간만 뒤집어 쓴다."""
    ts, xs = branches[0] if k0 < ks else branches[1]
    m = (xs[:, 0] >= k0) if k0 < ks else (xs[:, 0] <= k0)
    return (ts[m].max() - ts[m])[::-1], xs[m][::-1]


arm = arm_path(ARM, K0)
print('경로        종료 t(년)      k_T        c_T     e^{-rho t}mu k (T)')
for name, (ts, xs) in [('안장경로 ', arm), ('+3%      ', up), ('-3%      ', dn)]:
    print('%s %8.1f %10.4f %10.5f %14.3e' % (name, ts[-1], xs[-1, 0], xs[-1, 1], tvc(ts, xs)[-1]))
print('  (안장경로 행의 종료 t 는 역행 사격이 k* 의 1e-3 근방에 닿는 시점 — k_T -> k*=%.4f)' % KS)
print()
print('[예측 2] +3%%: k=0 도달 t=%.1f년 (실행 불가능)' % up[0][-1])
print('[예측 2] -3%%: (k,c) -> (%.3f, %.5f), k_bar=%.3f, 횡단조건 양 %.3e 로 발산'
      % (dn[1][-1, 0], dn[1][-1, 1], K_BAR, tvc(dn[0], dn[1])[-1]))
print('         -3%% 경로의 발산 기울기 (1-alpha)delta=%.4f, 안장경로는 기울기 -rho=%.4f 로 0 에 간다'
      % ((1 - ALPHA) * DELTA, -RHO))
late = arm[0] > 100
print('         검산: 안장경로 후반(t>100년) 로그 기울기 적합 %.5f  vs  -rho=%.5f'
      % (np.polyfit(arm[0][late], np.log(tvc(*arm)[late]), 1)[0], -RHO))
경로        종료 t(년)      k_T        c_T     e^{-rho t}mu k (T)
안장경로     150.9     8.5042    1.61590      3.527e-02
+3%           24.9     0.0498    2.52824      3.694e-03
-3%          600.0    89.4427    0.00000      1.163e+09
  (안장경로 행의 종료 t 는 역행 사격이 k* 의 1e-3 근방에 닿는 시점 — k_T -> k*=8.5052)

[예측 2] +3%: k=0 도달 t=24.9년 (실행 불가능)
[예측 2] -3%: (k,c) -> (89.443, 0.00000), k_bar=89.443, 횡단조건 양 1.163e+09 로 발산
         -3% 경로의 발산 기울기 (1-alpha)delta=0.0333, 안장경로는 기울기 -rho=-0.0300 로 0 에 간다
         검산: 안장경로 후반(t>100년) 로그 기울기 적합 -0.03000  vs  -rho=-0.03000
# [예측 2] 정방향 사격 — 상대오차 1e-6 이 언제 O(1) 이 되는가.
EPS = 1e-6
t_b, x_b = integrate([K0, C_ARM0], 0.01, 400.0, stop=lambda x: x[0] < 0.05)
t_p, x_p = integrate([K0, C_ARM0 * (1 + EPS)], 0.01, 400.0, stop=lambda x: x[0] < 0.05)
n = min(t_b.size, t_p.size)
dev = np.abs(x_p[:n, 1] - x_b[:n, 1]) / x_b[:n, 1]
band = (t_b[:n] > 60) & (t_b[:n] < 130)
rate = np.polyfit(t_b[:n][band], np.log(dev[band]), 1)[0]
print('상대오차 dev(t) 표')
for lvl in (1e-6, 1e-4, 1e-2, 1e-1):
    idx = int(np.argmax(dev >= lvl))
    print('   dev >= %7.0e  에서  t=%6.1f년' % (lvl, t_b[idx]))
t10 = t_b[int(np.argmax(dev >= 0.1))]
print('[예측 2] 외삽한 dev=1 도달 t=%.1f년 (t(dev=0.1)=%.1f 에 ln10/lambda2 를 더한다),'
      '  이론값 ln(1/eps)/lambda2=%.1f년'
      % (t10 + np.log(10) / LAM2, t10, np.log(1 / EPS) / LAM2))
print('         검산: 증폭률 적합(60~130년) %.5f  vs  손 계산 lambda2 %.5f (외삽값 %.1f년)'
      % (rate, LAM2, t10 + np.log(10) / rate))
print('         정방향 사격은 이 속도로 다양체를 이탈한다 — 그래서 그림 2 의 안장경로는'
      ' 역행 사격 궤적을 시간만 뒤집어 그린다.')
상대오차 dev(t) 표
   dev >=   1e-06  에서  t=   0.0년
   dev >=   1e-04  에서  t=  55.2년
   dev >=   1e-02  에서  t= 107.4년
   dev >=   1e-01  에서  t= 132.9년
[예측 2] 외삽한 dev=1 도달 t=159.2년 (t(dev=0.1)=132.9 에 ln10/lambda2 를 더한다),  이론값 ln(1/eps)/lambda2=157.5년
         검산: 증폭률 적합(60~130년) 0.08830  vs  손 계산 lambda2 0.08774 (외삽값 159.0년)
         정방향 사격은 이 속도로 다양체를 이탈한다 — 그래서 그림 2 의 안장경로는 역행 사격 궤적을 시간만 뒤집어 그린다.
# 그림 2 — 세 경로의 소비와 횡단조건 양. 지평 T=150년까지 잘라 그린다.
T_FIG = 150.0
fig, (a1, a2) = plt.subplots(2, 1, figsize=(6.6, 5.4), sharex=True)
for name, (ts, xs), col in [('안장경로', arm, 'C0'), ('+3%', up, 'C3'), ('-3%', dn, 'C1')]:
    m = ts <= T_FIG
    a1.plot(ts[m], xs[m, 1], color=col, lw=1.6, label=name)
    a2.semilogy(ts[m], tvc(ts, xs)[m], color=col, lw=1.6, label=name)
a1.axhline(CS, color='0.45', lw=0.9, ls='--')
a1.set_ylabel('소비 c_t')
a1.set_ylim(0, 2.8)
a1.set_title('횡단조건이 경로를 고른다')
a1.legend(fontsize=8, loc='upper left')
a2.yaxis.set_major_locator(LogLocator(base=10.0))
a2.yaxis.set_major_formatter(pow10)
a2.yaxis.set_minor_formatter(NullFormatter())
a2.set_ylabel(r'$e^{-\rho t}\mu_t k_t$')
a2.set_xlabel('시간(년)')
a2.set_xlim(0, T_FIG)
plt.show()
<Figure size 660x540 with 2 Axes>
# [예측 3] rho 를 0.03 -> 0.02 로 영구히 내린다 — 옛 정상상태에서의 점프.
RHO2 = 0.02
KS2, CS2 = steady_closed(RHO2)
_, DET2, LAM1b, LAM2b = eigen_hand(KS2, CS2, SIGMA, RHO2)
KA2, CA2 = arm_table(arm_branches(rho=RHO2))
c_lin = CS2 + LAM2b * (KS - KS2)                 # (eq-w11-16) 의 선형 근사
c_nl = float(np.interp(KS, KA2, CA2))            # 역행 사격으로 얻은 비선형 값
print('새 정상상태 k*=%.4f  c*=%.4f  lambda1=%.5f  lambda2=%.5f  반감기=%.2f년'
      % (KS2, CS2, LAM1b, LAM2b, np.log(2) / abs(LAM1b)))
print('옛 정상상태 (k0,c0)=(%.4f, %.4f) 은 새 phi 곡선 위에 그대로 있다 — phi(k0)=%.4f'
      % (KS, CS, phi(KS)))
print('[예측 3] 선형 근사 c0=%.4f  (%+.2f%%),  역행 사격 c0=%.4f  (%+.2f%%)'
      % (c_lin, 100 * (c_lin / CS - 1), c_nl, 100 * (c_nl / CS - 1)))
print('[예측 3] 두 값의 차이 %.2f%%p — 소비는 아래로 뛴다. 자본은 그 자리(스톡)'
      % abs(100 * (c_lin / CS - 1) - 100 * (c_nl / CS - 1)))
print('점프 직후 저축률 1-c0/y0=%.4f (옛 s*=%.4f), 소비 성장률 (f\'(k0)-rho2-delta)/sigma=%.4f'
      % (1 - c_nl / f(KS), S_STAR, (fp(KS) - RHO2 - DELTA) / SIGMA))
새 정상상태 k*=10.3913  c*=1.6626  lambda1=-0.05191  lambda2=0.07191  반감기=13.35년
옛 정상상태 (k0,c0)=(8.5052, 1.6160) 은 새 phi 곡선 위에 그대로 있다 — phi(k0)=1.6160
[예측 3] 선형 근사 c0=1.5270  (-5.51%),  역행 사격 c0=1.5203  (-5.92%)
[예측 3] 두 값의 차이 0.42%p — 소비는 아래로 뛴다. 자본은 그 자리(스톡)
점프 직후 저축률 1-c0/y0=0.2552 (옛 s*=0.2083), 소비 성장률 (f'(k0)-rho2-delta)/sigma=0.0050
# 그림 3 — 선형 근사와 안장경로의 차이를 |k-k*| 에 대해 쌓는다.
kg = np.concatenate([np.linspace(0.35 * KS, 0.99 * KS, 60),
                     np.linspace(1.01 * KS, 2.5 * KS, 60)])
c_true = np.interp(kg, KA, CA)
c_lin_g = CS + LAM2 * (kg - KS)
gap = np.abs(c_lin_g - c_true) / c_true
d = np.abs(kg - KS)
ref = np.argmin(np.abs(d - 1.0))
near = d < 0.5                                   # 정상상태 근방의 국소 기울기
slope = np.polyfit(np.log(d[near]), np.log(gap[near]), 1)[0]
fig, ax = plt.subplots(figsize=(6.4, 4.2))
ax.loglog(d, gap, 'o', ms=3, color='C0', label='선형 근사의 상대오차')
ax.loglog(d, gap[ref] * (d / d[ref]) ** 2, color='0.45', lw=1.0, ls='--', label='기울기 2 기준선')
ax.axvline(abs(K0 - KS), color='C3', lw=1.0, ls=':')
for axis in (ax.xaxis, ax.yaxis):
    axis.set_major_locator(LogLocator(base=10.0))
    axis.set_major_formatter(pow10)
    axis.set_minor_formatter(NullFormatter())
ax.set_xlabel(r'$|k-k^*|$')
ax.set_ylabel('상대오차')
ax.set_title('버린 항은 간극의 제곱으로 되살아난다')
ax.legend(fontsize=8)
plt.show()
print('k=%.0f (|k-k*|=%.2f): 선형 근사 %.4f 대 안장경로 %.4f — 오차 %.1f%%'
      % (K0, abs(K0 - KS), C_LIN0, C_ARM0, 100 * (C_LIN0 / C_ARM0 - 1)))
print('근방 %d개 점(|k-k*|<0.5)의 로그-로그 기울기 적합 %.2f — 버린 항은 (k-k*)^2 이다'
      % (near.sum(), slope))
<Figure size 640x420 with 1 Axes>
k=3 (|k-k*|=5.51): 선형 근사 1.1329 대 안장경로 1.0142 — 오차 11.7%
근방 7개 점(|k-k*|<0.5)의 로그-로그 기울기 적합 2.02 — 버린 항은 (k-k*)^2 이다
# 그림 4 — sigma 스윕. 반감기 곡선이 솔로우 기준선을 어디서 지나는가.
SIG_CROSS = (RHO + DELTA) / (ALPHA * DELTA)


def halflife(sigma):
    return np.log(2) / abs(eigen_hand(KS, CS, sigma)[2])


print('sigma   반감기(년)   솔로우 대비')
for s in (0.5, 1.0, 2.0, 3.0, SIG_CROSS, 6.0, 10.0):
    hl = halflife(s)
    tag = ('같다' if abs(hl - HALF_SOLOW) < 1e-9 * HALF_SOLOW      # 허용오차를 먼저 본다
           else ('빠르다' if hl < HALF_SOLOW else '느리다'))
    print('%6.2f %10.2f   %s' % (s, hl, tag))
print('교차 sigma=(rho+delta)/(alpha delta)=%.4f,  1/s*=%.4f,  솔로우 반감기=%.4f년'
      % (SIG_CROSS, 1 / S_STAR, HALF_SOLOW))
print('교차점에서 반감기 차이 %.2e년 — 부동소수점 잔차다' % (halflife(SIG_CROSS) - HALF_SOLOW))

sg = np.logspace(np.log10(0.2), np.log10(50.0), 300)
hg = np.array([halflife(s) for s in sg])
fig, ax = plt.subplots(figsize=(6.4, 4.0))
ax.plot(sg, hg, color='C0', lw=2.0, label=r'램지 $\ln 2/|\lambda_1|$')
ax.axhline(HALF_SOLOW, color='0.45', lw=1.2, ls='--', label='솔로우 %.1f년' % HALF_SOLOW)
ax.plot([SIG_CROSS], [halflife(SIG_CROSS)], 'o', color='C3', ms=6, zorder=5)
ax.plot([SIGMA], [HALF], 'ko', ms=5, zorder=5)
ax.annotate(r'$\sigma=1/s^*=%.1f$' % SIG_CROSS, (SIG_CROSS, HALF_SOLOW),
            textcoords='offset points', xytext=(8, -16), color='C3', fontsize=9)
ax.annotate(r'$\sigma=2$: %.1f년' % HALF, (SIGMA, HALF),
            textcoords='offset points', xytext=(8, -16), fontsize=9)
ax.set_xscale('log')
ax.set_xlim(0.2, 50)
ax.set_ylim(0, 130)
ax.xaxis.set_major_locator(FixedLocator([0.2, 0.5, 1, 2, 5, 10, 20, 50]))
ax.xaxis.set_minor_locator(NullLocator())
ax.xaxis.set_major_formatter(FuncFormatter(lambda v, _: '%g' % v))
ax.set_xlabel(r'상대적 위험회피도 $\sigma$ (로그축)')
ax.set_ylabel('반감기(년)')
ax.set_title(r'반감기 곡선과 솔로우 기준선 — 교차점은 $\sigma=1/s^*$')
ax.legend(fontsize=8, loc='upper left')
plt.show()
sigma   반감기(년)   솔로우 대비
  0.50       5.41   빠르다
  1.00       7.99   빠르다
  2.00      12.00   빠르다
  3.00      15.40   빠르다
  4.80      20.79   같다
  6.00      24.11   느리다
 10.00      34.33   느리다
교차 sigma=(rho+delta)/(alpha delta)=4.8000,  1/s*=4.8000,  솔로우 반감기=20.7944년
교차점에서 반감기 차이 -1.07e-14년 — 부동소수점 잔차다
<Figure size 640x400 with 1 Axes>
# 교차 sigma 에서 안장경로 위 저축률 — k 에 무관한가.
KAc, CAc = arm_table(arm_branches(sigma=SIG_CROSS))
print('sigma=%.1f 의 안장경로 위 저축률' % SIG_CROSS)
print('     k     c_arm(k)   1-c_arm/f(k)')
for k in (1.0, 2.0, 3.0, 5.0, KS, 12.0, 20.0):
    c = float(np.interp(k, KAc, CAc))
    print('%6.2f %10.4f %13.4f' % (k, c, 1 - c / f(k)))
print('s* = alpha delta/(rho+delta) = %.4f — k 에 무관하다' % S_STAR)
sigma=4.8 의 안장경로 위 저축률
     k     c_arm(k)   1-c_arm/f(k)
  1.00     0.7917        0.2083
  2.00     0.9974        0.2083
  3.00     1.1418        0.2083
  5.00     1.3537        0.2083
  8.51     1.6160        0.2083
 12.00     1.8125        0.2083
 20.00     2.1489        0.2083
s* = alpha delta/(rho+delta) = 0.2083 — k 에 무관하다

3. 대조

예측과 계산이 어긋난 지점을 적는다. 어느 쪽이 틀렸는지 판정한다.

예측결과어긋남원인

본문 확인: 어긋났으면 (eq-w11-14)·(eq-w11-16)으로 돌아간다.