nekoTheShadow’s diary

IT業界の片隅でひっそり生きるシステムエンジニアです(´・ω・`)

「ソモスの数列」(でいいのかな?)という数列があるそうです。

 いつもどおり(?)インターネットを漂流していると、somos sequence(以下「ソモスの数列」)なるものを見つけました。より詳しいことはここを参照してほしいのですが、わたしなりに簡単に要約してみると――4以上の整数kについて次のような漸化式/数列を考えます。

  • A1 = A2 = ... = Ak = 1
  • An * A(n - k) = (1..(k / 2).floor).sum{|j| A(n-j) * A(n-(k-j))} [n > k]

一見整数列には思えない数列ですが、実のところk=4,5,6,7において無限に整数が続くことが証明されているそうです。わたしはこれを証明することには興味がないというか、証明するだけの数学力を持ち合わせていませんので、今回は「ソモスの数列」を無限リストにして遊んでみたいと思います。

 さてやろうと決めて、思いついたのは以下の3選手。それぞれmake-somos-sequence1/2/3として実装しています。

 できました。ここには記しませんが、一応テストらしきこともやって3つとも正しく動作することは確かめています。よって次は性能の計測に移りたいと思います――と問題発生。端的にいうと単純な再帰のmake-somos-sequence1がまったく使いものになりませんでした……。わたしの環境では第30項ごときで結果が返ってきません。しょうがないのでmake-somos-sequence2と3のどちらが優秀かを競うこととします。

(time (stream->list (stream-take (make-somos-sequence2 7) 300)))
(time (stream->list (stream-take (make-somos-sequence3 7) 300)))

;(time (stream->list (stream-take (make-somos-sequence2 7) 300)))
; real   0.219
; user   0.219
; sys    0.000
;(time (stream->list (stream-take (make-somos-sequence3 7) 300)))
; real  10.232
; user  10.672
; sys    0.156

 結果はmake-somos-sequence2の圧勝でした。3はみずからを再帰的に呼び出しているのにくわえ、何本(?)もstreamを生成、高階関数に引き渡しているので、このあたりがボトルネックになったのではないかと想像します。やはり効率のいいschemeプログラミングをするにあたっては、できるだけ単純な再帰を避け、末尾再帰の可能性を探るということ大事になりそうで