Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Methods to exhaustively partition a vector into pairs in R

(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.)

Problem

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.

like image 226
gatsky Avatar asked Nov 08 '22 14:11

gatsky


1 Answers

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))
}
like image 152
Ista Avatar answered Nov 14 '22 23:11

Ista