Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
フィボナッチ数列の作り方
2013/2/2
cuzic
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
自己紹介
最近のできごと
Dell XPS 12 を買いました。
不思議なギミックでタブレットになる
Full HD、Touch Panel、Display Portポー...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
22フィボナッチ数列について
次の漸化式を満たす数列をフィボナッチ数列という
フィボナッチ数列の特徴
うさぎの増え方を表現する漸化式
𝐹𝑛−1
𝐹𝑛
が 黄金比(
5−1...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
単純な実装 33
a = 0; b = 1
loop do
a, b = b, a + b
break if 100 < a
puts a
end
■ (1) もっとも単...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
ブロック付メソッド 44
■ (4) ブロック付メソッド呼び出し ■ 処理の実施順
yield キーワードにより、ブロック引数に値を渡せる。
ブロック付メソッド呼出しによ...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
クラスの利用 55
■ (5) クラスの利用
クラスを利用し、内部状態をインスタンス変数に持たせる方法もある
この場合は、next を実行したときに内部状態が変化する。
...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
クロージャ 66
■ (6) クロージャの利用
クロージャという機能により、ブロックの中から
ネストの外側の変数を参照できる。
内部状態を持つ関数を簡易に作ることができる...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
Ruby 1.8: Generator の利用 77
■ (7) Generator の利用
Ruby 1.8 には、標準ライブラリに Generator があった
内部...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
Ruby 1.9: Enumerator の利用 88
■ (8) Enumerator の利用
Ruby 1.9 では Enumerator を利用可能
内部実装に F...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
(参考) Enumerator と Fiber 99
e = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b ...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
Enumerator はベンリ 1010
fib = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b = b,...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
Enumerable#lazy の利用 1111
fib = Enumerator.new do |yielder|
a = 0; b = 1
loop do
a, b ...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
(参考)Enumerable#lazy の実装(Ruby 2.0 での実際の実装とは異なる) 1212
module Enumerable
def lazy
Enumer...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
単純な再帰と末尾再帰
フィボナッチ数列を再帰で求めてみる
関数型プログラミング言語の入門には、ほぼ必ず登場する例
適度な難易度で説明しやすい
末尾再帰による高速化、遅延評...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
再帰と memoize
memoize という高速化手法がある
最初の一度だけ計算し、2回目以降は前回計算済みの値を返す
メモリを多く消費するが、計算時間を短縮できる
同...
2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」
1515まとめ
単純な問題だけど、いろんな解法がある
ループ、ブロック付メソッド、クラス、クロージャ、
Enumerator、再帰
個人的オススメは、Enumerator...
1616
ご清聴ありがとう
ございました
Upcoming SlideShare
Loading in …5
×

フィボナッチ数列の作り方

  • Be the first to comment

フィボナッチ数列の作り方

  1. 1. フィボナッチ数列の作り方 2013/2/2 cuzic
  2. 2. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 自己紹介 最近のできごと Dell XPS 12 を買いました。 不思議なギミックでタブレットになる Full HD、Touch Panel、Display Portポート Office 365 Home Premium に契約しました 毎月 $9.99 で Office の最新版を利用可能 パワポ、エクセル、ワードを最大5台にインストール可能 (2013年2月2日現在)日本では、まだサービス開始していません。。。 今後の勉強会 プログラマのためのサバイバルマニュアル読書会 第1回 3月2日 JR尼崎駅 徒歩2分 小田公民館で開催 11
  3. 3. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 22フィボナッチ数列について 次の漸化式を満たす数列をフィボナッチ数列という フィボナッチ数列の特徴 うさぎの増え方を表現する漸化式 𝐹𝑛−1 𝐹𝑛 が 黄金比( 5−1 2 = 0.683 …) に収束する プログラミングにおけるフィボナッチ数列 関数型プログラミング言語における Hello, World! 入門向けに使える程度の複雑さ 切り口によって、いろんな説明ができる 今回は、フィボナッチ数列の求め方をいろいろ説明します。 𝑭 𝒏 = 𝟎 𝟏 𝑭 𝒏−𝟏 + 𝑭 𝒏−𝟐 𝒏 = 𝟎 𝒏 = 𝟏 𝒏 ≥ 𝟐
  4. 4. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 単純な実装 33 a = 0; b = 1 loop do a, b = b, a + b break if 100 < a puts a end ■ (1) もっとも単純な実装 ■ (2) 多重代入の利用 もっとも単純なフィボナッチ数列の Ruby による実装 ポイント: ループ、多重代入、while の構文 a = 0; b = 1 loop do tmp = a a = b b = tmp + b break if 100 < a puts a end a = 0; b = 1 while a < 100 puts b a, b = b, a + b end ■ (3) while の利用
  5. 5. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 ブロック付メソッド 44 ■ (4) ブロック付メソッド呼び出し ■ 処理の実施順 yield キーワードにより、ブロック引数に値を渡せる。 ブロック付メソッド呼出しにより、 値の生成と、値の利用の処理を分離できる def fibonacci a = 0; b = 1 loop do a, b = b, a + b yield a end end n = 1 fibonacci do |i| break if 100 < a puts “#{n} #{i}" n += 1 end ① ② ③ ⑤ ⑦ ⑤ → ① → ② → ⑦ → ③ → ② → ⑦ → ③ → ② → ⑦ → ③ ・・・ (以下おなじ)
  6. 6. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 クラスの利用 55 ■ (5) クラスの利用 クラスを利用し、内部状態をインスタンス変数に持たせる方法もある この場合は、next を実行したときに内部状態が変化する。 class Fibonacci def initialize @a = 0; @b = 1 end def next @a, @b = @b, @a + @b return @a end end fibonacci = Fibonacci.new loop do i = fibonacci.next() break if i > 100 puts i end インスタンス変数の初期化インスタンス変数の初期化 次の値の生成次の値の生成
  7. 7. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 クロージャ 66 ■ (6) クロージャの利用 クロージャという機能により、ブロックの中から ネストの外側の変数を参照できる。 内部状態を持つ関数を簡易に作ることができる。 fibonacci = proc do a = 0; b = 1 lambda do a, b = b, a + b return a end end.call() loop do i = fibonacci.() break if i > 100 puts i end スコープを導入するためのクロージャスコープを導入するためのクロージャ 外側のクロージャを呼ぶ出すための Proc#call外側のクロージャを呼ぶ出すための Proc#call 内側のクロージャを作るための lambda 中で return を使いたいので 「 lambda 」にしている。 内側のクロージャを作るための lambda 中で return を使いたいので 「 lambda 」にしている。 クロージャを呼び出し、Fibonacci 数列の次の値を取得するクロージャを呼び出し、Fibonacci 数列の次の値を取得する
  8. 8. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 Ruby 1.8: Generator の利用 77 ■ (7) Generator の利用 Ruby 1.8 には、標準ライブラリに Generator があった 内部実装に callcc を使うので、ちょいと遅い require 'generator' g = Generator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder.yield a end end g.each do |i| break if i > 100 puts i end Generator が生成する項を順に取得するGenerator が生成する項を順に取得する 次の値を生成する次の値を生成する
  9. 9. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 Ruby 1.9: Enumerator の利用 88 ■ (8) Enumerator の利用 Ruby 1.9 では Enumerator を利用可能 内部実装に Fiber を使うため、高速 Enumerable モジュールのメソッド(each_cons など)を利用可能 e = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end e.each do |i| break if i > 100 puts i end Enumerator が生成する項を順に取得するEnumerator が生成する項を順に取得する 次の生成する値を yield する次の生成する値を yield する Enumerator による実装は、 利用側のコード行数がコンパクト になる。
  10. 10. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 (参考) Enumerator と Fiber 99 e = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end e.each do |i| break if 100 < i puts i end ■ (8) 《再掲》 Enumerator の例 fib = Fiber.new do a = 0; b = 1 loop do a, b = b, a + b Fiber.yield a end end def fib.each loop do yield self.resume end end fib.each do |i| break if 100 > i puts i end ■ 《参考》 (9) Fiberの例 Enumerator は Fiber を活用するデザインパターンの1つ Fiber における難しい概念を隠ぺい Fiber.yield => Enumerator::Yielder#<< 、 Enumerator#each による列挙 Enumerator を使いこなせれば、十分 Fiber のメリットを活用できる 《参考: Fiber にできて、Enumerator でできないこと》 親 Fiber から 子Fiber へのデータの送付(Fiber.yield の返り値の利用) これが必要。 だけど、これが 難しい。(汗) Enumerator の 内部実装まで 考えなくていい
  11. 11. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 Enumerator はベンリ 1010 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.each do |i| break if 100 < I puts i end fib.each_cons(2) do |i, j| break if 100 < i puts "#{i} #{j}" end ■ (10) Enumerator の例 def fibonacci a = 0; b = 1 loop do a, b = b, a + b yield a end end fibonacci do |i| break if 100 < i puts i end prev = nil fibonacci do |i| break if 100 < i puts "#{prev} #{i}" if prev prev = i end ■ 《参考》 (11) ブロック付メソッド呼び出しの例 Enumerator なら Enumerable の便利メソッドを使える。 ブロック付メソッド呼び出しは単純なイテレーションならいいけど。。。 Enumerator はオブジェクトなので、使い回しが簡単。 引数にしたり、返り値にしたり、インスタンス変数に格納したり Enumerator オブジェクトの生成も慣れれば簡単。 慣れれば、 カンタン 面倒 ! ! Enumerable の便利メソッド を使い放題 単純なイテレーション ならいいけど・・・。
  12. 12. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 Enumerable#lazy の利用 1111 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.select do |i| i < 100 end. take(10).each do |i| puts i end ■ (12) うまく動かない例 fib = Enumerator.new do |yielder| a = 0; b = 1 loop do a, b = b, a + b yielder << a end end fib.lazy.select do |i| i < 100 end.take(10).each do |i| puts i end ■ (13) Enumerable#lazy を利用する例 Ruby 2.0.0 から、Enumerable#lazy が利用可能となる Enumerable#lazy を使うと、遅延評価になり、無限を無限のまま扱える 宇宙ヤバい Ruby 1.9 でも gem でインストール可能 @yhara さんの作品 ここで、無限ループに なり、うまく動かない。 ※ take_while を 使う方法もある。 Enumerable#lazy を使えば、正しく 期待通りに動作する
  13. 13. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 (参考)Enumerable#lazy の実装(Ruby 2.0 での実際の実装とは異なる) 1212 module Enumerable def lazy Enumerator::Lazy.new(self) end end class Enumerator class Lazy < Enumerator def initialize(obj, &block) super(){|yielder| begin obj.each{|x| if block block.call(yielder, x) else yielder << x end } rescue StopIteration end } end def select(&block) Lazy.new(self){|yielder, val| if block.call(val) yielder << val end } end def take(n) taken = 0 Lazy.new(self){|yielder, val| if taken < n yielder << val taken += 1 else raise StopIteration end } end …… end Enumerable#lazy を使うと、Enumerable のメソッドの返り値が Enumerator::Lazy オブジェクトになる Enumerable モジュールに増やすメソッドは Enumerable#lazy だけ
  14. 14. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 単純な再帰と末尾再帰 フィボナッチ数列を再帰で求めてみる 関数型プログラミング言語の入門には、ほぼ必ず登場する例 適度な難易度で説明しやすい 末尾再帰による高速化、遅延評価とかを説明できる 1313 # めちゃくちゃ速い : O(n) の速さ def fib n, a = 0, b = 1 return a if n == 0 return fib(n-1, b, a+b) end # def fib_nonrecursive n # a = 0; b = 1 # loop do # return a if n == 0 # n, a, b = n-1, b, a+b # end # end (0..30).each do |n| puts fib(n) end # めちゃくちゃ遅い (指数関数オーダ) def fib n return n if n <= 1 return fib(n-1) + fib(n-2) end (0..30).each do |n| puts fib(n) end ■ (14) 単純な再帰、n項を取得 ■ (15) 再帰の例(高速版) 末尾再帰のコードと まったく同じ処理を 再帰を使わずに 書くとこうなる。
  15. 15. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 再帰と memoize memoize という高速化手法がある 最初の一度だけ計算し、2回目以降は前回計算済みの値を返す メモリを多く消費するが、計算時間を短縮できる 同じ計算を繰り返すときは、コストに見合う高速化効果が得られる 遅い再帰でも memoize することで、大幅に高速化できる 1414 # memoize版。同じ計算は1度だけ。圧倒的に速い。 def add a, b memo = nil lambda do return memo if memo memo = a.() + b.() return memo end end fib = Enumerator.new do |yielder| a = lambda{ 0 } b = lambda{ 1 } loop do a, b = b, add(a, b) yielder << a end end fib.take(36).each do |i| puts i.() end # memoize しない場合。同じ計算を何度もする。 # すごい遅い。 def add a, b lambda do return a.() + b.() end end fib = Enumerator.new do |yielder| a = lambda{ 0 } b = lambda{ 1 } loop do a, b = b, add(a, b) yielder << a end end fib.take(36).each do |i| puts i.() end ■ (16) memoize しない例 ■ (17) memoize する例
  16. 16. 2013/2/2 第56回Ruby勉強会 「フィボナッチ数列のつくり方」 1515まとめ 単純な問題だけど、いろんな解法がある ループ、ブロック付メソッド、クラス、クロージャ、 Enumerator、再帰 個人的オススメは、Enumerator による実装 利用側のコードがコンパクト Enumerable モジュールの便利メソッドが使える Enumerable#lazy とか 再帰はとくに実装がコンパクトになる ただし、注意しないと非常に遅い実装になることもある 遅いときも下記の指針に従うことで、高速化できる。 更新が必要な内部状態を引数に持ってくるように引数を見直す memoize を使って、計算を一度だけにする。
  17. 17. 1616 ご清聴ありがとう ございました

×