IT’s Portfolio

[Python] Fibonacci Sequence in Python λ³Έλ¬Έ

Development Study/Python

[Python] Fibonacci Sequence in Python

f1r3_r41n 2023. 1. 16. 13:13
728x90
λ°˜μ‘ν˜•

πŸ’» Fibonacci Sequence in Python

πŸ’‍♀️ What Fibonacci Sequence?

  • λ‹¨μˆœν•œ 단쑰 증가(monotonically increasing) μˆ˜μ—΄λ‘œ μ•žμ˜ 두 수λ₯Ό 더해가며 μƒμ„±λ˜λŠ” μˆ˜μ—΄
    • $F_{i+1} = F_{i} + F_{i-1}$
      • Fibonacci Seq 곡식 : $F_{i} = F_{i-1} + F_{i-2}$
    • $F_{0} = 0$
    • $F_{1} = F_{2} = 1$
  • μœ„μ˜ μˆ˜μ‹λ§Œ 보면 Fibonacci μˆ˜μ—΄μ˜ n번째 수λ₯Ό μ°ΎλŠ” μ½”λ“œλŠ” μ‰½κ²Œ κ΅¬ν˜„μ΄ κ°€λŠ₯
  • μ•„λž˜μ—μ„œ μ„œμˆ ν•  μ½”λ“œλ“€μ— 1100을 각각 ν• λ‹Ήν•˜μ—¬ μ£Όν”Όν„° λ…ΈνŠΈλΆμ˜ %%timeit 을 μ‚¬μš©ν•΄ 처리 μ‹œκ°„μ„ ν™•μΈν•΄λ³΄μž
    • μ½”λ“œλ“€μ˜ print ν•¨μˆ˜λ₯Ό λͺ¨λ‘ μ œμ™Έν–ˆκ³  λ‹¨μˆœ ν•¨μˆ˜ 호좜만으둜 μ‹œκ°„μ„ 츑정함

πŸ‘€ λ‹¨μˆœ μž¬κ·€

  • λ‹¨μˆœνžˆ κ³΅μ‹λ§Œ λŒ€μž…ν•˜μ—¬ μž¬κ·€μ μœΌλ‘œ ν’€μ΄ν•΄λ³΄μž
def fibo(n):
    return n if n<=1 else fibo(n-1) + fibo(n-2)

num = int(input())
print(fibo(num))
  • 곡식에 따라 0번째 μˆ˜λŠ” 0이며 1번째 μˆ˜λŠ” 1
  • n이 2 이상일 κ²½μš°λŠ” μž¬κ·€ μ²˜λ¦¬ν•˜λ©° κ°’ λ°˜ν™˜
  • n의 μΈμžκ°’μ— 32 μ΄μƒμ˜ μˆ«μžκ°€ ν• λ‹Ήλ˜λ©΄ κ²°κ³Όκ°’ λ°˜ν™˜ 속도가 λˆˆμ— λ„κ²Œ 느렀짐
  • ν•„μžμ˜ ν„°λ―Έλ„μ—μ„œλŠ” 43 μ΄μƒμ˜ μˆ«μžλΆ€ν„° ν”„λ‘œμ„ΈμŠ€κ°€ λ©ˆμΆ”λŠ” κ²ƒμœΌλ‘œ 확인됨
  • 1100 κ°’ ν• λ‹Ή -> μ—°μ‚° λΆˆκ°€
fibo = lambda n: n if n<=1 else fibo(n-1) + fibo(n-2)

num = int(input())
print(fibo(num))
  • λ‹¨μˆœ μž¬κ·€ μ½”λ“œλ₯Ό λžŒλ‹€ν‘œν˜„μ‹μœΌλ‘œ λ‚˜νƒ€λ‚Έ μ½”λ“œ
  • 1100 κ°’ ν• λ‹Ή -> μ—°μ‚° λΆˆκ°€
  • ν•΄λ‹Ή 방법은 ν•¨μˆ˜κ°€ ν•œ 번 호좜되면 λ‹€μ‹œ 두 번 호좜되기 λ•Œλ¬Έμ— μ‹œκ°„λ³΅μž‘λ„λŠ” $O(2^n)$ 이 됨

πŸ‘€ λ¬΄μž‘μ • 반볡

  • 보톡 Fibonacci SequenceλŠ” μž¬κ·€ν•¨μˆ˜μ— λŒ€ν•œ κ°œλ…μ„ μŠ΅λ“ν•˜λ €κ³  κ³΅λΆ€ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄μ§€λ§Œ λ¬΄μž‘μ • λ°˜λ³΅μ„ 톡해 결과값을 얻을 수 있음
def fibo(n):
    fibo_arr = [0, 1]

    for i in range(len(fibo_arr), n+1):
        fibo_arr[i%2] = fibo_arr[(n-1)%2] + fibo_arr[(n-2)%2]

    return fibo_arr[n%2]

num = int(input())
print(fibo(num))
  • Fibonacci Sequence μ •μ˜μ— 맞게 λ³€μˆ˜ μ΄ˆκΈ°ν™” ν›„ nλ²ˆμ§ΈκΉŒμ§€ λ°˜λ³΅μ„ 톡해 λͺ¨λ“  항을 ꡬ함
  • 1100 κ°’ ν• λ‹Ή -> 125 µs ± 692 ns
def fibo(n):
    if n <= 1: return n
    a, b = 0, 1

    for _ in range(n-1):
        a, b = b, a+b

    return b

num = int(input())
print(fibo(num))
  • λ‹¨μˆœ λ³€μˆ˜ 두 개만으둜 κ΅¬ν˜„ν•œ 같은 λ°©μ‹μ˜ μ½”λ“œ
  • 1100 κ°’ ν• λ‹Ή -> 37.8 µs ± 84.6 ns
  • ν•΄λ‹Ή 방법은 μž…λ ₯값인 n만큼 κ³„μ‚°ν•˜κΈ° λ•Œλ¬Έμ— μ‹œκ°„λ³΅μž‘λ„λŠ” $O(n)$
  • μ‹œκ°„λ³΅μž‘λ„λ‘œ 비ꡐ할 μ‹œ λ‹¨μˆœ μž¬κ·€λ³΄λ‹€ 훨씬 효율적인 것을 μ•Œ 수 있음

πŸ‘€ μž¬κ·€μ  동적 κ³„νšλ²•

  • 동적 κ³„νšλ²• : λΆ€λΆ„λ¬Έμ œλ₯Ό ν•΄κ²°ν•˜λ©΄ ν•΄λ‹Ή 값을 μ €μž₯ν•˜λŠ” 곡간 μ‚¬μš©
  • 배열을 μ‚¬μš©ν•œ μž¬κ·€μ  풀이
  • μ €μž₯곡간에 μžˆλŠ” λΆ€λΆ„λ¬Έμ œμ˜ 값을 μ°Ύκ³  μ—†μœΌλ©΄ 계산
def fibo(n):
    fibo_arr = [0, 1]

    def iterator(n):
        if n<2: return n
        try:
            if fibo_arr[n]: return fibo_arr[n]
        except IndexError:
            pass

        fibo_arr.append(iterator(n-1)+iterator(n-2))
        return fibo_arr[n]

    return iterator(n)

num = int(input())
print(fibo(num))
  • 1100 κ°’ ν• λ‹Ή -> 365 µs ± 188 ns
  • 동적 κ³„νšλ²•μ€ λ¬΄μž‘μ • 반볡 μ½”λ“œμ—λ„ 적용이 κ°€λŠ₯ -> 반볡적 동적 κ³„νšλ²•
  • μž¬κ·€μ  동적 κ³„νšλ²•μ€ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” 데 λ”°λ₯΄λŠ” μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— μ ˆλŒ€μ μœΌλ‘œλŠ” 반볡적 동적 κ³„νšλ²•λ³΄λ‹€ μ‹œκ°„μ΄ 였래 κ±Έλ¦Ό
    • ν•˜μ§€λ§Œ λ‘˜ λ‹€ μ‹œκ°„λ³΅μž‘λ„λŠ” $O(n)$
    • μ˜€λ²„ν—€λ“œ(overhead) : μ–΄λ–€ 처리λ₯Ό ν•˜κΈ° μœ„ν•΄ λ“€μ–΄κ°€λŠ” 간접적인 μ‹œκ°„, λ©”λͺ¨λ¦¬ 등을 칭함
def fibo(n, __fibo_arr={0: 0, 1: 1}):
    if n in __fibo_arr: return __fibo_arr[n]

    __fibo_arr[n] = fibo(n-1)+fibo(n-2)
    return __fibo_arr[n]

num = int(input())
print(fibo(num))
  • 파이썬 ν•¨μˆ˜μ˜ λ™μž‘ 방식을 ν™œμš©ν•œ μž¬κ·€μ  동적 κ³„νšλ²•μ˜ 또 λ‹€λ₯Έ μ½”λ“œ
  • 보톡 ν•¨μˆ˜ 내에 μ„ μ–Έλœ λ°μ΄ν„°λŠ” ν•¨μˆ˜κ°€ 호좜될 λ•Œ μƒμ„±λ˜κ³  ν•¨μˆ˜κ°€ μ’…λ£Œλ  λ•Œ 폐기되며 μžμ› νšŒμˆ˜λ„ 이루어짐
    • μœ„ μ½”λ“œμ™€ 같이 μ›ν•˜λŠ” 데이터λ₯Ό ν•¨μˆ˜ μ •μ˜λΆ€μ— 적으면 κ·Έ μžλ£Œκ΅¬μ‘°λŠ” ν•¨μˆ˜κ°€ μ •μ˜λ  λ•Œ μƒμ„±λ˜μ–΄ ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜κ±°λ‚˜ μ’…λ£Œλ˜κ±°λ‚˜ 상관없이 ν•¨μˆ˜ μžμ²΄κ°€ λ©”λͺ¨λ¦¬μ—μ„œ μ§€μ›Œμ§€κΈ° μ „κΉŒμ§€ 값이 μœ μ§€λ¨
    • ν•¨μˆ˜ μ‹€ν–‰ μ‹œ 데이터가 κΎΈμ€€νžˆ λ³€ν™”ν•˜κ³  값이 λ³΄μ‘΄λ˜κΈ°μ— μ˜ˆμƒν•˜μ§€ λͺ»ν•œ μ—λŸ¬λ₯Ό λ§ˆμ£Όν•  수 μžˆλŠ” 방식
    • 파이썬의 λ‚΄λΆ€ λ™μž‘ 방식을 ν™œμš©ν•˜κΈ°μ— νŒŒμ΄μ¬μ—μ„œλ§Œ κ΅¬ν˜„ κ°€λŠ₯ν•œ 방식
  • 1100 κ°’ ν• λ‹Ή -> 266 µs ± 550 ns

πŸ‘€ ν–‰λ ¬ μ—°μ‚°

  • ν–‰λ ¬ μ—°μ‚°μ˜ Fibonacci 곡식
    • $(\frac{F_{n+1}} {F_{n}}$ $\frac{F_{n}} {F_{n-1}})$ $=$ $(\frac{1} {1}$ $\frac{1} {0})^n$
  • ν–‰λ ¬ μ—°μ‚° 쀑 κ³±μ…ˆκ³Ό κ±°λ“­μ œκ³±μ— λŒ€ν•œ ν¬μŠ€νŒ…
  • ν–‰λ ¬ μ—°μ‚° 쀑 ν–‰λ ¬μ˜ κ³±μ…ˆ 항등원에 λŒ€ν•œ ν¬μŠ€νŒ…
  • $n^{64}$ 을 κ΅¬ν•˜λ €λ©΄ $n$ 을 64번 κ³±ν•΄ ꡬ할 수 있음
    • 2의 제곱수λ₯Ό ν™œμš©ν•΄ 결과물을 6번 μ œκ³±ν•˜λ©΄ $n^{64}$
      • $log_264$
      • $(n^{1})^2 = (n^{2})^2 = (n^{4})^2 = (n^{8})^2 = (n^{16})^2 = (n^{32})^2 = n^{64}$
  • $n^{100}$ 을 κ΅¬ν•˜λ €λ©΄ $n$ 을 100번 κ³±ν•΄ ꡬ할 수 있음
    • λͺ¨λ“  μžμ—°μˆ˜λŠ” 2의 제곱수의 ν•©μœΌλ‘œ λ‚˜νƒ€λ‚Ό 수 있음
      • $n^{100} = n^{64} \times n^{32} \times n^4$
  • λ‹€μŒκ³Ό 같은 방법을 μ μš©ν•˜μ—¬ λΆ€λΆ„λ¬Έμ œμ˜ 값을 μ €μž₯해두고 ν•„μš” μ‹œ κΊΌλ‚΄μ„œ μ‚¬μš©ν•˜λŠ” 방식을 κ΅¬ν˜„
def fibo(n):
    # 2*2 λ°°μ—΄ 크기 κ³ μ •κ°’
    SIZE = 2
    # fibonacci sequence ν–‰λ ¬ μ—°μ‚°μ˜ κΈ°λ³Έκ°’
    BASE = [[1, 1], [1, 0]]
    # ν–‰λ ¬μ˜ κ³±μ…ˆμ— λŒ€ν•œ 항등원
    IDENTITY = [[1, 0], [0, 1]]

    # ν–‰λ ¬μ˜ κ³±μ…‰ μ—°μ‚° ν•¨μˆ˜
    def square_matrix_operation(a, b, size=SIZE):
        # 결과값을 담을 λ°°μ—΄ μ„ μ–Έ 및 μ΄ˆκΈ°ν™”
        new = [[0 for _ in range(size)] for _ in range(size)]

        for i in range(size):
            for j in range(size):
                for k in range(size):
                    new[i][j] += a[i][k] * b[k][j]
        return new

    # ν–‰λ ¬μ˜ nμŠΉμ„ κ΅¬ν•˜κΈ° μœ„ν•œ νŒλ‹¨ ν•¨μˆ˜
    def matrix_judgment(n):
        # μ΅œμ’… κ²°κ³Όλ¬Ό
        matrix = IDENTITY.copy()
        # μž„μ‹œμ μΈ λ°°μ—΄
        tmp = BASE.copy()
        k = 0

        # 0 μ΄μƒμ˜ μ •μˆ˜ k에 λŒ€ν•œ μžμ—°μˆ˜ n이 2**k λ₯Ό ν¬ν•¨ν•˜λŠ”μ§€ νŒλ‹¨ν•˜λŠ” μ½”λ“œ
        while 2 ** k <= n:
            # λΉ„νŠΈ μ—°μ‚°μžμΈ &와 <<λ₯Ό μ‚¬μš©ν•˜μ—¬ 포함 μ—¬λΆ€ νŒλ‹¨
            if n & (1 << k) != 0:
                matrix = square_matrix_operation(matrix, tmp)
            k+=1
            # BASEλ₯Ό 톡해 계속 2**k 행렬을 μ €μž₯ν•˜λŠ” μ½”λ“œ 
            tmp = square_matrix_operation(tmp, tmp)

        return matrix

    return matrix_judgment(n)[0][1]

num = int(input())
print(fibo(num))
  • BASE : Fibonacci Sequence ν–‰λ ¬ μ—°μ‚° κ³΅μ‹μ˜ 기본이 λ˜λŠ” λ³€μˆ˜
  • IDENTITY : ν–‰λ ¬ κ³±μ…ˆμ˜ 항등원 λ³€μˆ˜
  • square_matrix_operation : μ •λ°©ν˜• ν–‰λ ¬μ˜ κ³±μ…ˆμ„ κ΅¬ν˜„ν•œ ν•¨μˆ˜
  • matrix judgment : square_matrix_operation ν•¨μˆ˜μ—κ²Œ μ–΄λ–€ 값을 λ„˜κΈΈμ§€ νŒλ‹¨ν•˜λŠ” ν•¨μˆ˜
    • matrix : $n$의 값이 0일 λ•Œλ₯Ό κ³ λ €ν•˜μ—¬ 항등원을 copyν•œ λ³€μˆ˜
    • tmp : $k$의 값이 컀짐에 따라 κΎΈμ€€νžˆ $2^k$ 행렬을 μ €μž₯ν•˜λŠ” λ³€μˆ˜
    • $n$이 $2^k$λ₯Ό ν¬ν•¨ν•˜λŠ”μ§€μ— λŒ€ν•œ μ—¬λΆ€λ₯Ό while 반볡문과 λΉ„νŠΈ μ—°μ‚°μžλ‘œ νŒλ‹¨
      • ν¬ν•¨ν•œλ‹€λ©΄ $n$을 κ΅¬μ„±ν•˜λŠ” $2^k$ μ‘°κ±΄μ—μ„œ square_matrix_operation ν•¨μˆ˜μ— matrix 와 tmp λ₯Ό λ„˜κΉ€
  • 1100 κ°’ ν• λ‹Ή -> 43.9 µs ± 43.4 ns
  • ν•΄λ‹Ή λ°©λ²•μ˜ μ‹œκ°„λ³΅μž‘λ„λŠ” $O(log_2n)$

πŸ‘€ μΌλ°˜ν•­ μ—°μ‚°

  • Fibonacci 식
    • $F_{i+1} = F_i + F_{i-1}$
    • $F_1 = F_2 = 1$
  • $\alpha$와 $\beta$ μ •μ˜
    • $\alpha + \beta = 1$
    • $\alpha\beta = -1$
  • 방정식 1
    • $F_{i+1} - F_i - F_{i-1} = 0$
    • $F_{i+1} - F_i = \beta(F_i - F_{i-1})$
    • = $\beta^2(F_{i-1} - \alpha F_{i-2})$
    • = $\beta^{i-1}(F_2 - \alpha F_1)$
    • = $\beta^{i-1}(1 - \alpha)$
  • 방정식 2
    • $F_{i+1} - F_i - F_{i-1} = 0$
    • $F_{i+1} - F_i = \alpha(F_i - F_{i-1})$
    • = $\alpha^2(F_{i-1} - \beta F_{i-2})$
    • = $\alpha^{i-1}(F_2 - \beta F_1)$
    • = $\alpha^{i-1}(1 - \beta)$
  • 방정식 1κ³Ό 2 정리
    • $F_i = \frac{1}{\alpha-\beta}\alpha^{i-1}(1 - \beta)-\beta^{i-1}(1-\alpha)$
    • = $\frac{\alpha^i-\beta^i}{\alpha-\beta}$
  • $\alpha$와 $\beta$ κ΅¬ν•˜κΈ°
    • 근의 방정식 μ‚¬μš©
    • $\alpha=\frac{1+\sqrt{5}}{2}$
    • $\beta=\frac{1-\sqrt{5}}{2}$
  • λŒ€μž…ν•˜κΈ°
    • $F_i=\frac{1}{\sqrt{5}}(\frac{1+\sqrt{5}}{2})^n-(\frac{1-\sqrt{5}}{2})^n$
def fibo(n):
    sqrt_5 = 5 ** (1/2)
    result = 1 / sqrt_5 * ( ((1 + sqrt_5) / 2) ** n  - ((1 - sqrt_5) / 2) ** n )
    return int(result)

num = int(input())
print(fibo(num))
  • $n=72$λΆ€ν„° μΌλ°˜ν•­ 연산이 1 더 큼
  • μˆ˜μ‹ νŠΉμ„±μƒ $n$이 컀지면 컀질수둝 μ˜€μ°¨λ²”μœ„κ°€ λŠ˜μ–΄λ‚¨
  • μ‹œκ°„λ³΅μž‘λ„λ‘œ 따지면 효율적인 μ½”λ“œκ°€ λ§žμœΌλ‚˜ μ •ν™•λ„λ‘œ 따지면 μ“Έ μ΄μœ κ°€ μ—†λŠ” μ½”λ“œ
  • 1100 κ°’ ν• λ‹Ή -> 431 ns ± 0.86 ns
  • ν•΄λ‹Ή λ°©λ²•μ˜ μ‹œκ°„λ³΅μž‘λ„λŠ” $O(log_n)$
728x90
λ°˜μ‘ν˜•

'Development Study > Python' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[Python] Generator  (0) 2022.11.30
[Python] Iterator  (0) 2022.11.28
[Python] requests와 urllib  (0) 2022.11.18
[Algorithm] Baekjoon - 반볡문 단계  (0) 2022.10.12
[Python] 파이썬의 λ¬Έμžμ—΄ ν¬λ©”νŒ… Ver.2  (0) 2022.10.06
Comments