Clojure入門 - Project Eulerを解いてみる 問13
第13問
50桁の数字を100個足しあわせて、上10桁が何になるかを求める。
数字が大きすぎるので普通に足しあわせたりはできない。JavaのLongの最大値は9223372036854775807とのこと。
戦略としては以下のとおり。
- 上10桁を数字として扱って足しあわせる。
- 下40桁を、桁ごとに評価し足していって、繰上げを次の桁に足していく。
- 上10桁の和と、下40桁の繰上げを足しあわせる。
- その数字の上10桁を取る。
(def data (str/split-lines src)) (def first-10 (map #(subs % 0 10) data)) (def last-40 (map #(subs % 10) data))
まず、データが改行で区切られた文字列srcに入っているのを分割、上10桁と下40桁にわける。
(def first-10-sum (->> first-10 (map #(Long/parseLong % 10)) (apply +)))
とりあえず上10桁は足しあわせておく。
(defn convert-to-number-list [n] (->> (seq n) (map str) (map #(Long/parseLong % 10)))) (def digit-sum-list (->> last-40 (map convert-to-number-list) (apply map +)))
桁ごとの和を求める。
(defn add-roll-ups [a b] (+ b (int (Math/floor (/ a 10)))))
reduceに渡すために作った、引数a bをとって、aの繰上げの部分をbに足す関数。
(as-> digit-sum-list x (cons first-10-sum x) (reverse x) (reduce add-roll-ups x) (str x) (subs x 0 10))
あとは組み合わせるだけ。何気に初めてas->マクロを使ってみたがどうだろうか。->で通して、最後の一関数だけ別に適用しても良かったかもしれない。
更新: あやぴーさんにご教示頂いたbigintで、テキストとしてデータが来るのを想定した場合:
(->> src (str/split-lines) (map bigint) (apply +) (str) (#(subs % 0 10)))
非常に楽。