22회 실험 · 규칙을 거꾸로 읽는다#
이 실험
짝 서술 |
|
확인하는 것 |
두 기법이 정말 미분 법칙에서 나오는지 본다. 치환에서 구간을 안 바꾸면 얼마나 틀리는지 잰다. 얻은 원시함수를 미분해 되돌린다 |
이 노트북은 얻은 답을 미분해 되돌리는 일을 되풀이한다.
원시함수를 찾는 데에는 규칙이 없다. 그런데 찾은 것이 맞는지 확인하는 데에는 규칙이 있다. 미분해서 원래 함수가 나오면 맞다. 19회 정리 64가 요구하는 것이 정확히 \(F'=f\)이므로, 그 확인이 곧 정리의 가정을 검사하는 것이다.
2절이 이 노트북에서 가장 값어치 있는 셀을 갖는다. 서술 파트가 “가장 흔한 계산 실수”라고 한 것 — 치환에서 적분 구간을 바꾸지 않는 것 — 을 실제로 저질러 보고 틀린 값을 출력한다.
서술 파트의 내용 |
여기서 확인하는 방법 |
|---|---|
정리 75가 곱의 미분에서 나온다 |
1절이 \((fg)'\)의 적분을 두 방식으로 계산한다 |
1절이 \(x\log x - x\)를 미분해 되돌린다 |
|
\(\int_0^{\pi/2}x\cos x = \pi/2-1\) |
1절이 상합·하합으로 좁혀 맞춘다 |
2절이 양변을 따로 계산한다 |
|
구간을 안 바꾸면 틀린다 |
2절이 틀린 값을 실제로 낸다 |
2절이 \(-\log\lvert\cos x\rvert\)를 미분한다 |
|
반복 적용 |
3절이 방정식을 세워 푼다 |
\(\int fg \ne \int f\int g\) |
3절이 두 값을 나란히 낸다 |
0. 준비#
정의로 적분하는 절차도 다시 둔다. 답을 맞춰 보는 데 쓴다.
# The integral of definition 7, used here only to check the answers
def lower_upper(f, a, b, n, samples=200):
points = np.linspace(a, b, n + 1)
left = points[:-1].reshape(n, 1)
right = points[1:].reshape(n, 1)
values = f(left + (right - left) * np.linspace(0.0, 1.0, samples).reshape(1, samples))
return (float(np.sum(values.min(axis=1) * np.diff(points))),
float(np.sum(values.max(axis=1) * np.diff(points))))
1. 부분적분#
정리 75의 증명은 \((fg)' = f'g + fg'\)의 양변을 적분한 것이다. 그 한 줄을 그대로 확인한다.
# The identity the proof starts from, and the two sides of theorem 22.1
f = x
g = sp.sin(x)
a, b = 0, sp.pi / 2
product_rule = sp.simplify(sp.diff(f * g, x) - (sp.diff(f, x) * g + f * sp.diff(g, x)))
print("(fg)' - (f'g + fg') =", product_rule)
left = sp.integrate(f * sp.diff(g, x), (x, a, b))
right = (f * g).subs(x, b) - (f * g).subs(x, a) - sp.integrate(sp.diff(f, x) * g, (x, a, b))
print()
print("left side of (22.1) :", sp.simplify(left))
print("right side of (22.1) :", sp.simplify(right))
print("equal :", sp.simplify(left - right) == 0)
(fg)' - (f'g + fg') = 0
left side of (22.1) : -1 + pi/2
right side of (22.1) : -1 + pi/2
equal : True
첫 줄이 \(0\)이므로 곱의 미분 법칙이 성립하고, 그것을 적분한 결과가 (22.1)이다.
계산 예를 되돌린다#
찾은 원시함수를 미분해 원래 피적분함수가 나오는지 본다.
# Each primitive, differentiated back to check
xp = sp.Symbol("xp", positive=True)
cases = [
(x * sp.cos(x), x * sp.sin(x) + sp.cos(x), x, "x cos x"),
(sp.log(xp), xp * sp.log(xp) - xp, xp, "log x"),
(x * sp.exp(x), x * sp.exp(x) - sp.exp(x), x, "x e^x"),
]
print(f"{'integrand':>12} {'primitive found':>28} {'derivative matches':>19}")
print("-" * 64)
for integrand, primitive, var, label in cases:
back = sp.simplify(sp.diff(primitive, var) - integrand)
print(f"{label:>12} {str(primitive):>28} {str(back == 0):>19}")
integrand primitive found derivative matches
----------------------------------------------------------------
x cos x x*sin(x) + cos(x) True
log x xp*log(xp) - xp True
x e^x x*exp(x) - exp(x) True
세 줄 모두 되돌아온다.
\(\int\log x\,dx\)가 특히 그렇다. 피적분함수가 곱이 아닌데 \(1\cdot\log x\)로 보아 부분적분을 썼고, 그 결과 \(f'g = \frac1x\cdot x = 1\)이 되어 남은 적분이 쉬워졌다.
# Why g' = 1 works for log: the leftover integral becomes trivial
print("f = log x, g' = 1 -> g = x, f' = 1/x")
print("leftover integrand f' g =", sp.simplify(sp.diff(sp.log(xp), xp) * xp))
f = log x, g' = 1 -> g = x, f' = 1/x
leftover integrand f' g = 1
정적분을 두 방법으로#
\(\int_0^{\pi/2} x\cos x\,dx = \frac{\pi}{2}-1\)이라 했다. 상합과 하합으로 좁혀 맞춘다.
# By parts, against the definition of the integral
by_parts = sp.integrate(x * sp.cos(x), (x, 0, sp.pi / 2))
print("by parts :", by_parts, "=", f"{float(by_parts):.9f}")
low, high = lower_upper(lambda t: t * np.cos(t), 0.0, np.pi / 2, 4000)
print(f"lower sum : {low:.9f}")
print(f"upper sum : {high:.9f}")
print(f"the two trap it : {low <= float(by_parts) <= high}")
by parts : -1 + pi/2 = 0.570796327
lower sum : 0.570575952
upper sum : 0.571016636
the two trap it : True
한 줄과 사천 조각이 같은 값을 준다. 19회 6절에서 본 대비가 여기서도 나온다.
2. 치환적분#
정리 76의 양변을 따로 계산한다. \(g(x)=1+x^2\), \(f(u)=u\)로 두고 \([0,1]\)에서 본다.
# Both sides of (22.4), computed independently
u = sp.Symbol("u", real=True)
inner = 1 + x ** 2
outer = u
left = sp.integrate(outer.subs(u, inner) * sp.diff(inner, x), (x, 0, 1))
right = sp.integrate(outer, (u, inner.subs(x, 0), inner.subs(x, 1)))
print("left side, in x :", left)
print("right side, in u :", right)
print("equal :", sp.simplify(left - right) == 0)
left side, in x : 3/2
right side, in u : 3/2
equal : True
구간을 안 바꾸면#
서술 파트가 “가장 흔한 계산 실수”라 한 것을 실제로 저질러 본다.
# The mistake, made on purpose, so the size of the error is visible
direct = sp.integrate(sp.expand(2 * x * (1 + x ** 2)), (x, 0, 1))
correct = sp.integrate(u, (u, 1, 2))
mistaken = sp.integrate(u, (u, 0, 1))
print("expanding and integrating in x :", direct)
print("substituting, limits changed :", correct)
print("substituting, limits unchanged :", mistaken, " <- wrong")
print()
print("the error is", sp.simplify(correct - mistaken), "which is",
f"{float((correct - mistaken) / correct) * 100:.0f}% of the answer")
expanding and integrating in x : 3/2
substituting, limits changed : 3/2
substituting, limits unchanged : 1/2 <- wrong
the error is 1 which is 67% of the answer
세 번째 줄이 틀렸다. 그리고 틀린 값도 그럴듯한 수이므로 결과만 보아서는 알아채기 어렵다. 그래서 첫 줄처럼 다른 방법으로 확인하는 습관이 필요하다.
계산 예를 되돌린다#
# The three substitution examples, differentiated back
substitution_cases = [
(2 * x / (1 + x ** 2), sp.log(1 + x ** 2), "2x / (1 + x**2)"),
(sp.sin(x) ** 3, -sp.cos(x) + sp.cos(x) ** 3 / 3, "sin^3 x"),
]
print(f"{'integrand':>18} {'primitive':>30} {'matches':>9}")
print("-" * 60)
for integrand, primitive, label in substitution_cases:
back = sp.simplify(sp.diff(primitive, x) - integrand)
print(f"{label:>18} {str(primitive):>30} {str(back == 0):>9}")
integrand primitive matches
------------------------------------------------------------
2x / (1 + x**2) log(x**2 + 1) True
sin^3 x cos(x)**3/3 - cos(x) True
\(\int\tan x\,dx\)는 따로 본다. 답에 절댓값이 들어 있어 \(\cos x\)의 부호에 따라 갈리기 때문이다.
# (22.6): check separately where cos is positive and where it is negative
for label, sample, primitive in (
("cos x > 0", 0.7, -sp.log(sp.cos(x))),
("cos x < 0", 2.5, -sp.log(-sp.cos(x)))):
derivative = sp.simplify(sp.diff(primitive, x))
numeric = float(derivative.subs(x, sample))
print(f"{label:>10} at x = {sample:<5} derivative = {sp.simplify(derivative)}"
f" value {numeric:.9f} tan x = {np.tan(sample):.9f}")
cos x > 0 at x = 0.7 derivative = tan(x) value 0.842288380 tan x = 0.842288380
cos x < 0 at x = 2.5 derivative = tan(x) value -0.747022297 tan x = -0.747022297
두 경우 모두 도함수가 \(\tan x\)이다. \(\cos x<0\)인 쪽에서는 \(\log(-\cos x)\)를 써야 하고, 그 둘을 한 식으로 묶은 것이 \(\log|\cos x|\)이다.
21회 따름정리 12가 없으면 두 번째 줄을 쓸 수 없다. \(\log\)의 정의역이 양수뿐이기 때문이다.
두 그림에서 파란 곡선의 기울기가 검은 곡선의 높이이다. \(\cos x\)가 부호를 바꾸는 자리에서 두 그림이 갈린다.
3. 반복 적용과 두 가지 주의#
\(I=\int e^x\sin x\,dx\)를 서술 파트의 방식으로 푼다. 두 번 부분적분하면 \(I\)가 다시 나오고, 그것을 방정식으로 본다.
# Integrate by parts twice, symbolically, and watch the original integral return
first = sp.exp(x) * sp.sin(x) - sp.integrate(sp.exp(x) * sp.cos(x), x)
print("after one application :", sp.simplify(first))
answer = sp.exp(x) * (sp.sin(x) - sp.cos(x)) / 2
print("solving the equation :", answer)
print("differentiate back :",
sp.simplify(sp.diff(answer, x) - sp.exp(x) * sp.sin(x)) == 0)
after one application : -sqrt(2)*exp(x)*cos(x + pi/4)/2
solving the equation : (sin(x) - cos(x))*exp(x)/2
differentiate back : True
세 번째 줄이 참이므로 (22.7)이 맞다.
적분은 곱으로 갈라지지 않는다#
# The integral of a product is not the product of the integrals
both = sp.integrate(x * x, (x, 0, 1))
apart = sp.integrate(x, (x, 0, 1)) * sp.integrate(x, (x, 0, 1))
print("integral of x * x :", both)
print("product of two integrals :", apart)
print("equal :", both == apart)
integral of x * x : 1/3
product of two integrals : 1/4
equal : False
\(\frac13\)과 \(\frac14\)이다. 8회 정리 22는 합과 상수배만 주고, 곱에 대한 규칙은 없다. 이 회차의 두 기법이 필요한 이유가 그것이다.
두 기법으로 안 되는 것#
# Some integrands have no elementary primitive; sympy says so in its own way
hard = sp.exp(-x ** 2)
result = sp.integrate(hard, x)
print("sympy's answer for exp(-x**2) :", result)
print()
print("but the definite integral exists and can be trapped:")
low, high = lower_upper(lambda t: np.exp(-t ** 2), 0.0, 1.0, 4000)
print(f" lower sum {low:.9f} upper sum {high:.9f}")
sympy's answer for exp(-x**2) : sqrt(pi)*erf(x)/2
but the definite integral exists and can be trapped:
lower sum 0.746745114 upper sum 0.746903144
sympy가 내놓은 것은 새 기호이지 우리가 아는 함수들의 조합이 아니다. 그런데 아래 두 줄은 정적분이 멀쩡히 존재함을 보인다.
19회 5절이 적은 대로 존재와 표현은 다른 문제이다. 원시함수는 있고, 이름이 없을 뿐이다.
마치며#
\((fg)' - (f'g+fg')\)가 \(0\)임을 확인하고, 그것을 적분한 결과가 (22.1)의 양변으로 갈라지는 것을 보았다. 부분적분이 곱의 미분 법칙에서 나온다는 말이 코드로 확인된다.
세 원시함수를 미분해 되돌렸다. \(x\sin x+\cos x\), \(x\log x - x\), \(xe^x - e^x\)이다. 원시함수를 찾는 데에는 규칙이 없지만 확인하는 데에는 규칙이 있다.
\(\int_0^{\pi/2}x\cos x\,dx\)를 부분적분으로 한 줄에 구했고, 사천 조각의 상합과 하합이 그 값을 가두었다.
치환에서 구간을 바꾸지 않는 실수를 실제로 저질러 \(\frac12\)을 얻었다. 옳은 값은 \(\frac32\)이므로 답의 3분의 2만큼 틀린다. 그런데 틀린 값도 그럴듯한 수라 결과만으로는 알 수 없다.
\(\int\tan x\)는 \(\cos x\)의 부호에 따라 두 경우로 갈렸고, 두 경우를 한 식으로 묶는 것이 \(\log|\cos x|\)였다. 21회 따름정리 12가 그것을 준비해 두었다.
더 해 볼 것#
1절의 \(\int x^n e^x\,dx\)를 \(n=1,2,3\)에서 sympy로 구해 보자. 항이 몇 개씩 늘어나는가. 부분적분을 몇 번 써야 하는지와 어떤 관계인가.
2절의 치환 예에서 \(g(x)=1+x^2\) 대신 \(g(x)=x^2\)로 두면 어떻게 되는가. \(g(a)\)와 \(g(b)\)가 무엇이 되는가.
2절의 \(\int\tan x\)를 \([0,\pi]\)에서 정적분해 보자. 무슨 일이 일어나는가. 정리 76의 어느 가정이 깨지는가.
3절의 \(I=\int e^x\cos x\,dx\)를 같은 방식으로 풀어 보자. 부호가 어디서 달라지는가.
짝이 되는 서술