Euler 26 – recurring cycles

Share

Another euler problem about counting the greatest recurring cycle inside a fraction.

Problem 26 – http://projecteuler.net/problem=26
A unit fraction contains 1 in the numerator. The decimal representation of the unit fractions with denominators 2 to 10 are given:
1/2 = 0.5
1/3 = 0.(3)
1/4 = 0.25
1/5 = 0.2
1/6 = 0.1(6)
1/7 = 0.(142857)
1/8 = 0.125
1/9 = 0.(1)
1/10 = 0.1
Where 0.1(6) means 0.166666…, and has a 1-digit recurring cycle. It can be seen that 1/7 has a 6-digit recurring cycle.
Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.

As usual, we will add facts just below the functions to document the function.

take-while and 1

We need to be able to capture elements that satisfies the first element plus the first element which falsifies the predicate. So for this, we will use the standard take-while and add the next element. It’s a take-while and 1.

(defn tw
  "'take-while pred coll' with the first element that wrong the predicate pred."
  [pred coll]
  (let [[h t] (split-with pred coll)]
    (concat h [(first t)])))

(m/fact
  (tw even? [0 2 4 6 8 9 10 11 12]) => [0 2 4 6 8 9]
  (tw even? [1 2 3 4])              => [1])

division

We compute the division as we learnt it in elementary school. We’ll use the iterate function to benefit from lazyness.

(defn division
  "Compute the division the elementary school way :D (D is denominator, N numerator, R remains, Q quotient)."
  [N D]
  (->> {:c? true :n N}
       (iterate
        (fn [{:keys [n]}]
          (let [R (rem n D)
                Q (quot n D)]
            (cond (or (= 0 R) (= R D)) {:c? false :n 0        :q Q}
                  (< R D)              {:c? true  :n (* 10 R) :q Q}
                  :else                {:c? true  :n R        :q Q}))))
       (drop 1)))

(m/fact :now-we-have-a-way-to-divide-infinitely
  (->> (division 1 8)
       (tw :c?)
       (map :q)) => [0 1 2 5]
  (->> (division 1 20)
       (tw :c?)
       (map :q)) => [0 0 5]
  (->> (division 1 7)
       (take 20)
       (map :q)) => [0 1 4 2 8 5 7 1 4 2 8 5 7 1 4 2 8 5 7 1]
  (->> (division 1 7)
       (take 20)
       (map :n)) => [10 30 20 60 40 50 10 30 20 60 40 50 10 30 20 60 40 50 10 30]
  (->> (division 1 13)
       (take 20)
       (map :q)) => [0 0 7 6 9 2 3 0 7 6 9 2 3 0 7 6 9 2 3 0])

recurring-cycle-count

Now I want to be able to count the recurring cycles inside a sequence.

(defn recurring-cycle-count
  "Compute the size of a recurring cycle in a sequence."
  [s]
  (->> {:v s :m {} :i 0}
       (iterate
        (fn [{:keys [v m i] :as cp}]
          (let [[h & t] v
                p       (m h)]
            (if p
              (assoc cp :r (- i p))
              {:v t :m (assoc m h i) :i (+ 1 i)}))))
       (drop-while #(nil? (:r %)))
       first
       :r))

(m/fact
  (recurring-cycle-count [10 30 20 60 40 50 10 30 20 60 40 50 10 30 20 60 40 50 10 30]) => 6
  (recurring-cycle-count [0 1 4 2 8 5 7 1 4 2 8 5 7 1 4 2 8 5 7 1])                     => 6
  (recurring-cycle-count [0 1 2 3 4 1 2 3 4])                                           => 4
  (recurring-cycle-count [0 1 2 5 2 5 2 5])                                             => 2)

recurring-cycle

Now that We have a way to infinitely divide a number by another and a function to compute the recurring-cycle count, we can compute what we want and ensure it’s ok according to the problem’s facts.

For this, we can create a function which will divide the number by 1, then retrieve the sequence of rest from that division and compute the recurring cycle count from this sequence.

(def recurring-cycle ^{:doc "Compute the recurring cycle from a division by 1"}
  (comp recurring-cycle-count
        (partial map :n)
        (partial division 1)))

(m/tabular
 (m/fact
   (recurring-cycle ?n) => ?r)
    ?n ?r
    2  1
    3  1
    4  1
    5  1
    6  1
    7  6
    8  1
    9  1
    10 1
    13 6)

Result

So, from 0 to 1000, we compute the recurring cycle count for each number then determine the max. The number 983 is the number with the longest recurring-cycle 982.

(defn max-recurring-cycle
  "Given a limit l, return the couple [longest-recurring-cycle number] from 1 to (l-1), which corresponds to the number for which 1/number has the longest recurring cycle."
  [l]
  (->> (range 1 l)
       (map (juxt recurring-cycle identity))
       (into (sorted-map))
       last))
(m/fact
  (max-recurring-cycle 1000) => [982 983])

Some small check on time:

euler-lab.core26> (time (max-recurring-cycle 1000))
"Elapsed time: 237.337448 msecs"
[982 983]