I have a little programming issue that I'm trying to resolve in Clojure.
Say, I have a list with Integer values (they also include zeros). These values have a sum, which I want to decrease by a certain value. To get to this lower sum, I want to decrease the values in the list by ratio.
Say, I have the following list: [0, 10, 30, 40, 20, 0]. The sum is 100, and I want to decrease the sum to 90. I want to decrease the values by ratio, so the new list will be [0, 9, 27, 36, 18, 0].
However, this gets problematic when the numbers turn into fractions. When you round numbers (either with round, floor or ceil), you can end up with a sum that's off by 1 or 2. I can't seem to find an elegant solution. Everything I get consists of going through all the values once, and then going back to repair the offset. Any ideas?
Edit
To clarify the behaviour I want to see, the way it rounds doesn't really matter to me, as long as the sum is correct and the ratios of the numbers are approximately the same. I don't care care whether the total error is the smallest or that most are rounded down.
Additional requirements are that numbers are only allowed to stay equal or get lower, numbers should be >= 0, and the resulting list of numbers should be integers.
We can specify the function's requirements with clojure.spec. If we want the function to support integers w/arbitrary precision, sequences that sum to zero, empty sequences, etc., we could write this function spec:
(s/def ::natural-integer (s/and integer? (comp not neg?)))
(s/fdef dec-sum-int
:args (s/and (s/cat :new-sum ::natural-integer
:nums (s/coll-of ::natural-integer))
#(<= (:new-sum %) (apply +' (:nums %))))
:ret (s/coll-of ::natural-integer)
:fn (fn [{:keys [args ret]}]
(and (= (count (:nums args)) (count ret))
;; each output <= corresponding input
(every? true? (map <= ret (:nums args)))
(or (empty? ret)
(= (:new-sum args) (apply + ret))))))
Then st/check the original answer below to see failing examples, or see example invocations with s/exercise-fn.
Here's a version that satisfies the spec for your updated requirements. Most of the complexity is to ensure each output <= input when adjusting for rounding error:
(defn dec-sum-int [new-sum nums]
(let [sum (apply +' nums)
ratio (if (zero? sum) 1 (/ new-sum sum))
nums' (map #(bigint (*' % ratio)) nums)
err (- new-sum (apply + nums'))]
(loop [nums nums
nums' nums'
out []
err err]
(cond
(zero? err)
(into out nums')
(seq nums')
(let [[num & more] nums
[num' & more'] nums']
(if (pos? num)
(let [num'' (min num (+ num' err))]
(recur more more'
(conj out num'')
(- err (- num'' num'))))
(recur more more' (conj out num') err)))
:else out))))
(st/summarize-results (st/check `dec-sum-int))
{:sym playground.so/dec-sum-int}
=> {:total 1, :check-passed 1}
Original Answer
Here's a function to multiply each number in a collection by a ratio to reach some desired sum:
(defn adjust-sum [new-sum nums]
(let [sum (apply + nums)]
(map #(* % (/ new-sum sum))
nums)))
(adjust-sum 90 [0 10 30 40 20 0])
=> (0N 9N 27N 36N 18N 0N)
(map int *1)
=> (0 9 27 36 18 0)
For your example the results naturally come out as big integers. This is the only given example, but this problem lends itself well to property-based, generative testing. We can define properties that should hold for all examples and use test.check to test the function against many random examples we may not have imagined:
(tc/quick-check 10000
(prop/for-all [new-sum gen/int
nums (->> (gen/vector gen/int)
;; current approach fails for inputs that sum to zero
(gen/such-that #(not (zero? (apply + %)))))]
(= new-sum (apply + (adjust-sum new-sum nums)))))
=> {:result true, :num-tests 10000, :seed 1552170880184}
See updates above for handling examples with rounding error, or prior edits for handling negative numbers.
I don't think there is way to solve it without going through the list a second time to fix the rounding. Here is one solution using Largest Remainder Method:
(defn adj-seq
[input ratio rounding]
(let [;;
;; function to apply ratio to a number
;;
mul-ratio (partial * ratio)
;;
;; function to apply ratio and rounding to a number
;;
mul-ratio-r (comp rounding mul-ratio)
;;
;; sort oirignal input with largest remainder first
;; then applies ratio and rounding to each number
;;
rounded-list (->> input
(sort-by #(- (mul-ratio-r %)
(mul-ratio %)))
(map mul-ratio-r))
;;
;; sum of original numbers
;;
sum-input (reduce + input)
;;
;; calculate the delta between the expected sum and sum of all rounded numbers
;;
delta (- (mul-ratio-r sum-input) (reduce + rounded-list))]
;;
;; distribute delta to the rounded numbers in largest remainder order
;;
(->> rounded-list
(reductions (fn [[remain _] e]
;; increment number by 1 if remaining delta is >1
(if (pos? remain)
[(dec remain) (inc e)]
;; otherwise returns the rounded number as is
[0 e]))
;; delta is the initial value to feed to the reducing function
[delta])
;;
;; ignore the first output from the reducing function - which is the original delta
;;
rest
;;
;; get the adjusted number: ratio + rounding + delta-adj
;;
(map last))))
And a sample run:
(def input [0 10 30 40 20 0])
(def ratio 0.83)
(def rounding int)
(reduce + input)
;; => 100
(* ratio *1)
;; => 83.0
(adj-seq input ratio rounding)
;; => (25 17 8 33 0 0)
(reduce + *1)
;; => 83
Is this what you need?
(defn scale-vector
"Given `s`, a sequence of numbers, and `t`, a target value for the sum of
the sequence, return a sequence like `s` but with each number scaled
appropriately."
[s t]
(let [ratio (/ (reduce + (filter number? s)) t)]
(map #(if (number? %) (/ % ratio) %) s)))
(scale-vector [10 20 :foo 30 45.3 0 27/3] 21)
=> (1.837270341207349 3.674540682414698 :foo 5.511811023622047 8.32283464566929 0.0 1.6535433070866141)
(reduce + (filter number? (scale-vector [10 20 :foo 30 45.3 0 27/3] 21)))
=> 21.0
What's going on here:
We're assuming that s is a sequence of numbers; but it isn't necessarily an error if some element is not a number. Filtering for numbers allows us to cope gracefully is some elements are non-numeric; I've chosen to preserve non-numeric elements, but you could equally drop them.
I've done nothing special to exclude rational numbers from the output, and I can't see why you'd need to; but if you wanted to do that you could use (map double [1 1/2 22/7]) => (1.0 0.5 3.142857142857143).
But idiomatically, in Clojure, a number is just a number. Any function that accepts numbers should accept numbers. Rational numbers - what you are referring to as 'fractions' - are just numbers like any other numbers. Don't worry about them.
Related
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
Consider a query function q that returns, with a delay, some (let say ten) results.
Delay function:
(defn dlay [x]
(do
(Thread/sleep 1500)
x))
Query function:
(defn q [pg]
(lazy-seq
(let [a [0 1 2 3 4 5 6 7 8 9 ]]
(println "q")
(map #(+ (* pg 10) %) (dlay a)))))
Wanted behaviour:
I would like to produce an infinite lazy sequence such that when I take a value only needed computations are evaluated
Wrong but explicative example:
(drop 29 (take 30 (mapcat q (range))))
If I'm not wrong, it needs to evaluate every sequence because it really doesn't now how long the sequences will be.
How would you obtain the correct behaviour?
My attempt to correct this behaviour:
(defn getq [coll n]
(nth
(nth coll (quot n 10))
(mod n 10)))
(defn results-seq []
(let [a (map q (range))]
(map (partial getq a)
(iterate inc 0)))) ; using iterate instead of range, this way i don't have a chunked sequence
But
(drop 43 (take 44 (results-seq)))
still realizes the "unneeded" q sequences.
Now, I verified that a is lazy, iterate and map should produce lazy sequences, so the problem must be with getq. But I can't understand really how it breaks my laziness...perhaps does nth realize things while walking through a sequence? If this would be true, is there a viable alternative in this case or my solution suffers from bad design?
This post of mine discusses Thomson's paradox, and simulates it in Clojure.
The state function returns the state of the lamp at time = t.
(defn thomsons-lamp []
(iterate (fn [[onoff dur]]
[(not onoff) (/ dur 2)])
[true 1]))
(defn state [t]
(let [t-vals (map second (thomsons-lamp))]
(loop [i 1]
(if (<= t (apply + (take i t-vals)))
((comp first last) (take i (thomsons-lamp)))
(recur (inc i))))))
How do I define a cleaner state function (preferably without loop/recur)?
The only sins here are
Unnecessary quadratic complexity in state
Evidence of floating point usage and error in your blog post. The code as written should be using ratios -- (state 2) should not terminate...
Reduce/reduced would be a good candidate for your state function.
(defn thomsons-lamp []
(map vector (iterate not true) (iterate #(* 1/2 %) 1)))
(defn state [t]
(reduce (fn [[s1 t1] [s2 t2]]
(if (>= t1 t) (reduced s1) [s2 (+ t1 t2)]))
(thomsons-lamp)))
A one-line solution in Clojure
In Clojure, though not in ClojureScript, we can express the state function as a series of pure function applications:
(defn state [t]
(-> t rationalize / biginteger .bitLength odd?))
or, without using the threading macro
(defn state [t]
(odd? (.bitLength (biginteger (/ (rationalize t))))))
Let's test it:
(map (juxt identity state) [1 0.7 0.5 0.4 0.3 0.2])
; ([1 true] [0.7 true] [0.5 false] [0.4 false] [0.3 false] [0.2 true])
Taking it step by step:
(defn state [t]
(-> t
rationalize ; convert to a ratio to avoid losing precision using floating point
/ ; take the reciprocal
biginteger ; round down (if need be) to a java.math.BigInteger
.bitLength ; take its length in bits (a method, not a Clojure function)
odd? ; ask whether odd
))
How does it work?
Instead of testing where the given number t fits in the series of toggle-points
1 1/2 1/4 1/8 ...
we test where 1/t (that's (/ t) in Clojure) fits in the series of inverted toggle-points
1 2 4 8 ...
which, in binary, is
1 10 100 1000 ...
which are the smallest numbers with
1 2 3 4 ...
binary digits.
Applying BigInteger/bitLength tells us how many binary digits 1/t has - rounding down has no effect. This is the number of terms of series 1 2 4 8 ... that 1/t reaches. So the answer is whether this number is odd.
I have a collection which I'd like to split by an arbitrary percentage. The actual problem I'm trying to solve is to split a dataset into a training and cross-validation set.
The destination of each element should be chosen at random, but each source element should appear only once in the result and the size of the partitions is fixed. If the source collection has duplicates, the duplicates could appear in different output partitions or the same.
I have this implementation:
(defn split-shuffled
"Returns a 2 element vector partitioned by the percentage
specified by p. Elements are selected at random. Each
element of the source collection will appear only once in
the result."
[c p]
(let [m (count c)
idxs (into #{} (take (* m p) (shuffle (range m))))
afn (fn [i x] (if (idxs i) x))
bfn (fn [i x] (if-not (idxs i) x))]
[(keep-indexed afn c) (keep-indexed bfn c)]))
repl> (split-shuffled (range 10) 0.2)
[(4 6) (0 1 2 3 5 7 8 9)]
repl> (split-shuffled (range 10) 0.4)
[(1 4 6 7) [0 2 3 5 8 9)]
But I'm not happy that keep-indexed is called twice.
How can this be improved?
EDIT: I originally wanted to keep the order in the partitions, but I dropped that requirement without re-thinking, so #mikera's solution is correct!
Why do you need the indexes at all?
Just shuffle the collection directly:
(defn split-shuffled
[c p]
(let [c (shuffle c)
m (count c)
t (* m p)]
[(take t c) (drop t c)]))
I'm just learning to use lazy sequences in Clojure, and I'm not sure what I'm doing wrong in the following code:
(defn sum [seqn]
(reduce + seqn))
(defn fib
([] (concat [0 1] (fib 0 1)))
([a b] (lazy-seq (cons (+ a b) (fib b (+ a b))))))
(defn up-to [n seqn]
(filter (fn [x] (< x n)) seqn))
(sum (up-to 100 (fib))) => ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)
The numbers being summed shouldn't be larger than 100, so what is causing the integer overflow?
Filtering an infinite seq produces an infinite seq and reducing over this causes filter to keep looking for another matching item even after the predicate stops returning true.
Replace filter with take-while. The infinite sequence generated by (fib) will cause filter to run forever, but before that it will break due to the ArithmeticException you're experiencing. take-while will stop further evaluation of the list after the (fn [x] (< x n)) predicate evaluates to false.
(defn up-to [n seqn]
(take-while (fn [x] (< x n)) seqn))
(sum (up-to 100 (fib))) ;; => 232
starting with clojure 1.3.0 numbers don't auto-promote to bigInt/bigDecimal.
to fix this use +' instead
your 100th fibinachi number is too large for an integer
user> (nth (fib) 100)
354224848179261915075N