지난 글에서는 등차수열에 대해 알아보고, 이를 sympy를 이용하여 어떻게 풀지 알아봤었어. 자세한 건 등차수열
를 참고해 봅시다.
이번엔 등비수열이야. 어떤 수열이 있는데 수열간의 증가(또는 감소)하는 비율이 일정한 수열이지. 예를 들어
$$1~,~2,~,~ 4,~,~8,~,~16, \cdots $$
는 항들 간의 비율이 $2$로 일정하잖아. 이러한 수열도 등차수열과 마찬가지로 첫 번째 항과 항 사이의비율 - 이것을 공비라 함 - 을 알면 모든 수열을 구할 수가 있어.
등비수열
등비수열은 항끼리의 비율이 같은 수열을 의미해. 첫째항과 항끼리의 비율인 공비로 수열의 속성이 결정되지.
첫째항이 $a$이고 공차가 $r$인 등차수열의 $n$번째 항을 $a_n$이라 하며,
$$ a_n = a\cdot r^{n-1} $$
python의 sympy 로 등비수열은 어떻게 세팅할 수 있을까? 등차수열 글을 읽다 보면, 함수
RecursiveSeq
가 나와. 이것을 이용하여 점화식을 만들어 내면 되지. 등비수열 $\{a_n\}$의 점화식은
$$ a_{n+1} = r \cdot a_n$$
임을 이용해 보자.
from sympy import *
from sympy.series.sequences import * # 아래 RecursiveSeq 를 이용하기 위해 필요
n = symbols("n", integer=True) # 수열의 항 번호
a = symbols("a") # 수열의 첫째항
y = Function("y") # 점화식을 나타내기 위한 함수
r = symbols("r") # 공비
geo = RecursiveSeq(y(n - 1) * r, y(n), n, [a])
# 첫째항이 a이고 y(n-1) *r 가 y(n)이 되는데 n이라는 변수를 매개로 되는 수열
print(geo[:10]) # 10번째 항까지 출력
위 code의 결과를 보자면,
[a, a*r, a*r**2, a*r**3, a*r**4, a*r**5, a*r**6, a*r**7, a*r**8, a*r**9]
등비수열 geo의 첫째 항부터 10번째 항까지 출력해 보니, 우리에게 익숙한 식처럼 표현이 되지?
문제 1
제 2항이 9, 제5항이 243이고, 공비가 실수인 등비수열 $\{a_n\}$에 대해
1) 이 수열의 첫째항과 공비는?
2) 이 수열은 제 몇 항에서 처음으로 1000보다 커지는가?
3) 수열 $\{b_n\}$이 $b_n = (a_{n+1})^2 -a_n^2를 만족할 때, 수열 $b_n$은?
이거 뭐 간단한 문제지? 수열 $a_n$을
$$a_n = ar^{n-1}$$
라 쓰면
$$a_2 = ar = 9~,~ a_5= ar^4 = 243$$ 이므로 두 식을 조합하면
$$r^3= 27\tag{1}$$ 이므로 $r=3$이 나오지. 따라서 $a=3$이고 결론적으로
$$a_n = 3\cdot 3^{n-1} = 3^n$$
이 나오게 됨!
1)번은 저절로 해결이 된 거고,
2) 번은 $3^n >1000$을 풀면 되는 문제임. 대충 때려 맞출 수 있는데,
$3^6=729$이므로 $3$의 7승을 하면 천이 넘는다. 따라서 $n=7$이지!
3) 번은
$$ b_n = a_{n+1}^2 - a_n^2 = 3^{2(n+1)} - 3^{2n} = 8\cdot 3^{2n}$$
이라는 수열이 얻어지지.
이거 한 번 python으로 풀어볼까?
import numpy as np
from sympy import *
from sympy.series.sequences import * # 아래 RecursiveSeq 를 이용하기 위해 필요
n = symbols("n", integer=True) # 수열의 항 번호
a = symbols("a", real=True) # 수열의 첫째항
z = Function("z") # 점화식을 나타내기 위한 함수
r = symbols("r", real=True) # 공비
geo = RecursiveSeq(z(n - 1) * r, z(n), n, [a]) # 등비수열 점화식 생성
eqn_list = [geo[1] - 9, geo[4] - 243]
# a_2= 9, a_5= 243을 코드로 표현한 것
# python의 index는 0부터 시작하므로 geo[1]이 두번째 항, geo[4]가 5번째 항이 됨
res = solve(eqn_list, {a, r}) # 위 두 방정식을 a,r에 대해서 풀고, dictionary 형태로 반환
print(res) # {a: ? , r:? } # dictionary 형 결과 출력
자 그러면 결과를 보자고.
[{a: 3, r: 3}]
원소의 형태가 dictionary 형인 list를 결과로 제시해 주지. 만일 $(a, r)$의 해가 1개가 아니라면, 이 list의 원소는 하나가 아닐 거야.
만일, 위 코드 중 위에서 5, 7번째 줄에, r을 정의해 놓은 부분을 보자고.
a = symbols("a", real=True)
r = symbols("r", real=True)
얘를 보면, real=True라는 조건을 달아서 $a, r$ 이 항상 실수가 나오도록 해를 찾아내지. 만일 이 부분을 생략하면 어떨까?
즉,
a = symbols("a")
r = symbols("r")
이렇게 말이지. 그러면 결과는
[{a: 3, r: 3}, {a: -3/2 - 3*sqrt(3)*I/2, r: -3/2 + 3*sqrt(3)*I/2}, {a: -3/2 + 3*sqrt(3)*I/2, r: -3/2 - 3*sqrt(3)*I/2}]
이렇게 나오지. 즉 세 개의 dictionary 형 원소 3개로 이루어진 list가 결괏값으로 출력돼. 그런데 2번째, 3번째를 보면 값에
I
라는 기호가 붙어 있네? 바로
$$ I=\sqrt {-1}$$
를 뜻하는 기호야. 구체적으로 말해서, 수식(1)을 풀 때, 복소수를 허용하게 되면 값이 3개가 나오게 돼. 그중 하나는 실수이고 나머지 2개는 복소수야.
이제 2) 번 문제 즉, $a_n>1000$인 최소의 $n$을 찾아보기 위해, 아래의 code를 덧붙여 보자.
geo = geo.subs(res[0]) # solve로 구한 a,r값을 geo 점화식에 대입
geo_head = geo[:10] # 이 점화식의 첫 10개의 값을 list형식으로 추출
geo_head = np.array(geo_head) # numpy.where를 사용하기 위해, array형태로 변환
target_index = np.where(geo_head > 1000) # where 문을 사용하여 조건을 만족하는 index 구함
print(target_index) # 조건에 부합한 index 출력
앞선 코드의 res[0]을 하면 {a:3, r:3} 이라는 dictionary 형 결과가 나오고, 이것을 subs를 이용하여 원래 점화식에 넣어주는 것이야. 그리고 점화식에서 추출된 첫 10개의 항을 뽑아 본 뒤 1000이 넘은 값을 가진 index를 다 뽑아보는 거야.
결과는 아래와 같아.
(array([6, 7, 8, 9], dtype=int64),)
즉, 처음으로 1000을 넘는 값을 가진 index가 6이라는 얘기지. 다시 한번 말하지만 python은 0부터 index가 시작하므로 index 6은 제7항을 뜻한다. 따라서 답은 7이지!
마지막으로 3) 번 문제를 풀어보자. 애석하게도 RecursiveSeq를 그대로 이용하여 푸는 방법을 찾지 못했으므로 우리에게 아주 익숙한 식
$$ a_n = a\cdot r^{n-1}$$
을 가지고 풀어볼게. 아래의 코드를 첨가해 봐.
geo = a * r ** (n - 1) # geo 를 등비수열 일반항 공식으로 setting
geo = geo.subs(res[0]) # 여기에 res[0] 즉, {a:3, r:3} 을 대입
new_seq = geo_head[1:] ** 2 - geo_head[:-1] ** 2 # a_{n+1}^2 - a_{n}^2 구함 -> new_seq
ratio_new_seq = new_seq[1:] / new_seq[:-1] # 위 수열의 항 간 비율을 계산해봄 ->new_seq 원소간 비율
print(new_seq) # new_seq 출력
print(ratio_new_seq) # new_seq 항 간 비율 출력
new_seq = geo.subs(n, n + 1) ** 2 - geo ** 2 # a_{n+1}^2- a_n^2 을 직접 구함
print(simplify(new_seq)) # 위 formula를 simplify하여 구함
지금 $$b_{n} = a_{n+1}^2 - a_n^2 \tag {2}$$을 구해야 해. 이웃한 두 항의 제곱의 차이를 구해야 해. 따라서
new_seq = geo_head[1:] ** 2 - geo_head[:-1] ** 2 # a_{n+1}^2 - a_{n}^2 구함 -> new_seq
이게 바로 그 부분이야.
* geo_head에 [1:]이라는 slicing을 걸어주면, index가 1부터 마지막항을 뜻하게 되고,
* geo_head에 [:-1]이라는 slicing을 걸어주면, index가 처음인 0부터 마지막항을 제외한 것을 뜻하게 되므로
정확히 식 (2)의 $b_n$을 구하는 과정인 거지. 그리고 마찬가지 방법으로
ratio_new_seq = new_seq[1:] / new_seq[:-1] # 위 수열의 항 간 비율을 계산해봄 ->new_seq 원소간 비율
이렇게 하면 $b_n$ 의 항 들간 비율을 구할 수 있게 되고.
결론적으로 python code를 실행시키면 다음의 결과가 나온다.
[72 648 5832 52488 472392 4251528 38263752 344373768 3099363912]
[9 9 9 9 9 9 9 9]
3**(2*n + 2) - 9**n
첫 번째 결과는 $b_n$의 처음 10개 항을 구하는 내역이야. 아무래도 공비 3이 영향을 미치니 숫자가 기하급수적으로 커지지.
두 번째 결과는 $b_n$이 항간 비율을 구한 것! 보면 모든 값이 9이므로 $b_n$이 공비를 9로 하는 등비수열이라는 냄새가 풀풀 나지.
마지막 결과는 직접 $b_n$을 구해 본 것이야. 위 결과에 따르면
$$ b_n = 3^{2n+2} - 9^n$$
이라는 뜻이지.
한 가지 고백할 것이, simplify 명령어 등으로 위 식을 간단하게 만들어보려고 아무리 발버둥을 쳐도 안되더라고. 어쨌든 python으로 위의 수준까지 답을 계산했으니, 나머지 간단화는 사람의 손을 빌려보자.
$$b_n = 3^{2n+2} - 9^n = 9^n \cdot 9 - 9^n = 8\cdot 9^n = 72 \cdot 9^{n-1}$$
어때? 새로운 등비수열의 형태가 나왔네. 바로 첫째항이 72이고 공비가 9인 등비수열이지!
정리
ResursiveSeq | 점화식을 구현 from sympy.series.sequences import * 의 import 문을 사용하여 해당 라이브러리를 탑재해 주는 것이 좋음 |
subs | 방정식에 값을 대입하는 함수. 2변수 이상일 때는 dictionary 형으로 |
solve | 2변수 이상에 대해 풀 때는 반환값이 dictionary형태이므로, 그냥 이 반환값을 subs로 방정식에 넣어 줄수 있음. 예 ) 위 코드의 subs(res) 참조 |
simplify | sympy에서 식을 간단히 할 때 쓴다 (위 예제에서는 간단히 안되었지만..) |
'파이썬으로 풀어보는 고딩수학 > 수열' 카테고리의 다른 글
등차수열 2번째 이야기(feat. 피타고라스 정리) (0) | 2024.04.01 |
---|---|
등차수열 (0) | 2024.01.02 |
댓글