Reputation: 1119
I have a matrix that contains rows of binary vectors:
library(gtools)
mat<-permutations(2,6,v=c(0,1),repeats.allowed=TRUE)
I would like to sort the rows of the matrix so that each row differs by only 1 digit from its direct neighbours (above and below).
Is this possible to do?
Upvotes: 3
Views: 116
Reputation: 44614
Here's another recursive approach, thanks for the idea @josilber.
# calculates the decimal difference between adjacent (1 digit difference)
# binary digits of length n, starting with 2.
diffs <- function(n) {
if (n == 1) {
return(n)
} else {
k <- Recall(n - 1)
c(k, 2^(n-1), rev(-k))
}
}
get.mat2 <- function(ncols) {
adj.ints <- rev(cumsum(c(0, diffs(ncols)))) # get all differences
mat32 <- matrix(rev(as.integer(intToBits(adj.ints))), ncol=32, byrow=TRUE) # convert to binary representation in matrix
mat32[, (33-ncols):32] # remove extraneous columns
}
I was curious what the pattern was if you viewed the rows as decimal integers:
m <- get.mat(8)
ints <- strtoi(apply(m, 1, paste, collapse=''), 2)
plot(ints, type='b')
There's obviously a pattern. It looks "non-stationary", so I took the first differences:
diffs <- diff(strtoi(apply(m, 1, paste, collapse=''), 2))
plot(diffs, type='b')
After inspecting this graph for a few n, the algorithm above jumps out. As a secondary note, this method doesn't seem to be significantly faster:
ncols <- 20
system.time(josilber <- get.mat(ncols))
# user system elapsed
# 1.90 0.13 2.05
system.time(plourde <- get.mat2(ncols))
# user system elapsed
# 1.61 0.19 1.81
josilber <- matrix(as.integer(josilber), ncol=ncol(josilber))
identical(josilber, plourde)
# TRUE
Upvotes: 2
Reputation: 44330
Interesting question! A way I thought of to accomplish this is to recursively build such a matrix from ones with a smaller number of digits. Let's assume we have a n-digit matrix (containing all the 2^n unique n-bit sequences) where each neighbor differs at most by one. If we rbind
this matrix with its reversal (by rows), then each row of the result will differ by at most 1, and the two rows right in the middle will be identical. We can add a 0 in front of the first half and a 1 in front of the second half. The result contains all 2^(n+1) unique (n+1)-bit sequences, and all neighbors have distance at most 1 from each other.
For a base case, we can use (0 1) for the 1-digit matrix.
get.mat <- function(size) {
if (size == 1) {
return(matrix(c(0, 1), ncol=1))
} else {
smaller <- get.mat(size-1)
return(rbind(cbind(0, smaller), cbind(1, smaller[nrow(smaller):1,])))
}
}
get.mat(4)
# [1,] 0 0 0 0
# [2,] 0 0 0 1
# [3,] 0 0 1 1
# [4,] 0 0 1 0
# [5,] 0 1 1 0
# [6,] 0 1 1 1
# [7,] 0 1 0 1
# [8,] 0 1 0 0
# [9,] 1 1 0 0
# [10,] 1 1 0 1
# [11,] 1 1 1 1
# [12,] 1 1 1 0
# [13,] 1 0 1 0
# [14,] 1 0 1 1
# [15,] 1 0 0 1
# [16,] 1 0 0 0
An interesting side effect of the approach for building these matrices is that the first and last rows will also have distance 1 from each other.
Upvotes: 5