find all ordered triples of distinct positive integers i, j, and k less than or equal to a given integer n that sum to a given integer s - clojure

this is the exercise 2.41 in SICP
I have wrote this naive version myself:
(defn sum-three [n s]
(for [i (range n)
j (range n)
k (range n)
:when (and (= s (+ i j k))
(< 1 k j i n))]
[i j k]))
The question is: is this considered idiomatic in clojure? And how can I optimize this piece of code? since it takes forever to compute(sum-three 500 500)
Also, how can I have this function take an extra argument to specify number of integer to compute the sum? So instead of sum of three, It should handle more general case like sum of two, sum of four or sum of five etc.
I suppose this cannot be achieved by using for loop? not sure how to add i j k binding dynamically.

(Update: The fully optimized version is sum-c-opt at the bottom.)
I'd say it is idiomatic, if not the fastest way to do it while staying idiomatic. Well, perhaps using == in place of = when the inputs are known to be numbers would be more idiomatic (NB. these are not entirely equivalent on numbers; it doesn't matter here though.)
As a first optimization pass, you could start the ranges higher up and replace = with the number-specific ==:
(defn sum-three [n s]
(for [k (range n)
j (range (inc k) n)
i (range (inc j) n)
:when (== s (+ i j k))]
[i j k]))
(Changed ordering of the bindings since you want the smallest number last.)
As for making the number of integers a parameter, here's one approach:
(defn sum-c [c n s]
(letfn [(go [c n s b]
(if (zero? c)
[[]]
(for [i (range b n)
is (go (dec c) n (- s i) (inc i))
:when (== s (apply + i is))]
(conj is i))))]
(go c n s 0)))
;; from the REPL:
user=> (sum-c 3 6 10)
([5 4 1] [5 3 2])
user=> (sum-c 3 7 10)
([6 4 0] [6 3 1] [5 4 1] [5 3 2])
Update: Rather spoils the exercise to use it, but math.combinatorics provides a combinations function which is tailor-made to solve this problem:
(require '[clojure.math.combinatorics :as c])
(c/combinations (range 10) 3)
;=> all combinations of 3 distinct numbers less than 10;
; will be returned as lists, but in fact will also be distinct
; as sets, so no (0 1 2) / (2 1 0) "duplicates modulo ordering";
; it also so happens that the individual lists will maintain the
; relative ordering of elements from the input, although the docs
; don't guarantee this
filter the output appropriately.
A further update: Thinking through the way sum-c above works gives one a further optimization idea. The point of the inner go function inside sum-c was to produce a seq of tuples summing up to a certain target value (its initial target minus the value of i at the current iteration in the for comprehension); yet we still validate the sums of the tuples returned from the recursive calls to go as if we were unsure whether they actually do their job.
Instead, we can make sure that the tuples produced are the correct ones by construction:
(defn sum-c-opt [c n s]
(let [m (max 0 (- s (* (dec c) (dec n))))]
(if (>= m n)
()
(letfn [(go [c s t]
(if (zero? c)
(list t)
(mapcat #(go (dec c) (- s %) (conj t %))
(range (max (inc (peek t))
(- s (* (dec c) (dec n))))
(min n (inc s))))))]
(mapcat #(go (dec c) (- s %) (list %)) (range m n))))))
This version returns the tuples as lists so as to preserve the expected ordering of results while maintaining code structure which is natural given this approach. You can convert them to vectors with a map vec pass.
For small values of the arguments, this will actually be slower than sum-c, but for larger values, it is much faster:
user> (time (last (sum-c-opt 3 500 500)))
"Elapsed time: 88.110716 msecs"
(168 167 165)
user> (time (last (sum-c 3 500 500)))
"Elapsed time: 13792.312323 msecs"
[168 167 165]
And just for added assurance that it does the same thing (beyond inductively proving correctness in both cases):
; NB. this illustrates Clojure's notion of equality as applied
; to vectors and lists
user> (= (sum-c 3 100 100) (sum-c-opt 3 100 100))
true
user> (= (sum-c 4 50 50) (sum-c-opt 4 50 50))
true

for is a macro so it's hard to extend your nice idiomatic answer to cover the general case. Fortunately clojure.math.combinatorics provides the cartesian-product function that will produce all the combinations of the sets of numbers. Which reduces the problem to filter the combinations:
(ns hello.core
(:require [clojure.math.combinatorics :as combo]))
(defn sum-three [n s i]
(filter #(= s (reduce + %))
(apply combo/cartesian-product (repeat i (range 1 (inc n))))))
hello.core> (sum-three 7 10 3)
((1 2 7) (1 3 6) (1 4 5) (1 5 4) (1 6 3) (1 7 2) (2 1 7)
(2 2 6) (2 3 5) (2 4 4) (2 5 3) (2 6 2) (2 7 1) (3 1 6)
(3 2 5) (3 3 4) (3 4 3) (3 5 2) (3 6 1) (4 1 5) (4 2 4)
(4 3 3) (4 4 2) (4 5 1) (5 1 4) (5 2 3) (5 3 2) (5 4 1)
(6 1 3) (6 2 2) (6 3 1) (7 1 2) (7 2 1))
assuming that order matters in the answers that is

For making your existing code parameterized you can use reduce.This code shows a pattern that can be used where you want to paramterize the number of cases of a for macro usage.
Your code without using for macro (using only functions) would be:
(defn sum-three [n s]
(mapcat (fn [i]
(mapcat (fn [j]
(filter (fn [[i j k]]
(and (= s (+ i j k))
(< 1 k j i n)))
(map (fn [k] [i j k]) (range n))))
(range n)))
(range n)))
The pattern is visible, there is inner most map which is covered by outer mapcat and so on and you want to paramterize the nesting level, hence:
(defn sum-c [c n s]
((reduce (fn [s _]
(fn [& i] (mapcat #(apply s (concat i [%])) (range n))))
(fn [& i] (filter #(and (= s (apply + %))
(apply < 1 (reverse %)))
(map #(concat i [%]) (range n))))
(range (dec c)))))

Related

Need the first 10 multiples of any number in Clojure

We've been given a task to print the first ten multiples of any number for which we have written the below code. It is throwing an error. In simple words, if n is 2 then we need to create a table of 2's till 10.
(defn multiples [n]
(while ( n < 11)
(println( n * n))
(swap! n inc)))
(def n (Integer/parseInt (clojure.string/trim (read-line))))
(multiples n)
With this, we're getting the error:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.
(defn multiples [n]
(map #(* n %) (range 1 (+ 10 1))))
user=> (multiples 1)
;; => (1 2 3 4 5 6 7 8 9 10)
user=> (multiples 2)
;; => (2 4 6 8 10 12 14 16 18 20)
The resulting list you can loop over and println each of the elements.
(for [i (multiples 2)]
(println i))
;; or:
(map println (multiples 2)) ;; though one usually doesn't apply
;; `map` on side effect functions ...
To improve your own construct:
You, coming from an imperative language, try to work with mutations.
That is very un-idiomatic clojure.
However, by declaring a value atom, you can access using the # operator to its place. And mutate the variable's value.
(defn multiples [n]
(let [i (atom 1)] ;; i is an atom
(while (< #i 11) ;; #i is the value saved into i
(println (* #i n))
(swap! i inc)))) ;; and correctly you can increase the value
With this multiples, you can also print the values.
You can't apply swap! to normal variables, only to atoms.
while loops one should apply only if number of elements not known.
In this case, one knows very well, when to stop. So use rather
a for loop.
(defn multiples [n]
(for [i (range 1 11)]
(println (* i n))))
Look at what iterate function does here
(defn multiples-of [n]
(iterate (partial * n) n))
(def ten-multiples-of-ten
(take 10 (multiples-of 10)))
EDIT: I misread the author of the question, I believe he wants to just generate a sequence of squares. Here is one way using transducers, cause why not ;)
(def xf
(comp
(map inc)
(map #(* % %))))
(defn first-n-squares [n]
(into [] xf (take n (range))))
You can use recur in a loop:
(defn multiples [n]
(if (< n 11)
(do ; then
(println (* n n))
(recur (inc n)))
nil)) ; else return nil
Running this by invoking
(multiples 1)
in a REPL will produce
1
4
9
16
25
36
49
64
81
100
nil

clojure split collection in chunks of increasing size

Hi I am a clojure newbie,
I am trying to create a function that splits a collection into chunks of increasing size something along the lines of
(apply #(#(take %) (range 1 n)) col)
where n is the number of chunks
example of expected output:
with n = 4 and col = (range 1 4)
(1) (2 3) (4)
with n = 7 and col = (range 1 7)
(1) (2 3) (4 5 6) (7)
You can use something like this:
(defn partition-inc
"Partition xs at increasing steps of n"
[n xs]
(lazy-seq
(when (seq xs)
(cons (take n xs)
(partition-inc (inc n) (drop n xs))))))
; (println (take 5 (partition-inc 1 (range))))
; → ((0) (1 2) (3 4 5) (6 7 8 9) (10 11 12 13 14))
Or if you want to have more influence, you could alternatively provide
a sequence for the sizes (behaves the same as above, if passed (iterate inc 1) for sizes:
(defn partition-sizes
"Partition xs into chunks given by sizes"
[sizes xs]
(lazy-seq
(when (and (seq sizes) (seq xs))
(let [n (first sizes)]
(cons (take n xs) (partition-sizes (rest sizes) (drop n xs)))))))
; (println (take 5 (partition-sizes (range 1 10 2) (range))))
; → ((0) (1 2 3) (4 5 6 7 8) (9 10 11 12 13 14 15) (16 17 18 19 20 21 22 23 24))
An eager solution would look like
(defn partition-inc [coll]
(loop [rt [], c (seq coll), n 1]
(if (seq c)
(recur (conj rt (take n c)) (drop n c) (inc n))
rt)))
another way would be to employ some clojure sequences functions:
(->> (reductions (fn [[_ x] n] (split-at n x))
[[] (range 1 8)]
(iterate inc 1))
(map first)
rest
(take-while seq))
;;=> ((1) (2 3) (4 5 6) (7))
Yet another approach...
(defn growing-chunks [src]
(->> (range)
(reductions #(drop %2 %1) src)
(take-while seq)
(map-indexed take)
rest))
(growing-chunks [:a :b :c :d :e :f :g :h :i])
;; => ((:a) (:b :c) (:d :e :f) (:g :h :i))

How to take n random items from a collection in Clojure?

I have a function that takes n random items from a collection such that the same item is never picked twice. I could accomplish this quite easily:
(defn take-rand [n coll]
(take n (shuffle coll)))
But I have a pesky requirement that I need to return the same random subset when the same seed is provided, i.e.
(defn take-rand [n coll & [seed]] )
(take-rand 5 (range 10) 42L) ;=> (2 5 8 6 7)
(take-rand 5 (range 10) 42L) ;=> (2 5 8 6 7)
(take-rand 5 (range 10) 27L) ;=> (7 6 9 1 3)
(take-rand 5 (range 10)) ;=> (9 7 8 5 0)
I have a solution for this, but it feels a bit clunky and not very idiomatic. Can any Clojure veterans out there propose improvements (or a completely different approach)?
Here's what I did:
(defn take-rand
"Returns n randomly selected items from coll, or all items if there are fewer than n.
No item will be picked more than once."
[n coll & [seed]]
(let [n (min n (count coll))
rng (if seed (java.util.Random. seed) (java.util.Random.))]
(loop [out [], in coll, n n]
(if (or (empty? in) (= n 0))
out
(let [i (.nextInt rng n)]
(recur (conj out (nth in i))
(concat (take i in) (nthrest in (inc i)))
(dec n)))))))
Well, I'm no clojure veteran, but how about:
(defn shuffle-with-seed
"Return a random permutation of coll with a seed"
[coll seed]
(let [al (java.util.ArrayList. coll)
rnd (java.util.Random. seed)]
(java.util.Collections/shuffle al rnd)
(clojure.lang.RT/vector (.toArray al))))
(defn take-rand [n coll & [seed]]
(take n (if seed
(shuffle-with-seed coll seed)
(shuffle coll))))
shuffle-with-seed is similiar to clojure's shuffle, it just passes an instance of Random to Java's java.util.Collections.shuffle().
Replace the shuffle function in your first solution with the reproducible one. I will show you my solution below:
(defn- shuffle'
[seed coll]
(let [rng (java.util.Random. seed)
rnd #(do % (.nextInt rng))]
(sort-by rnd coll)))
(defn take-rand'
([n coll]
(->> coll shuffle (take n)))
([n coll seed]
(->> coll (shuffle' seed) (take n))))
I hope this solution make results you expected:
user> (take-rand' 5 (range 10))
(5 4 7 2 6)
user> (take-rand' 5 (range 10))
(1 9 0 8 5)
user> (take-rand' 5 (range 10))
(5 2 3 1 8)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)

Overflow in Clojure computation despite using BigInt

The following closure computation overflows despite the use of big integers:
(defn binomial-coefficient [n k]
(let [rprod (fn [a b] (reduce * (range a (inc b))))]
(/ (rprod (- n k -1) n) (rprod 1 k))))
(binomial-coefficient 100N 50N)
I could not figure out where the overflow happens. For example, executing rprod by itself seems to work.
NB: the binomial coefficient code was taken from Rosetta Code.
The problem is that you're calling (rprod 1 k) with an integer 1 and not a bigint 1N:
(defn binomial-coefficient [n k]
(let [rprod (fn [a b] (reduce * (range a (inc b))))]
(/ (rprod (- n k -1) n) (rprod 1N k))))
(binomial-coefficient 100N 50N)
The problem lays in range function:
=> (range 1 10N)
(1 2 3 4 5 6 7 8 9)
=> (range 1N 10)
(1N 2N 3N 4N 5N 6N 7N 8N 9N)
Alternative solution is to use *', -' and inc' instead of ordinary *, - and inc operators, because they have build-in support for arbitrary precision and never overflow:
(defn binomial-coefficient [n k]
(let [rprod (fn [a b] (reduce *' (range a (inc' b))))]
(/ (rprod (-' n k -1) n) (rprod 1 k))))
(binomial-coefficient 100 50)

Lazy Pascal's Triangle in Clojure

I'm trying to write a succinct, lazy Pascal's Triangle in Clojure, rotated such that the rows/columns follow the diagonals of the triangle. That is, I want to produce the following lazy-seq of lazy-seqs:
((1 1 1 1 ...)
(1 2 3 4 ...)
(1 3 6 10 ...)
...
)
The code I have written is:
(def pascal
(cons (repeat 1)
(lazy-seq
(map #(map + %1 %2)
(map #(cons 0 %) (rest pascal)))
pascal
)))
so that each row is formed by adding a right-shifted version of itself to the previous row. The problem is that it never gets past the first line, since at that point (map #(cons 0 %) (rest pascal))) is empty.
=> (take 5 (map #(take 5 %) pascal))
((1 1 1 1 1))
What's a sensible way to go about solving this? I'm fairly new to programming in Clojure, and the very different way of thinking about a problem that it involves, so I'd really appreciate suggestions from anybody more experienced with this.
Succinct and lazy
(def pascal (iterate (partial reductions +') (repeat 1)))
(map (partial take 5) (take 5 pascal))
;=> ((1 1 1 1 1)
; (1 2 3 4 5)
; (1 3 6 10 15)
; (1 4 10 20 35)
; (1 5 15 35 70))
But too lazy?
(take 5 (nth pascal 10000))
;=> StackOverflowError
Try again
(take 5 (nth pascal 10000))
;=> (0)
Uh-oh, start over, and try, try again
(def pascal (iterate (partial reductions +') (repeat 1)))
(count (flatten (map (partial take 5) (take 100000 pascal))))
;=> 500000
Now these are all in your heap
(take 5 (nth pascal 100000))
;=> (1 100001 5000150001 166676666850001 4167083347916875001)
pascal should not be a var but a function that generates infinite seqs.
Check out this question for usage on lazy-seq
BTW, try this:
(defn gennext [s sum]
(let [newsum (+ (first s) sum)]
(cons newsum
(lazy-seq (gennext (rest s) newsum)))))
(defn pascal [s]
(cons s
(lazy-seq (pascal (gennext s 0)))))
(pascal (repeat 1)) gives you integer overflow exception but that does mean it produces the infinite seqs. You can use +' to use big integer.