1

Take integer partition problem for example, I can write the following code to output all partitions of a positive integer n:

(defn foo
  ([n] (foo n n []))
  ([n k buf]
    (if (zero? n)
      (println buf)
      (doseq [i (range 1 (inc (min n k)))]
        (foo (- n i) i (conj buf i))))))

Then (foo 5) outputs:

[1 1 1 1 1]
[2 1 1 1]
[2 2 1]
[3 1 1]
[3 2]
[4 1]
[5]

The question is how could I write a function bar which generates a lazy-seq containing such results? For example, I want (bar 5) generates ([1 1 1 1 1] [2 1 1 1] [2 2 1] [3 1 1] [3 2] [4 1] [5]).

SaltyEgg
  • 1,498
  • 1
  • 15
  • 26
  • 3
    Indeed, if you think for a minute about how lazy sequences work, you'll realize that being able to have all your state encapsulated for a `recur` call is pretty much exactly the same thing as having your state encapsulated for deferred (lazy) execution! – Charles Duffy Nov 06 '14 at 00:58
  • 1
    @CharlesDuffy I would appreciate it if you show me an example for this question. – SaltyEgg Nov 06 '14 at 01:26

2 Answers2

2

Here is a roughly equivalent function generating the partitions as a sequence of sequences:

(defn bar
  ([n] (bar n n))
  ([n k]
   (if (zero? n)
     [[]]
     (for [i (range 1 (inc (min n k))), tail (bar (- n i) i)]
       (cons i tail)))))

For example,

(bar 5)
; ((1 1 1 1 1) (2 1 1 1) (2 2 1) (3 1 1) (3 2) (4 1) (5))

How lazy is bar?

  • for is lazy.
  • to make it lazier, wrap the body in a lazy-seq.

I have my doubts about the above.

  • The function recurses to depth n.
  • Wrapping the body in lazy-seq I suspect just stacks up lazy sequences, producing just as deep a recursive call stack on accessing the first element.

Besides, the same tails are repeatedly computed; the more so since (bar n k) is the same for all k >= n.

If performance of this function is a specific concern, there are iterative algorithms with constant time per step. As @CharlesDuffy's comment implies, these can be re-jigged to produce lazy sequences.


Why gaze into the crystal ball when you can read the book?

The standard namespace clojure.math.combinatorics, hosted here, contains a partition function that produces a lazy sequence of the partitions of any sequence of objects - fast. Integer partition is where we count the elements of each partition of identical objects. They come out in reverse lexicographic order.

For example

(map #(map count %) (combo/partitions (repeat 5 :whatever)))
;((5) (4 1) (3 2) (3 1 1) (2 2 1) (2 1 1 1) (1 1 1 1 1))

No doubt the code can be stripped down to deal with just this case.

Thumbnail
  • 13,293
  • 2
  • 29
  • 37
  • Thank you very much! This works fine for a small `n`. For a large `n`, I have to implement a better algorithm. – SaltyEgg Nov 07 '14 at 03:44
  • @SaltyEgg A couple of references: [a blog entry that refers to Knuth's recent coverage of the subject](http://11011110.livejournal.com/4862.html) and [a paper from 1998 on FAST ALGORITHMS FOR GENERATING INTEGER PARTITIONS](http://www.site.uottawa.ca/~ivan/F49-int-part.pdf) - Ehrlich's looks suitable. – Thumbnail Nov 07 '14 at 08:18
  • @SaltyEgg My comment is redundant. Mark Engelberg has translated the relevant Knuth algorithm into idiomatic Clojure, which the answer now points to. – Thumbnail Nov 08 '14 at 10:42
1

Here is recursive solution. There are some ways to optimize it and code is not the best.

(defn partitions [n]
  (loop [m n
         res [(init-step n m)]]
    (let [l (last res)]
      (if (= m 1)
        res
        (if (last-step? (last res))
          (recur (- m 1) (vec (conj res (init-step n (- m 1)))))
          (recur m (next-step res)))))))


(defn init-step [n m]
  (if (= n m)
    [n]
    (loop [res [m (- n m)]]
      (let [l (last res)
            f (first res)]
        (if (<= l f)
          res
          (recur (vec (conj (vec (butlast res)) f (- l f)))))))))

(defn next-step [res]
  (let [input-vec (last res)
        cnt (count input-vec)
        i (.indexOf input-vec 1)
        j (if (> i -1) (- i 1) (- cnt 1))
        m (- cnt j)
        new-vec (conj (vec (take j input-vec)) (- (input-vec j) 1))]
    (conj res (vec (concat new-vec (repeat m 1))))))


(defn last-step? [input-vec]
  (if
    (or (nil? input-vec)
        (= (count input-vec) 1)
        (= (input-vec 1) 1)) true
    false))

(partitions 10)
#=> [[10] [9 1] [8 2] [8 1 1] [7 3] [7 2 1] [7 1 1 1] [6 4] [6 3 1] [6 2 1 1] [6 1 1 1 1] [5 5] [5 4 1] [5 3 1 1] [5 2 1 1 1] [5 1 1 1 1 1] [4 4 2] [4 4 1 1] [4 3 1 1 1] [4 2 1 1 1 1] [4 1 1 1 1 1 1] [3 3 3 1] [3 3 2 1 1] [3 3 1 1 1 1] [3 2 1 1 1 1 1] [3 1 1 1 1 1 1 1] [2 2 2 2 2] [2 2 2 2 1 1] [2 2 2 1 1 1 1] [2 2 1 1 1 1 1 1] [2 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1]]
fl00r
  • 82,987
  • 33
  • 217
  • 237