본문 바로가기
파이썬으로 풀어보는 고딩수학/수열

등비수열

by hustler78 2024. 4. 1.
728x90
반응형

 

지난 글에서는 등차수열에 대해 알아보고, 이를 sympy를 이용하여 어떻게 풀지 알아봤었어. 자세한 건 등차수열

 

등차수열

이 카테고리에서는 sympy를 이용하여 각종 수열에 관련된 내용을 다뤄볼 예정이야. 수열은 말그대로 수의 나열이라는 뜻이야. 이 수의 나열에서는 보통 규칙이 보이진 않지만, 차이가 일정하다든

sine-qua-none.tistory.com

 

를 참고해 봅시다. 

 

이번엔 등비수열이야. 어떤 수열이 있는데 수열간의 증가(또는 감소)하는 비율이 일정한 수열이지. 예를 들어

$$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에서 식을 간단히 할 때 쓴다 (위 예제에서는 간단히 안되었지만..)

 

728x90
반응형

댓글