Mae向きなブログ

Mae向きな日記のブログ版。ようやくこちらに移行してきました。

テイラー展開

数学ガール (数学ガールシリーズ 1)』のp231では「極限としての関数の姿」と題してsin関数のテイラー展開を扱っています。テイラー展開の無限級数を有限個だけ項を取り出して部分和を求めていくと、sin関数に近付いて行く様子が図を交えて分かりやすく解説されていますが、実際に試してみました。

学習のポイントは、

  • Proc

を使ってみるです。gen_taylor関数の中で使ってみました。使い方が正しいか分かりせんが…。
実行結果は、-4\pi\leq x \leq 4\piにおいて、テイラー展開を30項まで行ってみました。

次第にsin関数に近付いていく様子を観察することができました。

sin_curve.rb

require 'cairo'

class Graph
  FORMAT = Cairo::FORMAT_ARGB32
  attr_reader :x_max, :y_max
  def initialize(width, height, x_max, y_max)
    @width  = width
    @height = height
    @x_max = x_max
    @y_max = y_max
    create_graph
  end
  def create_graph
    @surface = Cairo::ImageSurface.new(FORMAT, @width, @height)
    @context = Cairo::Context.new(@surface)
    @context.set_source_rgb(1, 1, 1)
    @context.rectangle(0, 0, @width, @height)
    @context.fill
    draw_line(-@x_max, 0, @x_max, 0, [0.2, 0.2, 0.2]) # x軸
    draw_line(0, @y_max, 0, -@y_max, [0.2, 0.2, 0.2]) # y軸
  end
  def draw_line(x1, y1, x2, y2, rgb)
    x1 =  x1 * @width /@x_max/2.0 + @width /2.0
    y1 = -y1 * @height/@y_max/2.0 + @height/2.0
    x2 =  x2 * @width /@x_max/2.0 + @width /2.0
    y2 = -y2 * @height/@y_max/2.0 + @height/2.0
    @context.set_source_rgb(rgb)
    @context.move_to(x1, y1)
    @context.line_to(x2, y2)
    @context.stroke
  end
  def write_to_png(file_name)
    @surface.write_to_png(file_name)
  end
end

if __FILE__ == $0
  include Math
  def sin_curve(graph, from, to, step)
    rgb = [1, 0, 0]
    x  = from
    xx = x + step
    while x <= to
      graph.draw_line(x, sin(x), xx, sin(xx), rgb)
      x = xx
      xx += step
    end
  end

  def sin_curve_with_taylor(graph, from, to, step)
    1.step(30, 2) do |k|          # 30項まで
      taylor = gen_taylor(k)
      rgb = [rand, rand, rand]
      x  = from
      xx = x + step
      while x <= to
        graph.draw_line(x, taylor.call(x),
                        xx, taylor.call(xx), rgb)
        x = xx
        xx += step
      end
    end
  end

  # k項までのテイラー展開
  def gen_taylor(k)
    def fact(n)
      (1..n).inject(1){|p, i| p * i}
    end
    return Proc.new do |x|
      result = 0
      sign_flag = 0
      1.step(k, 2) do |j|
        result += ((-1.0)**sign_flag) * (x**j / fact(j))
        sign_flag = (sign_flag + 1) % 2
      end
      result
    end
  end

  graph = Graph.new(600, 300, 4*PI, 4)
  sin_curve_with_taylor(graph, -graph.x_max, graph.x_max, 0.2)
  sin_curve(graph, -graph.x_max, graph.x_max, 0.2)
  graph.write_to_png("sin_curve.png")
end