読者です 読者をやめる 読者になる 読者になる

Arantium Maestum

プログラミング、囲碁、読書の話題

Clojure入門 - Project Eulerを解いてみる 問13

第13問

50桁の数字を100個足しあわせて、上10桁が何になるかを求める。

数字が大きすぎるので普通に足しあわせたりはできない。JavaのLongの最大値は9223372036854775807とのこと。

戦略としては以下のとおり。

  1. 上10桁を数字として扱って足しあわせる。
  2. 下40桁を、桁ごとに評価し足していって、繰上げを次の桁に足していく。
  3. 上10桁の和と、下40桁の繰上げを足しあわせる。
  4. その数字の上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)))

非常に楽。