LoginSignup
2
0

More than 3 years have passed since last update.

初学者によるプログラミングMemo #18 フィボナッチ数列(メモ化)

Last updated at Posted at 2020-01-22

はじめに

今回はフィボナッチ数列のお話です
なお、本記述はMacにおいて、Railsでの開発を前提としています
また、まだまだひよっこですので、不備等ございましたらご指摘いただけると幸いです
*追記
コメント欄に簡潔なコードがありますので、そちらもご覧ください
元の記述は自分が考えた軌跡として残しておきます

目次

  • フィボナッチ数列とは
  • プログラムで解いてみる

フィボナッチ数列とは

初項 = 1、第2項 = 1、第3項以降は、前項と前々項の和となる数列です
第3項は初項と第2項の和なので「1+1」で"2"となります
第4項は第2項と第3項の和なので"3"となります
また、フィボナッチ数列は黄金比とも関係してるので、知りたい方は調べてみてください
数列としては、「1,1,2,3,5,8,13,21,34,55,・・・」と続きます

この数列の第n項目は何になるかを求めたいと思います

プログラムで解いてみる

では、プログラムで解いてみましょう
まずは、フィボナッチ数列がどういうものだったかを思い出しましょう

fib(n) = fib(n-2) + fib(n-1)

第n項は前(n-1)項と前々(n-2)項の和でしたよね
しかし、初項と第2項は決まっています
付け加えましょう

fib(n) = fib(n-2) + fib(n-1)
fib(0) = 1
fib(1) = 1

できました
このままではプログラムとしては成り立っていませんので、組み立てましょう

def fib(n)
  if n == 0 || n == 1
    return 1
  else
    fib(n-2) + fib(n-1)
  end
end

できました

しかし、このプログラムには欠点があります
fib(4)で解説しましょう

fib(4) = fib(3) + fib(2)

ですね
でも、fib(3)とfib(2)も求めないといけないですよね
そうすると

fib(4) = fib(3) + fib(2)
fib(3) = fib(2) + fib(1)
fib(2) = fib(1) + fib(0)

こうなりますが、fib(3)の中にもfib(2)が入っていますね
そうするとこうなります

fib(4) = fib(3) + fib(2)
  fib(3) = fib(2) + fib(1)  #fib(4)の中のfib(3)を求める式
    fib(2) = fib(1) + fib(0)  #fib(3)の中のfib(2)を求める式
  fib(2) = fib(1) + fib(0)  #fib(4)の中のfib(2)を求める式

ここで違和感というか、無駄が発生していることにお気づきでしょうか
fib(2)は2回、fib(1)とfib(0)は3回出てきています
nが小さい時はあまり問題がありませんが、n=30くらいになると、答えが返ってくるまでに時間がかかります
n=35で2秒ほどかかります
n=100では計算が終わらないようですので、実行はしないようにしましょう

メモ化を使う

問題が発見されたので、変更しないといけませんね
使えないこともないコードならまだしも、n=100程度でギブアップするなんて使えないコードです
そこで、メモ化というものを使います
配列に入れてしまいましょう
メモ化の特徴としては、記憶領域と引き換えに、時間のコストを削減するというものがあります
いわゆるトレードオフをする必要があるということです

F = []
def fib(n)
  if n == 0 || n == 1
    return 1
  elsif F[n] != nil #ここだけのプログラムであれば不要(関与しない)
    return F[n]
  else
    F[n] = fib(n - 2) + fib(n - 1)
  end
end

できました
変更点は、配列をつくって、それに入れている部分です
しかしこれでは数学的には間違っているので、対話型のプログラムにしつつ正しくしましょう

F = []
def fib(n)
  if n == 0 || n == 1
    return 1
  elsif F[n] != nil 
    return F[n]
  else
    F[n] = fib(n - 2) + fib(n - 1)
  end
end

p "フィボナッチ数列の何項目が知りたいん?うちが教えたるで"

num = gets.to_i

if num >= 1
  p fib(num-1)
else
  p "ちゃんと入力しやん人はいややぁ"
end

できました
数学的な一般項はなかなか複雑ですが、プログラム上での一般項(?)はわかりやすくていいですね

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0