Partition specific sets - clojure

I want to use clojure to partition specific sets:
For example:
There is a vector [1 2 3 4 5]. Also there is a map here, like {2 1, 3 1}, which means generate 1 set that contains 2 elements, and 1 set that contains 3 elements.
(some_function {2 1, 3 1} [1 2 3 4 5])
;; I only want to these:
(([1 2 3] [4 5])
([1 2 4] [3 5])
([1 2 5] [3 4])
([1 2] [3 4 5])
([1 3 4] [2 5])
([1 3 5] [2 4])
([1 3] [2 4 5])
([1 4 5] [2 3])
([1 4] [2 3 5])
([1 5] [2 3 4]))
Previously, I thought to use (combo/partitions [1 1 2]) to generate all the cases, and then filter based on the condition, but it is so inefficient when you have a large vector like [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17] and map like {5 1, 6 2}(generate 1 set that contains 5 elements, and 2 sets that contain 6 elements). I just want to generate what I need at the first time, how can I do this job?

In the specific example you give, there are 5,717,712 partitions that satisfy those constraints, and considerably more possibilities with no constraints. So it's not so much that combinatorics is inefficient; I think you're simply underestimating how many possibilities there are.
The combinations function is a better tool for achieving the more direct combinations you are looking for.
In your example,
(def specific-partitions
(let [initial-list [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17]]
(for [c1 (combinations initial-list 5)
:let [remainder1 (remove (set c1) initial-list)]
c2 (combinations remainder1 6)
:let [remainder2 (remove (set c2) remainder1)]]
[c1 c2 remainder2])))
=>(take 10 specific-partitions)
([(1 2 3 4 5) (6 7 8 9 10 11) (12 13 14 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 10 12) (11 13 14 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 10 13) (11 12 14 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 10 14) (11 12 13 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 10 15) (11 12 13 14 16 17)]
[(1 2 3 4 5) (6 7 8 9 10 16) (11 12 13 14 15 17)]
[(1 2 3 4 5) (6 7 8 9 10 17) (11 12 13 14 15 16)]
[(1 2 3 4 5) (6 7 8 9 11 12) (10 13 14 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 11 13) (10 12 14 15 16 17)]
[(1 2 3 4 5) (6 7 8 9 11 14) (10 12 13 15 16 17)])
It's still a rather large number of partitions, but at least you are now generating them directly.
From this example, you could generalize this technique to a function that takes an arbitrary map describing the partition structure, like you described in your question.
The specific code I've written here wouldn't work right if your initial set has duplicate elements; that would require a slightly different removal method.

Related

How do I check if any element of a vector is in the value of a hashmap?

I have vectors with a list of tiles and a map of adjacent tiles (like a graph). I want to return true if there are any adjacent tiles and false if there are no adjacent tiles. So comparing [1 3 19] should be true, because (adjacents 1) contains 3. While comparing [1 14 19] should be false because (adjacents 1) does not contain 14 or 19, (adjacents 14) does not contain 1 or 19 and (adjacents 19) does not contain 1 or 14.
So tile 1 is adjacent to tiles 2, 3, 4, 5, 6 and 7.
(def adjacents
{1 [2 3 4 5 6 7]
2 [1 3 7 8 9 10]
3 [1 2 4 10 11 12]
4 [1 3 5 12 13 14]
5 [1 4 6 14 15 16]
6 [1 5 7 16 17 18]
7 [1 2 6 8 18 19]
8 [2 7 8 18]
9 [2 8 10]
10 [2 3 9 11]
11 [3 10 12]
12 [3 4 11 13]
13 [4 12 14]
14 [4 5 13 15]
15 [5 14 16]
16 [5 6 15 17]
17 [6 16 18]
18 [6 7 17 19]
19 [7 8 18]})
I've thought about using map, but I can't figure out what function to put inside it. Also thought about turning the values of the hashmap into a set and the vector into a set then comparing the intersection but I'm not thinking about this correctly.
You can write a function that loops over the possible pairs of adjacent vertices and checks if any of the pairs are adjacent:
(defn any-adjacent? [adjmap vertices]
(seq (for [a vertices
b vertices
x (adjmap a)
:when (= x b)]
a)))
(any-adjacent? adjacents [1 3 19])
;; => (1 3)
(any-adjacent? adjacents [1 14 19])
;; => nil
If you use sets instead of vectors as suggested by #amalloy, that is
(def adjacents {1 #{7 4 6 3 2 5},
2 #{7 1 3 9 10 8}
...
})
it is possible to implement more concisely and with better time complexity:
(defn any-adjacent? [adjmap vertices]
(seq (for [a vertices
b vertices
:when ((adjmap a) b)]
a)))
You could collect all the adjacent tiles with mapcat, then test whether that collection contains any of your tiles using some:
(defn any-adjacent [graph tiles]
(some (set tiles)
(mapcat graph tiles)))
(any-adjacent adjacents [1 3 19])
; => 3
(any-adjacent adjacents [1 14 19])
; => nil
(any-adjacent adjacents [8])
; => 8
If the return value needs to be Boolean:
(defn any-adjacent? [graph tiles]
(some? (some (set tiles)
(mapcat graph tiles))))
The main part of the solution will be function some.
For each number in the given vector v, you have to find a vector of adjacent elements: (graph %) and then compare this vector to other elements in v, using second some: (some (disj (set v) %) (graph %)). So, I think this should work:
(defn any-adjacent? [graph v]
(some #(some (disj (set v) %)
(graph %))
v))
Tests:
(def adjacents
{1 [2 3 4 5 6 7]
2 [1 3 7 8 9 10]
3 [1 2 4 10 11 12]
4 [1 3 5 12 13 14]
5 [1 4 6 14 15 16]
6 [1 5 7 16 17 18]
7 [1 2 6 8 18 19]
8 [2 7 8 18]
9 [2 8 10]
10 [2 3 9 11]
11 [3 10 12]
12 [3 4 11 13]
13 [4 12 14]
14 [4 5 13 15]
15 [5 14 16]
16 [5 6 15 17]
17 [6 16 18]
18 [6 7 17 19]
19 [7 8 18]})
(any-adjacent? adjacents [1 3 19])
=> 3
(any-adjacent? adjacents [1 14 19])
=> nil
This is the solution I came up with. It turns the list of locations and the values of the map into sets then takes the intersection. If the intersection is empty, there is no overlap so nothing is adjacent. By using map on the hashmap, it pulls all the relevant values out.
(defn adjacent?
[locations-v neighbors-m]
(let [loc-set (set locations-v)
neighbors-set (set (flatten (map neighbors-m locations-v)))]
(seq (set/intersection loc-set neighbors-set))))

Does Clojure have a set that remembers insertion order?

Like Java's LinkedHashSet. So instead of this:
(-> #{} (conj 1 2 3 4 5) vec)
=> [1 4 3 2 5]
You get:
(-> ??? (conj 1 2 3 4 5) vec)
=> [1 2 3 4 5]
Not in the core library, but there's this one.
(use 'flatland.ordered.set)
(ordered-set 4 3 1 8 2)
=> #ordered/set (4 3 1 8 2)
(conj (ordered-set 9 10) 1 2 3)
=> #ordered/set (9 10 1 2 3)
(into (ordered-set) [7 6 1 5 6])
=> #ordered/set (7 6 1 5)

Multiply nested pairs of list values in Clojure

What I am ultimately trying to do is multiply two vectors together and return a vector of the same size eg:
[6 7 8 9 10] * [0 1 3 1 0] => [0 7 24 9 0]
I was attempting to do something like:
(partition 2 (interleave [6 7 8 9 10] [0 1 3 1 0])) => ((6 0) (7 1) (8 3) (9 1) (10 0))
... then multiply each nested list value and use flatten to get:
(0 7 24 9 0)
but I cant figure out how I could multiply nested list values?
Map takes multiple sequences and a function to combine each member with the corresponding member of the other sequences.
user> (map * [6 7 8 9 10] [0 1 3 1 0])
(0 7 24 9 0)
user> (mapv * [6 7 8 9 10] [0 1 3 1 0])
[0 7 24 9 0]
in this case it calls * on the first number from each list, then on the second from each list and so on, building a seq of the output. If you would prefer the output in a vector mapv is convenient.

How to convert a map into a collection without the keys

I have a map like this:
{1 [1 4 7], 2 [2 5 8], 0 [3 6 9]}
I want to write a function that returns this:
[[1 4 7] [2 5 8] [3 6 9]]
As a side note, I'm getting the map by doing this:
(group-by #(mod % 3) [1 2 3 4 5 6 7 8 9])
If anyone has a shortcut for going from a vector like this: [1 2 3 4 5 6 7 8 9] to the second one shown above, please let me know!
As to your other question: (partition 3 v) creates a matrix, and (apply map vector m) is an idiom to transpose a matrix m (how does it work? Exercise for the reader or google it). So:
> (apply map vector (partition 3 [1 2 3 4 5 6 7 8 9]))
([1 4 7] [2 5 8] [3 6 9])
There are other ways of course. For example, with destructuring:
> (let [{ a 0 b 1 c 2 } (group-by #(mod % 3) [1 2 3 4 5 6 7 8 9])] [b c a])
[[1 4 7] [2 5 8] [3 6 9]]
Does order matter? vals is takes a map, and returns a sequence of the values. But order is not defined for a map, so the order of the values sequence returned is arbitrary.
> (vals {1 [1 4 7], 2 [2 5 8], 0 [3 6 9]})
([3 6 9] [1 4 7] [2 5 8])
Sorted:
> (sort (vals {1 [1 4 7], 2 [2 5 8], 0 [3 6 9]}))
([1 4 7] [2 5 8] [3 6 9])

How to split a sequence into every 3 neighbor elements in clojure?

Given a sequence like:
[1 2 3 4 5 6]
How to split it into every 3 neighbor elements in clojure? Just like the following:
([1 2 3] [2 3 4] [3 4 5] [4 5 6])
Functions in clojure.core are preferred!
See partition:
user=> (partition 3 1 [1 2 3 4 5 6])
((1 2 3) (2 3 4) (3 4 5) (4 5 6))