PE #005, #003, #007 in J

昨日 (id:flappphys:20080324#p2) の続きで,「20以下の自然数全てで割り切れる最小の自然数」.
せっかく再帰を覚えたのに,Jは末尾再帰をループに変換しないらしく,3000段くらいでstack errorになる.例えば1から順に数え上げるループで,第 i 番目の数の20以下の全ての自然数による剰余を求めて,そのorを取ればいずれは発見できる... というプログラム:

   ]`($:@>:) @. (+./@(*@((1+i.10)&|))) 1
2520
   ]`($:@>:) @. (+./@(*@((1+i.11)&|))) 1
|stack error
|       ]`($:@>:)@.(+./@(*@((1+i.11)&|)))1

そこでdo-whileループ相当のイディオムを覚えた:

   (>: ^: (+./@(*@((1+i.10)&|))) ^: _) 1
2520
   (>: ^: (+./@(*@((1+i.11)&|))) ^: _) 1
27720
   (>: ^: (+./@(*@((1+i.12)&|))) ^: _) 1
27720
   (>: ^: (+./@(*@((1+i.13)&|))) ^: _) 1
360360

これは「13以下の自然数全てで...」くらいの問題サイズで明らかに実行時間が辛くなってくる.仕方ないので除数を素因数分解して必要な因数を調べ,それらを掛け合わせる.Jには素数関連のルーチンが組み込まれているので楽勝:

   */@(p:@i. ^ >./@(q: >:@i.)) 20x
232792560

p: i は第 i 番目の素数を返し,m q: n は最初の m 個の素数を使って n を素因数分解した結果の指数の配列を返す.20x多倍長整数リテラル
#003と#007は上記のverbを使って瞬殺.素数関連には色々アルゴリズムがあるようで興味を惹かれるが,似たようなことは以前にも度々やってるし,見つけた素数の表の管理とかがうざいので無視.