clispで円周率

■BBPとは?

 BBPは円周率の小数点以下の任意の桁の値を求める事が出来るアルゴリズムです。

 参考:BBPアルゴリズム
 http://www14.ocn.ne.jp/~kk62526/pi/BBP.html

 参考:円周率の望みの桁を直接計算する法
 http://d.hatena.ne.jp/rikunora/20110627/p1

 参考:円周率の任意の桁を一発で計算する
 http://www.riverplus.net/sci/2005/05/post_83.html

 参考:π計算プログラム 「スーパーπ Ver 1.1」
 http://www1.coralnet.or.jp/kusuto/PI/super_pi.html

■以下にCLISPのコードがあった。

 ※BBPのメリットを無視するようだが、
  単純に円周率を毎回1から計算する方針で進める。

 参考:GNU/Linux上で円周率の計算をおこなう
 http://h2np.net/pi/

■しかしCLISPには組み込みの「pi」関数がある

$ echo "pi" | clisp -q | tail -1
3.1415926535897932385L0

■というわけでmypiに変更

(setf (EXT:LONG-FLOAT-DIGITS) 330)
 (defun mypi ()
  (defun s(k)
  (* (/ 1.0L0 (expt 16.0L0 k))
   (- (/ 4.0L0 (+ 1.0L0(* 8.0L0 k)))
    (+ (/ 2.0L0 (+ 4.0L0 (* 8.0L0 k)))
     (+  (/ 1.0L0 (+ 5.0L0 (* 8.0L0 k)))
      (/ 1.0L0 (+ 6.0L0 (* 8.0L0 k))))))))
  (defun m(n)
    (if (> n 90L0)  0.0L0
        (+ (m (+ n 1.0L0))
         (s n))))
  (m 0.0L0))

■時間を計る時には、「-o」オプションが使えなかったので、リダイレクトする。

$ time (echo "(mypi)" | clisp -q -i bbp.lisp > bbp.log)
real    0m0.115s
user    0m0.008s
sys     0m0.040s

■小数点以下100桁を取り出します。

$ tail -1 bbp.log | awk -F\. '{print $2}' | awk -F L '{print $1}' | wc -c
107

$ echo "(mypi)" | clisp -q -i bbp.lisp | tail -1 | \
   cut -c 3-102 | sed s/"."/"&\n"/g | \
   awk '{if (NR % 10 == 0) print $1 ":"; else print $1}' | \
   xargs echo -n | sed s/" "//g | sed s/":"/"\n"/g;echo ""
1415926535
8979323846
2643383279
5028841971
6939937510
5820974944
5923078164
0628620899
8628034825
3421170679

■有効桁数については、「EXT:LONG-FLOAT-DIGITS」の
 フローティングポイントを変更すればよいので、以下のようにします。

$ cat bbp.lisp | sed s/"330"/"1000"/ > bbp1.lisp
$ cat bbp.lisp | sed s/"330"/"10000"/ > bbp2.lisp
$ cat bbp.lisp | sed s/"330"/"100000"/ > bbp3.lisp
$ cat bbp.lisp | sed s/"330"/"1000000"/ > bbp4.lisp
$ cat bbp.lisp | sed s/"330"/"3300000"/ > bbp5.lisp

■毎回以下のようにするのは面倒。

$ cat bbp3.log | awk -F\. '{print $2}' | awk -F\L '{print $1}' | tail -1 | wc -c
309

■以下で充分。

$ tail -1 bbp1.log | wc -c | awk '{print $1-($1%100)}'
300

■ループでbbp3.lispまで計算してみる。

$ for list in bbp[1-3].lisp;do \
     LOG=$(echo $list | sed s/"\.lisp"/"\.log"/); \
     time (echo "(mypi)" | clisp -q -i $list > $LOG); \
  done

real    0m0.155s
user    0m0.012s
sys     0m0.052s

real    0m1.183s
user    0m0.308s
sys     0m0.124s

real    3m7.682s
user    1m5.108s
sys     0m2.260s


■bbp4.lispは約2日かかった。bbp5.lispは現在も実行中。

$ ps -ef | grep bbp5.lisp | grep -v grep | awk '{print $7}'
3-23:41:21

■有効桁数を見てみる。
 ※最近のCentOSでは、「tail -1」が通らない。
  その場合は「tail -n 1」とする必要がある。

$ for list in bbp[1-3].log;do \
     tail -1 "$list" | wc -c | awk '{print $1-($1%100)}'; \
   done
300
3000
30100

■bbp4.logの有効桁数は以下の通り、30万桁。

$ tail -1 bbp5.log | wc -c | awk '{print $1-($1%100)}'
301000

■bbp5.logは、100万桁の予定。
 以下の通りCPU使用率は高いがメモリは余り使っていない。

$ top -b -n 1 | head -7 | tail -1;top -b -n 1 | grep lisp
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 9183 labunix   20   0  113m  69m 2424 R 99.9 13.8   5749:54 lisp.run

■以下の通りメモリ512MBしか割り当てていない仮想マシン

$ free
             total       used       free     shared    buffers     cached
Mem:        511004     452172      58832          0     118972     109420
-/+ buffers/cache:     223780     287224
Swap:      1421712       8308    1413404

■以下の通りに変換。
 ※「5.」で改行とスペースを消すので、
  それぞれ別の記号に前変換してエスケープしている。

 1.最終行を取り出し
 2.「3.1」を除いた301千桁の取り出し、すべての文字の後ろに改行を挿入⇒列にする
 3.100列ごとに「%」を挿入、それ以外の10列ごとに「:」を挿入
 5.「\n」を取り除き、余分なスペースを削除
 6.「:」をスペースに、「%」を改行に変換

$ tail -1 bbp4.log | \
   cut -c 3-301002 | sed s/"."/"&\n"/g | \
   awk  '{if (NR % 100 == 0) print $1 "%"; else if (NR % 10 == 0) print $1 ":"; else print $1}' | \
   xargs echo -n | sed s/" "//g | \
   sed s/":"/" "/g | sed s/"%"/"\n"/g > bbp4.txt

■10桁区切り、10回が一行で、「wc -l」で行数を掛ける

$ wc -l bbp4.txt | awk '{print 10*10*$1}'
301000

■ワード単位(英文のスペース区切りの意味)1ワード=(10)として数える。

$ wc -w bbp4.txt | awk '{print 10*$1}'
301000

■サイズは328k

$ du -h bbp4.txt
328K    bbp4.txt

■載せようとしたら文字数オーバーでした。。。

$ cat bbp4.log

■計算したpiをチェックする
 ※参考1のダウンロードサイトはサービスが終了しておりダウンロード出来ない。

 参考1:π1000万桁のDownload
 http://www1.coralnet.or.jp/kusuto/PI/PI-data/index.html

 参考2:円周率 1,000,000 桁
 http://www.kisaragiweb.jp/pi/pi1m.htm

■チェックしたが、違うらしい。。。

$ for num in `seq 1 4`;do \
     echo -n "bbp$num :" ; \
     diff bbp${num}.txt check${num}.txt | head -1; \
   done
bbp1 :4d3
bbp2 :31d30
bbp3 :302d301
bbp4 :2,3010c2,3010

■チェックファイルは以下のようにして保存。
 ※一行で出来る。

$ w3m -dump -cols 110 http://www.kisaragiweb.jp/pi/pi1m.htm | \
   grep "^[0-9]" | \
   sed s/" "/"\n"/g > check.txt

■100万桁あることを確認。

$ wc -l check.txt | awk '{print 10*$1}'
100000010桁づつ出力

$ for list in bbp[1-4].log;do
  TXT=$(echo $list | sed s/"\.log"/"\.txt"/); \
  tail -n 1 $list | \
    awk -F\. '{print $2}' | awk -F L '{print $1}' | sed s/"."/"&\n"/g | \
    awk  '{if (NR % 10 == 0) print $1 ":"; else print $1}' | \
    xargs echo -n | sed s/" "//g | \
    sed s/":"/"\n"/g > $TXT
done

■check用のファイル切り出し。

$ for list in bbp*.txt;do \
     LINE=$(wc -l $list | awk '{print $1}') ; \
     CHECKLOG=$(echo $list | sed s/"\.txt"/"\.check"/); \
     head -n $LINE check.txt > $CHECKLOG; \
   done

■lisp自体の再実行に時間をかけるのもアレなので、コンパイルしよう。

$ for list in bbp[1-4].lisp;do clisp -q -c "$list";done | head -3
;; Compiling file ~/bbp1.lisp ...
;; Wrote file ~/bbp1.fas
0 errors, 0 warnings

■ついでに性能の高いシステム側で再実行
 ※仮想マシンで2日かかった処理が8分で終わった。。。

$ for list in bbp[1-4].fas;do \
     LOG=$(echo $list | sed s/"\.fas"/"\.log"/); \
     echo "(mypi)" | clisp -q -i "$list" > $LOG; \
   done

■再確認したが、やはり違うようだ。

$ for num in `seq 1 4`;do \
     echo -n "bbp${num} : "; \
     head -n 1 "bbp${num}.lisp" | awk '{print $3}' | sed s/")"//g | xargs echo -n; \
     echo -n " : "; \
      diff bbp${num}.txt check${num}.txt | head -1; \  
   done
bbp1 : 1000 : 4d3
bbp2 : 10000 : 31d30
bbp3 : 100000 : 302d301
bbp4 : 1000000 : 2,3010c2,3010100桁まで有効と書いてある。。。

 参考:clispで円周率計算のプログラムを書いてみた
 http://h2np.net/tips/wiki/index.php?clisp%A4%C7%B1%DF%BC%FE%CE%A8

■なるほど、確かに、すべて同じ間違いをしています。

$ for num in `seq 1 4`;do \
     diff -y -W 80 bbp${num}.txt bbp${num}.check | \
     nl -n rz | grep "|" | head -n 1; \
   done
000012  3281510877                            | 3282306647
000012  3281510877                            | 3282306647
000012  3281510877                            | 3282306647

■まとめ。

今回は時間の無駄だったということで。。。