非常にコスト高(だと思ったけどそうでもなさそ)な素数チェック

K A N O U . J P さんところを見ていて(続きを読まずに)、791 が素数かどうかちょっと気になったので。

791 isPrime   "=> Unknown selector, please confirm, correct, or cancel "

とか、ありそうでない。w わからんでもないけど。

n
n _ 791. (Integer primesUpTo: n) includes: n

コスト無視とは言え、あれば最後に来ることが分かっていて「含むかどうか」を訊くのはちょっと気持ち悪い。

(Integer primesUpTo: n) last = n

でも、last のレシーバは #() かもしれない。

#() last   "=> Error: this collection is empty "

かといって、

({1}, (Integer primesUpTo: n)) last = n
([(Integer primesUpTo: n) last] ifError: [1]) = n

などと、逃げを打つのもいかがなものか。


はっ!(…わざとらしく) #() なのは n = 1 のときだけじゃないか…。<バカ?

(n > 1 ifTrue: [(Integer primesUpTo: n) last]) = n

でいいじゃん。もっとも、はなからメソッドにしたりする気はないので、前の方の、

(Integer primesUpTo: n) last = n

で、いいんですけどね…。orz


もちろん、Integer class >> #primesUpTo: などというマニアックなメソッドを探す手間かける前に、

((1 to: n) select: [: each | n \\ each = 0]) size = 2

とか、これじゃさすがにアレなんで、

((2 to: n // 2) select: [: each | n \\ each = 0]) isEmpty

って素直に書いちゃったほうが(因数が欲しいとかで つぶしが利くし)早いんですが。

ただ #select: の実装では、レシーバと同サイズ配列の領域をまず確保してしまうので、あまり大きな数には使えません。そういった意味でも #primesUpTo: は大きめの数に対処するなどちょっとインテリジェントなところがあるので、前のほうのでも言うほど悪くないのかも。


ところで、エラトステネスの篩いって Smalltalk とか Ruby のコレクションとイテレータRuby の場合はブロック付きメソッド)を使ってエレガントに書くとどうなるんでしょうかねぇ…。


もとい、そもそもエレガントに書けるものなんでしょうか(^_^;)。結局、コストが高くつきそうで…。こうなると、Cマガに載っていると言われるアルゴリズムも気になります。

篩の目を interval で表現して蓄積、チェックしたりしてみましたが、いまひとつ。

limit primeIntervals
limit _ (2 raisedTo: 7) - 1. primeIntervals _ OrderedCollection new. (2 to: limit) do: [: i | (primeIntervals anySatisfy: [: each | each includes: i ]) ifFalse: [primeIntervals add: (i to: limit by: i)]]. primeIntervals collect: [: each | each first]