I tried to compute factorial through defmulti and defmethod.
(defmulti factorial identity)
(defmethod factorial 0 [_] 1)
(defmethod factorial :default [num]
(* num (factorial (dec num))))
It works fine for small numbers
(-> 10 factorial) ;;3628800
(-> 2 factorial) ;; 2
it shows Integer Overflow Exception for factorial 40
(-> 40 factorial)
ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow
My curiosity is
How can we compute factorial for Big Numbers using defmulti and defmethod?
Clojure's implementation of number types builds on the host platform's number types. Your solution works when you define the arbitrary size flag N, because the underlying number type changes on the JVM.
(type 10) ;=> java.lang.Long
(type 10N) ;=> clojure.lang.BigInt
clojure.lang.BigInt uses either java.math.BigInteger or a Java long as the underlying type, depending on the bit size of the number.
On a different host, the Javascript engine of a browser, both types are JavaScript's native Numbers. The factorial function gives a result up to 170 in ClojureScript. It does not throw when overflowing, but returns the JavaScript number value Infinity:
(factorial 170) ; => 7.257415615307994e+306
(factorial 170N) ; => 7.257415615307994e+306
(factorial 171N) ; => Infinity
Update: This answer (pointed out by #cske) gives a neat solution to use the *' operator, which bumps up the number type in case it would overflow:
(defmethod factorial :default [num]
(*' num (factorial (dec num))))
(factorial 40) ; => 815915283247897734345611269596115894272000000000N
I have solved it
(-> 40N factorial) ;;815915283247897734345611269596115894272000000000N
Related
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.
So, I grabbed the latest numeric tower for a couple quick calculations and noticed that the numbers returned have "N" at the end. Why? What does it mean?
clojure.math.numeric-tower=> (expt 64 20)
1329227995784915872903807060280344576N
clojure.math.numeric-tower=> (expt 36 20)
13367494538843734067838845976576N
That is the literal form of BigInt:
user=> (type 1N)
clojure.lang.BigInt
versus, for example:
user=> (type 1)
java.lang.Long
or
user=> (type 1.0)
java.lang.Double
There's also the M suffix for BigDecimal.
user=> (type 1M)
java.math.BigDecimal
I'm not sure of all the rules for promotion to arbitrary precision (BigInt, BigDecimal). I think most of the "regular" math functions won't promote to arbitrary precision, but there are a few that do (e.g. +', -', *', inc', dec').
e.g. Regular + overflows:
user=> (+ Long/MAX_VALUE 1)
ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)
but +' promotes:
user=> (+' Long/MAX_VALUE 1)
9223372036854775808N
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.
We can represent the number 12 as 2r001100 in clojure.
Is there a built-in function to print 2r001100 when given the number 12?
java.lang.Integer/toString will print numbers with arbitrary radix:
(Integer/toString 0xf2 2) ==> "11110010"
(Integer/toString 0xf2 16) ==> "f2"
(Integer/toString 0xf2 27) ==> "8q"
see cl-format
user=> (require '[clojure.pprint :refer (cl-format)])
nil
user=> (cl-format nil "2r~6,'0',B" 12)
"2r001100"
These functions generate and print strings using java.util.Formatter.
format
printf
But they don't do binary, so the best I could come up with is:
(fn [i] (str "2r" (Integer/toBinaryString i)))
All of these answers are good, but either won't support two's-complement for negative numbers (cl-format), or won't print out the correct number of bits based on the width of the data itself (e.g., calling Integer/toBinaryString or Integer/toString on a byte will not do what you want, especially for negative numbers).
Here's a solution that will correctly print out the exact bits of the underlying data:
(defn print-bits [b]
(let [class-name (.getName (class b))
is-byte (= "java.lang.Byte" class-name)
num-bits (clojure.lang.Reflector/getStaticField class-name "SIZE")
format-string (str "~" num-bits "'0b")
bin-str-fn #(clojure.lang.Reflector/invokeStaticMethod
(if is-byte "java.lang.Integer" class-name)
"toBinaryString"
(to-array [%]))
bit-string (if is-byte
(str/join (take-last 8 (bin-str-fn (Byte/toUnsignedInt b))))
(bin-str-fn b))]
(println (str (str/join (repeat (- num-bits (count bit-string)) \0))
bit-string))))
Test of extremes here, using (bit-shift-left 1 63), or 1000000000000000000000000000000000000000000000000000000000000000.
The cl-format solution provided gives me an integer overflow.
Integer/toBinaryString gives me Value out of range for int: -9223372036854775808.
But Long/toBinaryString gives me the string that I expected.
I want to calculate !1000 in clojure, how can I do this without getting a integer-overflow exception?
My factorial code is right now: (reduce * (range 1 1001)).
You could use the *' operator which supports arbitrary precision by automatically promoting the result to BigInt in case it would overflow:
(reduce *' (range 1 1001))
Put N at the end of the number which makes it a bigint,
(reduce * (range 1N 1001N))
Coerce the parameters to clojure.lang.BigInt
(reduce * (range (bigint 1) (bigint 1001)))
I.e. if you are working with an third-party library that doesn't use *'
(defn factorial' [n]
(factorial (bigint n)))