Euler: Porblem 95

Posted by YpsilonTAKAI On 2013年6月3日月曜日 0 コメント
95番です。友愛数の列をみつける問題です。

 2月に解いていたのに、なぜかここにエントリを作っていませんでした。 そんなわけで、どんな風に解いたのかうろ覚えなので、コードを読んで解説してみます。


友愛数を見つけるためには約数の和を求める必要があります。約数関数はσ(n)で表わされて、公式もあったりして、以前の問題ではそれを使ったりしたのですが、素因数分解が必要で時間がかかります。
今回は、趣向を変えて、以前勉強会のときに考えた、エラトステネスの篩のロジックを使って約数の総和の一覧を求めて使うことにしました。

エラトステネスの篩は、数の一覧から、「ある数の倍数になっているものを削除」することで素数を見つけますが、YがXの倍数ということはXはYの約数であるということを使って「ある数の倍数の場所にその数字を足す」ということで総和を求めます。

一覧ができてしまえば、あとはたどって行くだけです。
ただ、A->B->C->D->E->(C)のように途中からループしたり、さらに、M->N->D->E->C->(D)のようあるループに横から突入するパターンも考えられるので、それを考慮した探索にしてあります。

時間は1分をいくらか超えてしまっていますが、許容範囲でしょう。


;; Problem 95
;; "Elapsed time: 82961.485742 msecs"
(require '[clojure.test :as test])
(defn sigma1-list [^long size]
(loop [i 2
^longs arr (long-array size 1)]
(if (>= i (/ size 2))
arr
(recur (inc i)
(loop [a arr
j (* i 2)]
(if (>= j size)
a
(recur (do (aset a j (+ (aget a j) i))
a)
(+ j i))))))))
(test/is (= (seq (sigma1-list 20))
(seq (long-array [1 1 1 1 3 1 6 1 7 4 8 1 16 1 10 9 15 1 21 1]))))
(defn find-seq [start ^longs arr max]
(loop [point start
s []]
(cond (> point max)
,,[false s]
(some #(= point %) s)
,,[true
(drop-while #(not= point %) s)]
:t
,,(recur (aget arr point)
(conj s point)))))
(defn find-loop [size ^longs arr]
(loop [nums (apply sorted-set (range 1 size))
loop-list #{}]
(if (empty? nums)
loop-list
(let [[res found] (find-seq (first nums) arr size)
new-nums (disj (apply disj nums found ) (first nums))]
(if res
(recur new-nums (conj loop-list found))
(recur new-nums loop-list))))))
(defn pe-95 [size]
(->> (sigma1-list size)
(find-loop size ,,)
(sort-by count ,,)
(last ,,)))
view raw pe_95.clj hosted with ❤ by GitHub



0 コメント:

コメントを投稿