(This is inspired by another question marked as a duplicate. I think it is an interesting problem though, although perhaps there is an easy solution from combinatorics, about which I am very ignorant.)
For a vector of length n
, where n mod 2
is zero, find all possible ways to partition all elements of the vector into pairs, without replacement, where order does not matter.
For example, for a vector c(1,2,3,4)
:
list(c(1,2), c(3,4))
list(c(1,3), c(2,4))
list(c(1,4), c(2,3))
My approach has been the following (apologies in advance for novice code):
# write a function that recursively breaks down a list of unique pairs (generated with combn). The natural ordering produced by combn means that for the first pass through, we take as the starting pair, all pairings with element 1 of the vector with all other elements. After that has been allocated, we iterate through the first p/2 pairs (this avoids duplicating).
pairer2 <- function(kn, pair_list) {
pair1_partners <- lapply(kn, function(x) {
# remove any pairs in the 'master list' that contain elements of the starting pair.
partners <- Filter(function(t) !any(t %in% x), pair_list)
if(length(partners) > 1) {
# run the function again
pairer2(kn = partners[1:(length(partners)/2)], partners)
} else {return(partners)}
})
# accumulate results into a nested list structure
return(mapply(function(x,y) {list(root = x, partners = y)}, kn, pair1_partners, SIMPLIFY = F))
}
# this function generates all possible unique pairs for a vector of length k as the starting point, then runs the pairing off function above
pair_combn <- function(k, n = 2) {
p <- combn(k, n, simplify = F)
pairer2(kn = p[1:(length(k)-1)], p)}
# so far a vector k = 4
pair_combn(1:4)
[[1]]
[[1]]$root
[1] 1 2
[[1]]$partners
[[1]]$partners[[1]]
[1] 3 4
[[2]]
[[2]]$root
[1] 1 3
[[2]]$partners
[[2]]$partners[[1]]
[1] 2 4
[[3]]
[[3]]$root
[1] 1 4
[[3]]$partners
[[3]]$partners[[1]]
[1] 2 3
It also works for larger k
as far as I can tell. This isn't that efficient, possibly because Filter
is slow for large lists, and I have to confess I can't collapse the nested lists (which are a tree representation of possible solutions) into a list of each partitioning. It feels like there should be a more elegant solution (in R)?
Mind you, it is interesting that this recursive approach generates a parsimonious (albeit inconvenient) representation of the possible solutions.
Here is one way:
> x <- c(1,2,3,4)
> xc <- combn(as.data.frame(combn(x, 2)), 2, simplify = FALSE)
> Filter(function(x) all(1:4 %in% unlist(x)), xc)
[[1]]
V1 V6
1 1 3
2 2 4
[[2]]
V2 V5
1 1 2
2 3 4
[[3]]
V3 V4
1 1 2
2 4 3
>
More generally:
pair_combn <- function(x) {
Filter(function(e) all(unique(x) %in% unlist(e)),
combn(as.data.frame(combn(x, 2)),
length(x)/2, simplify = FALSE))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With