Created
January 1, 2020 07:26
-
-
Save hindol/f918b382b94dd1f5d98306195f231128 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns com.github.hindol.euler | |
(:require | |
[clojure.test :refer [is]] | |
[clojure.tools.trace :refer [trace-ns untrace-ns]]) | |
(:gen-class)) | |
(set! *unchecked-math* true) | |
(set! *warn-on-reflection* true) | |
(defn pentagonal-seq | |
[] | |
(let [xf (comp | |
(mapcat (juxt identity -)) | |
(map #(/ (* % (dec (* 3 %))) 2)))] ;; k (3k - 1) / 2 | |
(sequence xf (iterate inc 1)))) | |
(def cached-pentagonal-seq | |
(pentagonal-seq)) | |
;; https://projecteuler.net/problem=78 | |
;; | |
;; * | |
;; | |
;; ** | |
;; * * | |
;; | |
;; *** | |
;; ** * | |
;; * * * | |
;; | |
;; **** | |
;; *** * | |
;; ** ** | |
;; ** * * | |
(def count-partitions | |
"Given n coins, how many distinct partitions are possible?" | |
(memoize | |
(fn | |
[n] | |
{:pre [(is (not (neg? n)))] | |
:post [(is (not (neg? %)))]} | |
(cond | |
(neg? n) 0 | |
(zero? n) 1 | |
:else (let [xf (comp | |
(take-while #(<= % n)) | |
(map #(count-partitions (- n %)))) | |
terms (sequence xf cached-pentagonal-seq) | |
signs (cycle [+1 +1 -1 -1])] | |
(reduce +' (map * signs terms))))))) | |
(defn -main | |
[& _] | |
(let [n 6 | |
xf (comp | |
(map count-partitions) | |
(take-while #(pos? (rem % 1000000))))] | |
(+ n (count (sequence xf (iterate inc n)))))) | |
(time (-main)) |
For posterity, here it is with the transducer:
Takes 4.2s on my machine.
(set! *unchecked-math* true)
(def ^:const modulo
1000000)
(def ^:const limit
100000)
(defn solve []
(loop [n 1 partitions (vector-of :long 1)]
(if (<= n limit)
(let [sum (unchecked-long
(loop [sum 0 i 0 offsets (sequence (comp (mapcat (juxt identity -))
(map (fn [^long %] (quot (* % (dec (* 3 %))) 2))))
(iterate inc 1))]
(let [offset (unchecked-long (first offsets))]
(if (< n offset)
sum
(if (< (rem i 4) 2)
(recur (rem (+ sum (unchecked-long (nth partitions (- n offset)))) modulo) (inc i) (rest offsets))
(recur (rem (- sum (unchecked-long (nth partitions (- n offset)))) modulo) (inc i) (rest offsets)))))))
sum (if (neg? sum) (+ sum modulo) sum)]
(if (zero? sum)
(count partitions)
(recur (inc n) (conj partitions sum))))
(count partitions))))
(time (solve))
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I also played a bit with it, and it is such a hot loop, that it is very sensitive to any kind of overhead.
This is the most simple port to Clojure, without unchecked math, without any casting or type hinting, using immutable data-structures:
And still on my machine (a 2012 dell xps 13) it just takes 2.1s total. So 2s extra then the C++ version. On repl.it it takes 15s.
So my impression would be that, I think what you are doing is a different algorithm. One thing I've noticed, your take-while goes really big, the number passed to it exceeds the size of long, which is why you need to use
+'
in your reduce, but the algorithm I copied from C++ never reaches such highn
.