素数とフィボナッチ数の出会いをGviz(Ruby Graphviz Wrapper)で描く
(追記:2014-3-3) Gvizについてのまとめ頁を作りました。
数字は社会生活における便利な道具の一つです。一方でそれはまた多くの人々を魅了して止まない迷宮の世界です。つまり数字は実用と神秘なる美しさとを同時に兼ね備えた概念なのです。
というわけで…
Gvizを使って今度は数字の美しさを表現してみようと思います。素数とフィボナッチ数を使います。
300までの数字を並べる
まずは単純に1〜300までの数字を平面的に並べてみます。Gvizで300個のノードを生成し、neato
レイアウトで描画します。
require "gviz"
max = 300
label = 'Numbers upto 300'
Graph do
global layout:'neato', label:label, fontsize:54, size:15
nodes shape:'circle', style:'filled'
(1..max).each { |i| node :"#{i}" }
save :number, :png
end
出力は次のようになりました。
カエルの卵みたいでちょっと気持ち悪いです。
素数列を取り出す
さて次に、ここから素数列のみを取り出し、それらのノードを順にエッジでリンクしてみます。
やはりグラフには色がないと寂しいので、素数列には青系の色を付けるようにしましょう。まずは色名をリスト(一行に一つの名前)で持ったテキストファイルcolors.txtを読み出して、青系色を抽出します。
File.open('colors.txt') do |f|
COLORS = f.read.lines.map(&:chomp).reject { |l| l.empty? }
end
def color_pick(regex)
COLORS.select { |c| c.match regex }
end
blues = color_pick(/(blue|cyan)\D*$/)
blues # => ["aliceblue", "blue", "blueviolet", "cadetblue", "cornflowerblue", "cyan", "darkslateblue", "deepskyblue", "dodgerblue", "lightblue", "lightcyan", "lightskyblue", "lightslateblue", "lightsteelblue", "mediumblue", "mediumslateblue", "midnightblue", "navyblue", "powderblue", "royalblue", "skyblue", "slateblue", "steelblue"]
色の準備ができたので、次に素数列を作ってroute
メソッドでエッジでリンクされたノード列を作ります。
require "prime"
max = 300
primes = Prime.each(max)
label = 'Prime Numbers upto 300'
Graph do
global layout:'neato', label:label, fontsize:54, size:15
nodes shape:'circle', style:'filled'
(1..max).each { |i| node :"#{i}" }
primes.each_cons(2) do |gr|
route Hash[*gr]
c = blues[rand blues.size]
node :"#{gr[1]}", fillcolor:c, fontcolor:'white'
end
save :number, :png
end
出力は次のようになりました。
素数列が300の数字から抽出されて、一列に並びました。
フィボナッチ数を取り出す
次に、一旦素数列を元に戻して、フィボナッチ数を抽出してみます。フィボナッチ数を生成するコードはなんでもいいですが、ここではEnumeratorを使って作ってみました。
class Fib
def self.create
Enumerator.new do |y|
a, b = 1, 1
loop do
y << a
a, b = b, a+b
end
end
end
end
max = 300
fibs = Fib.create.take_while { |i| i <= max*20 }
fibs # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
300までだとちょっと数が寂しいので、ここでは6000までのフィボナッチ数を抽出しています。
ノードの色を赤系として、フィボナッチ数を抽出します。
max = 300
fibs = Fib.create.take_while { |i| i <= max*20 }
reds = color_pick(/(red|pink|violet)\D*$/)
label = 'Fibonacci Numbers upto 6000'
Graph do
global layout:'neato', label:label, fontsize:54, size:15
nodes shape:'circle', style:'filled'
(1..max).each { |i| node :"#{i}" }
fibs.each_cons(2) do |gr|
route Hash[*gr]
c = reds[rand reds.size]
node :"#{gr[1]}", fillcolor:c, fontcolor:'white'
end
save :number, :png
end
出力は次のようになりました。
いいですね。
素数とフィボナッチ数の出会い
さて最後に素数とフィボナッチ数を重ねてみます。つまり先の2つの数列を同時に描画します。2つの数列にどんな出会いがあるのでしょうか。
label = 'Fibonacci Numbers upto 6000'
Graph do
global layout:'neato', label:label, fontsize:54, size:15
nodes shape:'circle', style:'filled'
(1..max).each { |i| node :"#{i}" }
primes.each_cons(2) do |gr|
route Hash[*gr]
c = blues[rand blues.size]
node :"#{gr[1]}", fillcolor:c, fontcolor:'white'
end
fibs.each_cons(2) do |gr|
route Hash[*gr]
c = reds[rand reds.size]
node :"#{gr[1]}", fillcolor:c, fontcolor:'white'
end
save :number, :png
end
結果は次のとおりです!
雰囲気が大分変わりました。数列はループを描いています。ちょっとリンクを追ってみます。
5
で別れた2つの数列は13
でまたすぐ出会います。それから素数列は下側に小さめのループを描く一方で、フィボナッチ数列は21
,34
,55
と直線的に進んで、両者は89
でまた出会います。それから素数列はまた長い旅に出て右上に大きめのループを描く一方で、フィボナッチ数列は144
の一つだけ進みます。そして両者は再び233
で出会うのです…
ロマンチックです。美しいです。
更に先の出会いへ
ここまで来ると彼らが次にどこで出会うのかが気になります…
max = 5000
として描画してみます。
結果は次のとおりです。
なんかどこかで見たような…不思議なかたち…
アダムとイヴは実は素数とフィボナッチ数だったとか!
それはさておき、3つ目のループの交点が見えますか?それが答えです!
さらにmax = 30000
としてみます。
出力は次のようになりました。図では確認できませんが、次の出会いは28657
でやって来ます。
数字の神秘をGvizで表現してみました。
Gviz sample: Meet Primes with Fibonaccies — Gist
関連記事:
Yet Another Ruby Graphviz Interfaceを作ったからみんなで大量のグラフを作って遊ぼうよ!
Ruby Graphvizラッパー「Gviz」でアメリカ合衆国をデータビジュアライズしよう!
東京の地下鉄をGviz(Ruby Graphviz Wrapper)で描く
(追記:2012-10-7)max=30000の出力を追加しました。
博士の愛した数式 (新潮文庫) by 小川 洋子
blog comments powered by Disqus