Adding sets of numbers up to 16 - clojure

I have some sets of numbers:
(#{7 1} #{3 5} #{6 3 2 5}
#{0 7 1 8} #{0 4 8} #{7 1 3 5}
#{6 2} #{0 3 5 8} #{4 3 5}
#{4 6 2} #{0 6 2 8} #{4} #{0 8}
#{7 1 6 2} #{7 1 4})
I wish to make each set into a four number vector, such that the sum of all the vectors add up to 16 and they can only come from the set of numbers:
#{7 1} => [1 1 7 7]
#{4 3 5} => [3 4 4 5]
#{4} => [4 4 4 4]
#{0 8} => [0 0 8 8]
Lastly, the vector has to contain all the numbers in the set. It'll be great to solve this for abitrary vector lengths :)
How would the clojure code be written.

With small sets and the originally stated output length of 4
This is easily handled with naive search
(defn bag-sum [s n]
(for [a s, b s, c s, d s
:let [v [a b c d]]
:when (= n (apply + v))
:when (= (set v) s)]
v))
(take 1 (bag-sum #{7 1} 16)) ;=> ([7 7 1 1])
(take 1 (bag-sum #{3 5} 16)) ;=> ([3 3 5 5])
(take 1 (bag-sum #{4 3 5} 16)) ;=> ([4 4 3 5])
Assuming 16 is fixed and all numbers are non-negative
The search space even without the set constraint is tiny.
(require '[clojure.math.combinatorics :refer [partition]])
(count (partitions (repeat 16 1))) ;=> 231
So, again a naive solution is very practical. We'll produce solutions of all lengths, which can be further filtered as desired. If there is a zero in the input set, it can pad any solution.
(defn bag-sum16 [s]
(for [p (partitions (repeat 16 1))
:let [v (mapv (partial apply +) p)]
:when (= (set v) s)]
v))
First example has 2 solutions - length 4 and length 10.
(bag-sum16 #{7 1}) ;=> ([7 7 1 1] [7 1 1 1 1 1 1 1 1 1])
(bag-sum16 #{3 5}) ;=> ([5 5 3 3])
(bag-sum16 #{3 4 5}) ;=> ([5 4 4 3])
Using core.logic finite domains to prune the search space with arbitrary but specified domain set s, output length m, and sum n
This is still fairly naive but prunes the search tree when the target sum is exceeded. I am a novice at core.logic, so this is more an opportunity to practice than an attempt at best representation of the problem. This performs worse than the naive solutions above on small spaces, but enables calculation in some medium size cases.
(defn bag-sum-logic [s m n]
(let [m* (- m (count s))
n* (- n (apply + s))
nums (vec (repeatedly m* lvar))
sums (concat [0] (repeatedly (dec m*) lvar) [n*])
dom (apply fd/domain (sort s))
rng (fd/interval n*)
sol (run 1 [q]
(== q nums)
(everyg #(fd/in % dom) nums)
(everyg #(fd/in % rng) sums)
(everyg #(apply fd/+ %)
(map cons nums (partition 2 1 sums))))]
(when (seq sol) (sort (concat s (first sol))))))
(bag-sum-logic #{7 1} 4 16) ;=> (1 1 7 7)
(bag-sum-logic #{7 1} 10 16) ;=> (1 1 1 1 1 1 1 1 1 7)
(bag-sum-logic #{3 5} 4 16) ;=> (3 3 5 5)
(bag-sum-logic #{3 4 5} 4 16) ;=> (3 4 4 5)
(time (bag-sum-logic #{3 4 5} 30 100))
;=> "Elapsed time: 18.739627 msecs"
;=> (3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5)
Better algorithms for the general case?
This problem is a linear Diophantine equation, which can be solved with the Extended Euclidean Algorithm via matrix unimodular row reduction, i.e. carry out the Euclidean algorithm in one column while bringing the entire basis row along for the ride.
For example, in the case of #{3 5} and sum 16, you want to solve the equation
3x + 5y = 16
subject to the additional constraints that x > 0, y > 0 and x + y = 4 (your example).
The matrix and reduction steps
[[3 1 0] -> [[3 1 0] -> [[1 2 -1] -> [[1 2 -1]
[5 0 1]] [2 -1 1]] [2 -1 1]] [0 -5 3]]
So the GCD of 3 and 5 is 1, which divides into 16. Therefore there are (infinitely many) solutions before the constraints
x = 16 * 2 - 5k
y = 16 * -1 + 3k
Since we need x + y = 4, 4 = 16 - 2k and therefore k = 6, so
x = 2
y = 2
And we need 2 copies of 3 and 2 copies of 5.
This generalizes to more than 2 variables in the same manner. But whereas for 2 variables the length of the solution fully constrains the single free variable as shown above, more than 3 variables can be underspecified.
Solving linear Diophantine equations can be done in polynomial time. However, once you add the bounds (0, m), finding a solution becomes NP-complete, though a quick perusal of research results suggest there are fairly tractable approaches.

Working on the assumptions that you only want one solution per set and you want the solution ordered ascending as per your example this is what I came up with. There aren't many combinations of sets of 1-4 numbers so the way I initially decomposed the problem was to look at what the pattern of possible solutions might look like.
(def x #{3 5})
(def g 16)
(def y {1 [[0 0 0 0]]
2 [[0 0 0 1][0 0 1 1][0 1 1 1]]
3 [[0 0 1 2][0 1 1 2][0 1 2 2]]
4 [[0 1 2 3]]})
This key of this map indicates the size of the set x that is being evaluated. The values are the possible permutations of indices to the set once it is sorted into a vector. Now we can choose the permutations based on the size of the set and calculate the values of each permutation, stopping as soon as we reach the goal:
(filter #(= g (apply + %))
(for [p (y (count x))]
(mapv #((into [] (sort x)) %) p)))
The values of each key of the map above the permutations form a pattern: the first index is always 0 and the last always is the set size - 1 and all values are either the same as or one above the value to the left. Therefore, the above map can be generalised to:
(defn y2 [m s]
(map (fn [c] (reduce #(conj %1 (+ %2 (peek %1))) [0] c))
(clojure.math.combinatorics/permutations
(mapv #(if (>= % (dec s)) 0 1) (range (dec m))))))
(def y (partial y2 4))
The filter will now work for any number of set items up to s. As the input set is sorted, the search could be optimised to find the right (or no) solution by doing a binary search over the permutations of possible solutions for log2n search time.

Related

Iterate over a list with a given step in clojure

I'm trying to iterate over a list with a given step in clojure.
In python I would have done the following :
xs = list(range(10))
xs[::2]
# out: [0, 2, 4, 6, 8]
xs[1::2]
# out: [1, 3, 5, 7, 9]
I can't figure out a clojure solution that feels idiomatic.
Here is the best I can think of:
(defn iterate-step-2 [xs]
(map first (take-while some? (iterate nnext xs))))
(iterate-step-2 (range 10))
; out: (0 2 4 6 8)
(iterate-step-2 (rest (range 10)))
; out: (1 3 5 7 9)
But it's not as generic (step is not configurable) and as flexible as the python solution. Plus it seems overly complicated.
Is there a better way to do this ?
You can use take-nth for this:
user=> (take-nth 2 (range 10))
(0 2 4 6 8)
user=> (take-nth 2 (rest (range 10)))
(1 3 5 7 9)
;; equivalent to Python's your_seq[1:7:2] would be:
(->> your-seq (drop 1) (take 7) (take-nth 2))
;; equivalent to Python's your_seq[::2] would be:
(->> your-seq (take-nth 2))
;; equivalent to Python's your_seq[2:4:-3] would be:
(->> your-seq (take 4) (drop 2) (reverse) (take-nth 3))
;; equivalent to Python's your_seq[2:-4:-1]:
(->> your-seq (take (+ 1 (- (length your-seq) 4))) (drop 2) (reverse))
Another option is to generate the desired index values, and then use those for the lookup:
(let [N 10
data (vec (range N)) ; `vec` is optional but faster than using (lazy) list if large N
idxs (range 1 10 2)
result (mapv #(nth data %) idxs)]
)
with result:
N => 10
data => [0 1 2 3 4 5 6 7 8 9]
idxs => (1 3 5 7 9)
result => [1 3 5 7 9]
or something less simple:
(let [N 9999
data (vec (range N)) ; `vec` is optional but faster than using (lazy) list if large N
idxs (mapv #(Math/pow 2 %) (range 11))
result (mapv #(nth data %) idxs)]
with result:
idxs => [1.0 2.0 4.0 8.0 16.0 32.0 64.0 128.0 256.0 512.0 1024.0]
result => [1 2 4 8 16 32 64 128 256 512 1024]
Interesting! Apparently nth will accept a floating-point index value as long as it has a zero fraction. :)

How to use frequencies in clojure to combine same frequency and display them once?

I making a poker hands game in clojure. I have to define a function such that such that it returns the ranks in the descending order. For example: order ["2H" "3S" "6C" "5D" "4D"] should return (6 5 4 3 2). But if there is a two-pair like this: ["5H" "AD" "5C" "7D" "AS"] then it should return (14 5 7), but mine returns [14 14 7 5 5], how can I correct this? It should work in the same way for other cases of poker hands as well like for a full house it should give the rank of the three of a kind and the rank of the two of a kind. So, for this I have written:
(defn order
[hand]
"Return a list of the ranks, sorted highest first"
(let [ranks (reverse (sort (map #(.indexOf "--23456789TJQKA" %)
(map #(str (first %)) hand))))]
(if (= ranks [14 5 4 3 2])
[5 4 3 2 1]
(into [] ranks))))
I have also written all the other poker hand functions like flush?, straight? etc.
Also, I have to define another function such that it takes two orders like '(8 5 9) '(8 7 3) and returns true if the first has the larger value of the first difference, else false. Could someone please give me an idea how to do this?
Updated to show sorting by count, then rank:
(defn ->r [hand]
(let [ranks (zipmap "23456789TJKQA" (range 2 15)) ; creates a map like {\2 2, .... \A 14}
count-then-rank
(fn [x y] (compare
[(second y) (first y)]
[(second x) (first x)]))]
(->> hand
(map (comp ranks first)) ; returns the rank for each card eg: '(5 14 5 7 14)
frequencies ; returns a map of rank vs count eg: {5 2, 14 2, 7 1}
(sort count-then-rank) ; becomes a sorted list of tuples eg: '([14 2] [5 2] [7 1])
(map first)))) ; extract the first value each time eg: '(14 5 7)
For a more complete solution, you can use the frequencies to determine if you have 4 of a kind, 3 of a kind, full house etc.
Updated with more info on straight and straight flush:
For a straight, one approach is:
Extract the ranks so you would have a list like '(14 3 2 4 5)
Sort this list to get '(2 3 4 5 14)
Get the first element: 2, and the last element 14
Construct a range from 2 (inclusive) to 15 (exclusive) to get '(2 3 4 5 6 7 8 9 10 11 12 13 14)
Compare against the sorted sequence. In this case the result is false.
Retry, but first replace 14 with 1.
replace => '(1 3 2 4 5)
sort => '(1 2 3 4 5)
(range 1 6) => '(1 2 3 4 5)
This time, the range and the sorted list match, so this is a straight.
(defn straight? [cards] ; eg: ["AH" "3H" "2H" "4H" "5H"]
(let [ranks (zipmap "23456789TJKQA" (range 2 15))
ranks-only (map (comp ranks first) cards) ; eg: '(14 3 2 4 5)
ace-high (sort ranks-only) ; eg: '(2 3 4 5 14)
ace-low (sort (replace {14 1} ranks-only)) ; eg: '(1 2 3 4 5)
consecutive #(= (range (first %) (inc (last %))) %)] ; eg: (= (range 1 6) '(1 2 3 4 5))
(or (consecutive ace-high)
(consecutive ace-low))))
For a flush, simply extract all the suits, and then ensure they are all equal:
(defn flush? [cards]
(apply = (map second cards))) ; this is when suit is the second character
Now, simply combine these two boolean conditions to determine if this is a straight flush
(defn straight-flush? [cards]
(and (straight? cards) (flush? cards)))
See if you can solve 4clojure best hand puzzle, to open up a large number of different ways to tackle this. When I solved this, I used similar, but not identical functions.
Spoiler a more complete solution (using suit first "D7" instead of rank first "7D") is below
https://github.com/toolkit/4clojure-solutions/blob/master/src/puzzle_solutions/best_hand.clj
I think frequencies will get you closer to what you're looking for.
user=> (frequencies [14 14 7 5 5])
{14 2, 7 1, 5 2}
You could use this for sorting:
user=> (sort-by (frequencies [14 14 7 5 5]) [14 14 7 5 5])
(7 14 14 5 5)
And then you could use distinct:
user=> (distinct [14 14 7 5 5])
(14 7 5)
Putting all of these together should get you exactly what you want. I'll leave that as an exercise for the reader. When I'm stuck wondering if there's an easy way to do something, I often turn to Clojure's cheatsheet.

Map with an accumulator in Clojure?

I want to map over a sequence in order but want to carry an accumulator value forward, like in a reduce.
Example use case: Take a vector and return a running total, each value multiplied by two.
(defn map-with-accumulator
"Map over input but with an accumulator. func accepts [value accumulator] and returns [new-value new-accumulator]."
[func accumulator collection]
(if (empty? collection)
nil
(let [[this-value new-accumulator] (func (first collection) accumulator)]
(cons this-value (map-with-accumulator func new-accumulator (rest collection))))))
(defn double-running-sum
[value accumulator]
[(* 2 (+ value accumulator)) (+ value accumulator)])
Which gives
(prn (pr-str (map-with-accumulator double-running-sum 0 [1 2 3 4 5])))
>>> (2 6 12 20 30)
Another example to illustrate the generality, print running sum as stars and the original number. A slightly convoluted example, but demonstrates that I need to keep the running accumulator in the map function:
(defn stars [n] (apply str (take n (repeat \*))))
(defn stars-sum [value accumulator]
[[(stars (+ value accumulator)) value] (+ value accumulator)])
(prn (pr-str (map-with-accumulator stars-sum 0 [1 2 3 4 5])))
>>> (["*" 1] ["***" 2] ["******" 3] ["**********" 4] ["***************" 5])
This works fine, but I would expect this to be a common pattern, and for some kind of map-with-accumulator to exist in core. Does it?
You should look into reductions. For this specific case:
(reductions #(+ % (* 2 %2)) 2 (range 2 6))
produces
(2 6 12 20 30)
The general solution
The common pattern of a mapping that can depend on both an item and the accumulating sum of a sequence is captured by the function
(defn map-sigma [f s] (map f s (sigma s)))
where
(def sigma (partial reductions +))
... returns the sequence of accumulating sums of a sequence:
(sigma (repeat 12 1))
; (1 2 3 4 5 6 7 8 9 10 11 12)
(sigma [1 2 3 4 5])
; (1 3 6 10 15)
In the definition of map-sigma, f is a function of two arguments, the item followed by the accumulator.
The examples
In these terms, the first example can be expressed
(map-sigma (fn [_ x] (* 2 x)) [1 2 3 4 5])
; (2 6 12 20 30)
In this case, the mapping function ignores the item and depends only on the accumulator.
The second can be expressed
(map-sigma #(vector (stars %2) %1) [1 2 3 4 5])
; (["*" 1] ["***" 2] ["******" 3] ["**********" 4] ["***************" 5])
... where the mapping function depends on both the item and the accumulator.
There is no standard function like map-sigma.
General conclusions
Just because a pattern of computation is common does not imply that
it merits or requires its own standard function.
Lazy sequences and the sequence library are powerful enough to tease
apart many problems into clear function compositions.
Rewritten to be specific to the question posed.
Edited to accommodate the changed second example.
Reductions is the way to go as Diego mentioned however to your specific problem the following works
(map #(* % (inc %)) [1 2 3 4 5])
As mentioned you could use reductions:
(defn map-with-accumulator [f init-value collection]
(map first (reductions (fn [[_ accumulator] next-elem]
(f next-elem accumulator))
(f (first collection) init-value)
(rest collection))))
=> (map-with-accumulator double-running-sum 0 [1 2 3 4 5])
(2 6 12 20 30)
=> (map-with-accumulator stars-sum 0 [1 2 3 4 5])
("*" "***" "******" "**********" "***************")
It's only in case you want to keep the original requirements. Otherwise I'd prefer to decompose f into two separate functions and use Thumbnail's approach.

List of a..b in clojure

In Clojure, I can have a sequence a..b with (range a b). But this is a lazy sequence as I understand. Can I just generate a list and/or vector of numbers a..b?
Note: I am new to Clojure.
do you mean something like
user> (vec (range 2 7))
[2 3 4 5 6]
user> (apply list (range 2 7))
(2 3 4 5 6)
user> (into [] (range 2 7))
[2 3 4 5 6]
user> (into '() (range 2 7))
(6 5 4 3 2) ; <-- note the order
user> (into #{} (range 2 7))
#{2 3 4 5 6}

Clojure equality of collections with sequences

I noticed that Clojure (1.4) seems to be happy to consider vectors equal to the seq of the same vector, but that the same does not apply for maps:
(= [1 2] (seq [1 2]))
=> true
(= {1 2} (seq {1 2}))
=> false
Why should the behaviour of = be different in this way?
Clojure's = can be thought of as performing its comparisons in two steps:
Check if the types of the things being compared belong to the same "equality partition", that is a class of types whose members might potentially be equal (depending on things like the exact members of a given data structure, but not the particular type in the partition);
If so, check if the things being compared actually are equal.
One such equality partition is that of "sequential" things. Vectors are considered sequential:
(instance? clojure.lang.Sequential [])
;= true
As are seqs of various types:
(instance? clojure.lang.Sequential (seq {1 2}))
;= true
Therefore a vector is considered equal to a seq if (and only if) their corresponding elements are equal.
(Note that (seq {}) produces nil, which is not sequential and compares "not equal" to (), [] etc.)
On the other hand, maps constitute an equality partition of their own, so while a hash map might be considered equal to a sorted map, it will never be considered equal to a seq. In particular, it is not equal to the seq of its entries, which is what (seq some-map) produces.
I guess this is because in sequences order as well as value at particular position matters where as in map the order of key/value doesn't matter and this difference between semantics causes this to work as shown by your sample code.
For more details have a look at mapEquals in file https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java
It checks if the other object is not map then return false.
user=> (seq {1 2})
([1 2])
user=> (type {1 2})
clojure.lang.PersistentArrayMap
It seems to me that this example points out a slight inconsistency in the notion of equality of values in clojure for this case where they are different types derived from the same type (by the seq function). It could well be argued that this is not inconsistent because it is comparing a derived type to the type it is derived from and I can understand that if the same logic was applied to this same example using vectors (note at the bottom)
the contents are the same type:
user> (type (first (seq {1 2})))
clojure.lang.MapEntry
user> (type (first {1 2}))
clojure.lang.MapEntry
user> (= (type (first {1 2})) (type (first (seq {1 2}))))
true
user> (= (first {1 2}) (first (seq {1 2})))
true
the sequences have the same values
user> (map = (seq {1 2}) {1 2})
(true)
but they are not considered equal
user> (= {1 2} (seq {1 2}))
false
this is true for longer maps as well:
user> (map = (seq {1 2 3 4}) {1 2 3 4})
(true true)
user> (map = (seq {1 2 3 4 5 6}) {1 2 3 4 5 6})
(true true true)
user> (map = (seq {9 10 1 2 3 4 5 6}) {9 10 1 2 3 4 5 6})
(true true true true)
even if they are not in the same order:
user> (map = (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
(true true true true)
but again not if the containing types differ :-(
user> (= {1 2 3 4} (seq {1 2 3 4}))
false
EDIT: this is not always true see below:
to work around this you can convert everything to a seq before comparison, which is (I presume) safe because the seq function always iterates the whole data structure the same way and the structures are immutable values and a seq of a seq is a seq
user> (= (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
false
user> (= (seq {9 10 1 2 3 4 5 6}) (seq {1 2 3 4 5 6 9 10}))
true
vectors are treated differently:
user> (= [1 2 3 4] (seq [1 2 3 4]))
true
Perhaps understanding the minor inconsistencies is part of learning a language or someday this could be changed (though I would not hold my breath)
EDIT:
I found two maps that produce different sequences for the same value so just calling seq on the maps will not give you proper map equality:
user> (seq (zipmap [3 1 5 9][4 2 6 10]))
([9 10] [5 6] [1 2] [3 4])
user> (seq {9 10 5 6 1 2 3 4})
([1 2] [3 4] [5 6] [9 10])
user>
here is an example of what I'm calling proper map equality:
user> (def a (zipmap [3 1 5 9][4 2 6 10]))
#'user/a
user> (def b {9 10 5 6 1 2 3 4})
#'user/b
user> (every? true? (map #(= (a %) (b %)) (keys a)))
true
(seq some-hash-map) gives you a sequence of entries (key/value pairs).
For example:
foo.core=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:c 3] [:b 2])
which is not the same as [:a 1 :b 2 :c 3].