Say I want to get all combinations of bill/coins denominations that amount to a given value, given also a set of available denominations.
So for instance, for (change 14 #{1 2 5 10}) I'd expect
(
{10 1, 5 0, 2 2, 1 0}
{10 1, 5 0, 2 1, 1 2}
{10 0, 5 2, 2 2, 1 0}
{10 0, 5 2, 2 1, 1 2}
;; ...
)
My attempt was
(defn change [amount denominations]
(let [dens (sort > denominations)
vars (repeatedly (count dens) lvar)]
(run* [q]
(== q (zipmap dens vars))
(everyg #(fd/in % (fd/interval 0 amount)) vars)
(== amount (apply + (map * dens vars))))))
But naturally the last line doesn't work. I haven't found a way to do a sort of reduce over the vars sequence, or some other way to set goals that are not valid for each lvar individually, but for the whole (while also doing some operation with external values, amount and denominations in this example).
I haven't found a [...] way to set goals that are not valid for each lvar individually, but for the whole (while also doing some operation with external values, amount and denominations in this example).
One way to do this is to define a recursive relation function that takes the logic vars, the denominations, and the desired sum, and uses conso to set goals for each pair of vars and dens items:
(defn productsumo [vars dens sum]
(fresh [vhead vtail dhead dtail product run-sum]
(conde
[(emptyo vars) (== sum 0)]
[(conso vhead vtail vars)
(conso dhead dtail dens)
(fd/* vhead dhead product)
(fd/+ product run-sum sum)
(productsumo vtail dtail run-sum)])))
Notice the fresh logic variables here for the heads and tails of vars and dens, a product to store the product of the head of each for each "pass", and a run-sum variable that will be used to constrain the total of all the products such they're equal to sum. The combination of conso and recursion allows us to set goals for the whole of vars and dens.
Then plug that in to your existing function:
(defn change [amount denoms]
(let [dens (sort > denoms)
vars (repeatedly (count dens) lvar)]
(run* [q]
(== q (zipmap dens vars))
(everyg #(fd/in % (fd/interval 0 amount)) vars)
(productsumo vars dens amount))))
And finally get the answers:
(change 14 #{1 2 5 10})
=>
({10 0, 5 0, 2 0, 1 14}
{10 1, 5 0, 2 0, 1 4}
{10 0, 5 1, 2 0, 1 9}
{10 0, 5 0, 2 1, 1 12}
{10 1, 5 0, 2 1, 1 2}
{10 1, 5 0, 2 2, 1 0}
{10 0, 5 0, 2 2, 1 10}
{10 0, 5 2, 2 0, 1 4}
{10 0, 5 1, 2 1, 1 7}
{10 0, 5 0, 2 3, 1 8}
{10 0, 5 0, 2 4, 1 6}
{10 0, 5 1, 2 2, 1 5}
{10 0, 5 0, 2 5, 1 4}
{10 0, 5 2, 2 1, 1 2}
{10 0, 5 0, 2 6, 1 2}
{10 0, 5 1, 2 3, 1 3}
{10 0, 5 0, 2 7, 1 0}
{10 0, 5 2, 2 2, 1 0}
{10 0, 5 1, 2 4, 1 1})
I suspect there may be a more succinct/elegant solution, but this works.
Related
We have sort-by in clojure to sort values against a vector or map.
For example, if I have a vector that I want to sort against:
(def ^:const sort-numbers ["one" "two" "three" "four" "five"])
And I have this random vector with disordered values, something like:
(def numbers ["two" "three" "one" "four"])
Now, they could be sorted by:
(sort-by #(.indexOf sort-numbers %) numbers)
Again, now I have this vector of maps:
(def numbers-map [{:one 1 :two 2 :three 3}
{:one 4 :two 4 :three 3}
{:one 3 :two 2 :three 1}])
If I want to sort the numbers-map by the value of the key :one against all the maps in the vector,
(sort-by :one numbers-map)
would do, and it would give me the following result:
({:one 1, :two 2, :three 3} {:one 3, :two 2, :three 1} {:one 4, :two 4, :three 3})
Now, what I need is a combination of these.
That is, I need to sort the numbers-map by the value of the key :one, but I don't want them to be auto-sorted, but rather sort them against a specified vector of all possible values of :one that I already have somewhere.
How can that be achieved?
This allows you to do that
(def numbers-maps [{:one 1 :two 2 :three 3}
{:one 4 :two 4 :three 3}
{:one 3 :two 2 :three 1}])
(def sort-numbers [4 3 2 1])
(sort-by #(.indexOf sort-numbers (:one %))
numbers-maps)
({:one 4, :two 4, :three 3}
{:one 3, :two 2, :three 1}
{:one 1, :two 2, :three 3})
Here is another example:
(def numbers-maps [{:one 6, :two 9, :three 9}
{:one 9, :two 9, :three 8}
{:one 7, :two 6, :three 2}
{:one 4, :two 4, :three 5}
{:one 9, :two 1, :three 5}
{:one 1, :two 8, :three 8}
{:one 8, :two 3, :three 9}
{:one 8, :two 4, :three 5}
{:one 4, :two 8, :three 1}
{:one 5, :two 1, :three 1}])
(def one-values [10 5 1 2 4 3])
(sort-by #(.indexOf one-values (:one %))
numbers-maps)
({:one 6, :two 9, :three 9}
{:one 9, :two 9, :three 8}
{:one 7, :two 6, :three 2}
{:one 9, :two 1, :three 5}
{:one 8, :two 3, :three 9}
{:one 8, :two 4, :three 5}
{:one 5, :two 1, :three 1}
{:one 1, :two 8, :three 8}
{:one 4, :two 4, :three 5}
{:one 4, :two 8, :three 1})
I'm new to Clojure trying my hands on with different "destructing" in clojure.
So what am I trying to achieve here is, I have two data-set like in below code snippet :- Major & minor,
(def result {"Major" { [200 {1 5,2 6, 3 4}] [ 201 {1 5,2 10,4 10,6 10}]}
"Minor" { [ 200 {1 5,2 6,3 4,4 10}] [ 201 {1 5,2 10,3 10}]}})
I want to take each of minor's data-set entry and compare it with its corresponding major data-set entry, if the value of the major data-set entry is sub-set of the minor one, then delete that entry from both the data-set (i.e major and minor).Else assoc that entry in some other var (i.e major-only & minor-only). And vice versa.
For example:-
{"Major" { [200 {1 5,2 10, 3 10}] [201 {1 5,2 10,4 10,6 10}] [204 {1 4,2 5,3 8,4 9}]}
"Minor" { [200 {1 5,2 10,3 10,4 10}] [203 {1 5,2 10,3 10}] [204 {1 4,2 5,3 8}]}})
major-only will be:- {201 value} (because it doesn't exists in minor), {204 value} (since the major's value is not subset of minor's value for key 204)
minor-only will be:-{203 value} (Since it does not exists in major),{204 value} entry(because the subset condition failed)
I tried to perform reduce with update-in while destructuring and comparing the data, but couldn't get the efficient way to get the expected result. Can anyone assist me here?
Also, i want to return the result of the function as below:-
{:major-only major-only
:minor-only minor-only}, how can I return this type of value?
I'm not sure all of your rules are completely clear to me, but here's a stab at an implementation of the sort of function you describe.
First, I'd define a map-subset? function:
(defn map-subset? [m1 m2]
(and (<= (count m1) (count m2))
(every?
(fn [[k v]]
(and (contains? m2 k)
(= (m2 k) v)))
m1)))
That might not be exactly what you mean by "subset", so feel free to adapt it accordingly.
Here's your data. I removed the square brackets around pairs in the map, since your question uses invalid EDN, and I assume this is what you meant:
(def data {"Major" {200 {1 5, 2 10, 3 10}
201 {1 5, 2 10, 4 10, 6 10}
204 {1 4, 2 5, 3 8, 4 9}}
"Minor" {200 {1 5, 2 10, 3 10, 4 10}
203 {1 5, 2 10, 3 10}
204 {1 4, 2 5, 3 8}}})
The core function, then, is relatively simple:
(let [{:strs [Major Minor]} data]
{:major-only (into {}
(remove (fn [[k v]]
(map-subset? v (Minor k))))
Major)
:minor-only (into {}
(remove (fn [[k v]]
(map-subset? v (Major k))))
Minor)})
This rebuilds each map, removing entries whose values are subsets of the corresponding value in the other map:
{:major-only {201 {1 5, 2 10, 4 10, 6 10}
204 {1 4, 2 5, 3 8, 4 9}}
:minor-only {200 {1 5, 2 10, 3 10, 4 10}
203 {1 5, 2 10, 3 10}}}
This produces slightly different results than in your example, which is why I'm not entirely sure if I understand your requirements, since from my interpretation of map subsets, Major's 200 is a subset of Minor's 200, and Minor's 204 is a subset of Major's 204.
I have a sequence of sorted data, and want to set the neighbor flag. e.g for the following data, for any element, if any neighbor has flag as 1, then set the
any-neighbor-flagged as 1 for that element. We could define neighbor as whether the diff of the seq is <=2, if the diff<=2, then they are neighbor.
There could be million of data point.
(def a '({:seq 1 :flag 1} {:seq 2 :flag 0} {:seq 5 :flag 0} {:seq 8 :flag 0} {:seq 10 :flag 1} {:seq 12 :flag 1}))
the expected result is:
({:seq 1 :any-neighbor-flagged 0} {:seq 2 :any-neighbor-flagged 1} {:seq 5 :any-neighbor-flagged 0} {:seq 8 :any-neighbor-flagged 1}
{:seq 10 :any-neighbor-flagged 1} {:seq 12 :any-neighbor-flagged 1})
With partition, we can look at a collection with neighboring context.
user=> (partition 3 1 (range 10))
((0 1 2) (1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7) (6 7 8) (7 8 9))
Given an input in that form, we can use reduce to accumulate a result based on neighbor comparisons.
user=> (pprint/pprint (reduce (fn [acc [i j k]]
(conj acc
(assoc j :any-neighbor-flagged
(if (or (= (:flag i) 1)
(= (:flag k) 1))
1 0))))
[]
(partition 3 1 (concat [nil] a [nil]))))
[{:any-neighbor-flagged 0, :seq 1, :flag 1}
{:any-neighbor-flagged 1, :seq 2, :flag 0}
{:any-neighbor-flagged 0, :seq 5, :flag 0}
{:any-neighbor-flagged 1, :seq 8, :flag 0}
{:any-neighbor-flagged 1, :seq 10, :flag 1}
{:any-neighbor-flagged 1, :seq 12, :flag 1}]
Basic idea is to map over 3 sequences - original one, shifted by 1 to the left and shifted by one to the right:
(defn set-flags [coll]
(map
(fn [curr {nf :flag} {pf :flag}]
(-> curr
(dissoc :flag)
(assoc :any-neighbor-flagged (if (or (= pf 1) (= nf 1)) 1 0))))
coll
(concat [{}] (drop-last coll))
(concat (rest coll) [{}])))
(set-flags a) ; => ({:any-neighbor-flagged 0, :seq 1} {:any-neighbor-flagged 1, :seq 2} {:any-neighbor-flagged 0, :seq 5} {:any-neighbor-flagged 1, :seq 8} {:any-neighbor-flagged 1, :seq 10} {:any-neighbor-flagged 1, :seq 12})
Illustration (for simplicity, only value of :flag is displayed):
(1 0 0 0 [1] 1) ; original seq
---------------
(1 0 0 [0] 1) ; shifted to right
(0 0 0 1 [1]) ; shifted to left
Now in map function we also have left neighbor and right neighbor for each element of input (possibly, empty maps). Based on that it's easy to set correct value for :any-neighbor-flagged.
Let's say I have [{1 2 3 4}]. How do I append {5 6 7 8} so that the vector will say [{1 2 3 4} {5 6 7 8}]
The same way you append any other value to a vector: conj
(let [v [#{1 2 3 4}]]
(conj v #{5 6 7 8}))
;; gives [#{1 2 3 4} #{5 6 7 8}]
(Incidentally, note the #{} syntax. {1 2 3 4} is a map containing the pairs (1 2) and (3 4), while #{1 2 3 4} is a set of the numbers 1, 2, 3 and 4.)
(conj [#{1 2 3 4}] #{5 6 7 8})
Note that in your example you have not sets, but maps from long to long.
Also, note that this is not appending to the vector (vectors are immutable), but rather it is creating a new vector with the new value appended.
I have a list of keys: (1 2 3 4)
I want a map with the values set to 0 like this: {1 0, 2 0, 3 0, 4 0}. How do I do that?
You could do something like this with the zipmap function:
(zipmap '(1 2 3 4) (repeat 0))
=> {4 0, 3 0, 2 0, 1 0}
zipmap takes a list of keys and a list of values and converts them into a map. The repeat function creates an infinite sequence of 0s. zipmap stops when it reaches the end of the shorter list, so just don't pass it two infinite sequences :)
You can also create a function with James' zipmap:
Clojure=> (defn map-to-n [n] (zipmap (range 1 n) (repeat 0)))
#'user/map-to-n
Clojure=> (map-to-n 10)
{9 0, 8 0, 7 0, 6 0, 5 0, 4 0, 3 0, 2 0, 1 0}
The more general pattern for this is to use (apply collection list to create the collection. The Clojure collections all have "constructors" or creation functions that take a variable number of arguments and return thows arguments bundled up in the collection. if your arguments are already wrapped up in another collection then apply is a convenient way to take them out of the collection and pass them to the creation function as arguments.
This is a lot more work. which is why we have wrapper functions like zipmap.
Wow I didn't know about zipmap, thats useful
I would have done it like this
(apply hash-map (interleave '(1 2 3 4) (repeat 0)))
Just another way, which retains the original order of the keys (up to 9 keys!):
(defn list-to-map [& n]
(->> (map vector n (repeat 0))
(into (hash-map))))
(list-to-map 1 2 3 4) => {1 0, 2 0, 3 0, 4 0}
(list-to-map 4 3 2 1) => {4 0, 3 0, 2 0, 1 0}