Project Euler Problem 16
問題16: 2^1000 の各桁の数字を合計した値を求めよ。[=>問題文]
bigint ライブラリの改良
かなりヘビーな計算量になりそうなので、問題13で作った多倍長整数ライブラリの改良と、機能追加をします。改良点は2つ。まず、多倍長数をあらわすリストの要素を0〜9の1桁から、0〜9999の4桁に変更。計算速度が4倍になります。もう一つの改良は加算関数 bigint+ の効率化。以前のものは桁数の大きく異なる2数の和計算でとても非効率でした。
追加機能は、乗算と累乗計算。ここまで作れば問題は解けたも同然です。
(defun bigint-expt (n m) "累乗計算。n:bigint, m:0以上の整数" (let ((result '(1)) (i 0) (cnt 0)) (while (zerop (/ m 2)) (incf cnt) (setq m (/ m 2))) (while (<= 0 (decf m)) (setq result (bigint* result n))) (while (<= 0 (decf cnt)) (setq result (bigint* result result))) result))
累乗計算は、最初に指数 m を割れなくなるまで 2 で割ってその回数をカウントしてます。残りの m の回数だけ n を繰り返し積算したその結果を、最初に2で割った回数だけ二乗すれば答えがでます。これは掛け算の回数を減らす工夫ですが、パフォーマンスアップはさほどでもありませんでした。
※多倍長演算ライブラリの全コードはこちらへ移動しました。
問題16の解答
(defun problem016 () (let ((str (bigint-to-string (bigint-expt (string-to-bigint "2") 1000)))) (apply #'+ (mapcar #'(lambda (c) (- c ?0)) str))))
処理にかかった時間は 0.035 秒。なかなかのもんじゃないでしょうか。