LaTeXでグラフ描画

rubyco2006-04-11

数学ガール最新作、テトラちゃんとハーモニック・ナンバーに出てきた、反比例・調和数・対数関数のグラフを描くLaTeXのソースを生成するRubyスクリプトを作ります。
まずは、p.21, p.22に出てきた反比例と調和数のグラフ(make_invx_graph.rb)。

#!/usr/local/bin/ruby -Ks

def output(lower)
  scale = 1
  width = 15
  height = 80.0
  limit = 7.0

  puts '% y = 1/x'
  print <<'EOD'
\begin{figure}[h]
\begin{center}
\unitlength=1mm
\begin{picture}(100,100)(0,0)
\thinlines
\put(0,0){\vector(1,0){100}}\put(101,0){$x$}
\put(0,0){\vector(0,1){100}}\put(0,101){$y$}
EOD

  puts '% curve : 1/x'
  x = 1.0
  index = 0
  while x < limit
    if !lower && (x + 1.0) >= limit
      break;
    end
    px = format("%.2f", if lower then (x - 1) * width else x * width end)
    py = format("%.2f", height * (1 / x))
    if index % scale == 0
      print "\\put(#{px},#{py}){\\circle*{1.0}}"
      print "\n"
    end
    index += 1
    x += 0.1
  end

  puts '% rectangle : 1/x'
  x = 1.0
  y = 0.0
  index = 0
  print "\\put(#{0-1},#{-5}){$#{0}$}"
  while x < limit
    y = 1 / x
    px = format("%.2f", x * width)
    py = format("%.2f", y * height)
    px_ = format("%.2f", (x-1) * width)
    # 数値
    sy = format("\\frac{1}{%d}", x)
    psx = format("%.2f", x * width + 1.0)
    psy = format("%.2f", y * height + 3.0)
    if index % scale == 0
      print "\\put(#{px_},#{py}){\\line(1,0){#{width}}}"
      print "\\put(#{px},#{py}){\\circle*{1}}"
      print "\\put(#{px},0){\\line(0,1){#{py}}}"
      print "\\put(#{psx},#{psy}){$#{sy}$}"
      print "\\put(#{x * width - 1},#{-5}){$#{x.to_i}$}"
      print "\n"
    end
    index += 1
    x += 1.0
  end
  print <<"EOD"
\\end{picture}
\\end{center}
\\caption{#{if lower then "lower" else "upper" end}}
\\end{figure}
\\newpage
EOD
end

# Preamble.
puts <<'EOD'
\documentclass{jarticle}
\usepackage{euler}
\begin{document}
EOD

output(false)
output(true)

# Postamble.
print <<'EOD'
\end{document}
EOD

それから、p.23, p.24, p.25に出てきた対数関数と調和数のグラフ(make_log_graph.rb)。

#!/usr/local/bin/ruby -Ks

def output(scale)
  width = 15.0 / scale.to_f
  height = 40.0 / (1 + Math.log(scale.to_f) / 2.0)
  limit = 6.5 * scale.to_f

  STDERR.puts "scale = #{scale}"
  puts "\\section*{scale = #{scale}}"
  print <<'EOD'
% y = log(x + 1)
\begin{figure}[h]
\begin{center}
\unitlength=1mm
\begin{picture}(100,100)(0,0)
\thinlines
\put(0,0){\vector(1,0){100}}\put(101,0){$x$}
\put(0,0){\vector(0,1){100}}\put(0,101){$y$}
EOD

  puts '% curve : log(x + 1)'
  x = 0.0
  index = 0
  while x < limit
    px = format("%.2f", x * width)
    py = format("%.2f", height * Math.log(x + 1.0))
    if index % scale == 0
      puts "\\put(#{px},#{py}){\\circle*{1.0}}"
    end
    index += 1
    x += 0.05
  end

  puts '% rectangle : sum 1/x'
  x = 1.0
  y = 0.0
  index = 0
  puts "\\put(#{0-1},#{-5}){$#{0}$}" if scale == 1
  while x < limit
    y += 1 / x
    px = format("%.2f", x * width)
    py = format("%.2f", y * height)
    px_ = format("%.2f", (x-1) * width)
    # numeric value
    sy = format("%.4f", y)
    psx = format("%.2f", x * width + 1.0)
    psy = format("%.2f", y * height + 8.0)
    # diff
    sdy = format("%.4f", y - Math.log(x + 1.0))
    pdx = format("%.2f", x * width + 1.0)
    pdy = format("%.2f", y * height + 4.0)
    if index % scale == 0
      print "\\put(#{px},#{py}){\\circle*{1}}"
      print "\\put(#{px},0){\\line(0,1){#{py}}}"
      print "\\put(#{psx},#{psy}){$#{sy}$}"
      print "\\put(#{pdx},#{pdy}){($#{sdy}$)}"
      print "\\put(#{x * width - 1},#{-5}){$#{x.to_i}$}"
      print "\n"
    end
    index += 1
    x += 1.0
  end

  print <<"EOD"
\\end{picture}
\\end{center}
\\caption{scale = #{scale}}
\\end{figure}
\\newpage
EOD
end

# Preamble.
puts <<'EOD'
\documentclass{jarticle}
\usepackage{euler}
\begin{document}
EOD

[1, 1000, 10000].each do |scale|
  output(scale)
end

# Postamble.
print <<'EOD'
\end{document}
EOD

これらから実際にグラフを生成するには、たとえば以下のようにします。RubyLaTeXがインストールされている必要があります。

ruby make_invx_graph.rb > 1.tex
platex 1
dvipdfm 1
ruby make_log_graph.rb > 2.tex
platex 2
dvipdfm 2

これを実行すると、1.pdf, 2.pdfというPDFファイルができます。
注意:ここではpicture環境を使って、ある意味「無理矢理」グラフを描いています。今回は時間がなかったので自分で作ってしまいましたが、本来は、LaTeX関連でグラフを作るための適切なパッケージやツールを探して使うべきでしょう。