HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Counting ways to form an amount using coins

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
countingamountcoinswaysusingform

Problem

This is just a fun little exercise I had to do for a homework once (in Java rather than Clojure though). Basically, the goal is to find the number of different coin stacks you can build with the coins 1,2,5 and 10 to form a number N (I believe a closed form solution for this exists, but this isn't what this is about).

My solution here works, but I'm not quite happy with it:

  • Explicit loop. Not sure how to do away with it though.



  • A few functions there that I think should be core functions, but I can't find them.



  • Even if they aren't in core, my functions could probably be written somewhat more concisely



Anyways, here's my code (by the way, the function is called "fast-iterative" because the exercise was about calculating these values recursively. Which I think is insane due to the branching factor, but whatever):

(ns test.coin-stacks)

(def maximum (partial reduce max))

(defn shift-vector
  "Shifts v one to the left, insert shift-val at the right"
  [v shift-val]
  (conj (vec (rest v)) shift-val))

(defn update
  "Updates ks in m with f applied to ks.
  I can't believe I have to write this myself."
  [m ks f]
  (apply assoc m (interleave ks (map (comp f m) ks))))

(defn fast-iterative-coinstacks 
  "Returns the number of ways to form a coin stack with a total
  value of n with coins"
  [n coins]
  (let [max-coin (maximum coins)
        initial-stacks (apply conj [1] (repeat max-coin 0))]
    (loop [stacks initial-stacks iteration 0] 
      (if-not (< iteration n) 
        (first stacks)
        (recur (shift-vector (update stacks coins (partial +' (stacks 0))) 0)
               (inc iteration))))))

Solution

How about something like this:

(defn shift-vector
  "Shifts v one to the left, insert shift-val at the right"
  [v shift-val]
  (conj (subvec v 1) shift-val))

(defn update-in-all
  "Updates ks in m with f applied for each value at k."
  [m ks f]
  (reduce (fn [m' k] (update-in m' [k] f)) m ks))

(defn fast-iterative-coinstacks
  "Returns the number of ways to form a coin stack with a total
  value of n with coins"
  [n coins]
  (let [max-coin (reduce max coins)
        initial-stacks (into [1] (repeat max-coin 0))
        process-stacks (fn [stacks]
                         (-> stacks
                             (update-in-all coins (partial + (first stacks)))
                             (shift-vector 0)))]
    (-> (iterate process-stacks initial-stacks)
        (nth n)
        first)))


Some explanations:

-
update-in works both with vectors and maps, and can update nested values as well,

-
the threading macro -> makes the flow of your code easier to follow,

-
iterate is a really convenient function that given f and x returns a lazy sequence of x, (f x), (f (f x))...

-
you can do a lot of things with reduce!

Code Snippets

(defn shift-vector
  "Shifts v one to the left, insert shift-val at the right"
  [v shift-val]
  (conj (subvec v 1) shift-val))

(defn update-in-all
  "Updates ks in m with f applied for each value at k."
  [m ks f]
  (reduce (fn [m' k] (update-in m' [k] f)) m ks))

(defn fast-iterative-coinstacks
  "Returns the number of ways to form a coin stack with a total
  value of n with coins"
  [n coins]
  (let [max-coin (reduce max coins)
        initial-stacks (into [1] (repeat max-coin 0))
        process-stacks (fn [stacks]
                         (-> stacks
                             (update-in-all coins (partial + (first stacks)))
                             (shift-vector 0)))]
    (-> (iterate process-stacks initial-stacks)
        (nth n)
        first)))

Context

StackExchange Code Review Q#17973, answer score: 4

Revisions (0)

No revisions yet.