Question

I am filling a 10x10 martix (mat) randomly until sum(mat) == 100

I wrote the following.... (i = 2 for another reason not specified here but i kept it at 2 to be consistent with my actual code)

 mat <- matrix(rep(0, 100), nrow = 10)
 mat[1,]  <- c(0,0,0,0,0,0,0,0,0,1)
 mat[2,]  <- c(0,0,0,0,0,0,0,0,1,0)
 mat[3,]  <- c(0,0,0,0,0,0,0,1,0,0)
 mat[4,]  <- c(0,0,0,0,0,0,1,0,0,0)
 mat[5,]  <- c(0,0,0,0,0,1,0,0,0,0)
 mat[6,]  <- c(0,0,0,0,1,0,0,0,0,0)
 mat[7,]  <- c(0,0,0,1,0,0,0,0,0,0)
 mat[8,]  <- c(0,0,1,0,0,0,0,0,0,0)
 mat[9,]  <- c(0,1,0,0,0,0,0,0,0,0)
 mat[10,] <- c(1,0,0,0,0,0,0,0,0,0)

 i <- 2

 set.seed(129)

 while( sum(mat) < 100 ) {
 # pick random cell
 rnum <- sample( which(mat < 1), 1 )
 mat[rnum] <- 1
 ##
 print(paste0("i =", i))
 print(paste0("rnum =", rnum))
 print(sum(mat))
 i = i + 1
 }

For some reason when sum(mat) == 99 there are several steps extra...I would assume that once i = 91 the while would stop but it continues past this. Can somone explain what I have done wrong...

If I change the while condition to

while( sum(mat) < 100 & length(which(mat < 1)) > 0 )

the issue remains..

Was it helpful?

Solution 2

See ?sample

Arguments:

       x: Either a vector of one or more elements from which to choose,
          or a positive integer.  See ‘Details.’

...

     If ‘x’ has length 1, is numeric (in the sense of ‘is.numeric’) and
     ‘x >= 1’, sampling _via_ ‘sample’ takes place from ‘1:x’.  _Note_
     that this convenience feature may lead to undesired behaviour when
     ‘x’ is of varying length in calls such as ‘sample(x)’.  See the
     examples.

In other words, if x in sample(x) is of length 1, sample returns a random number from 1:x. This happens towards the end of your loop, where there is just one 0 left in your matrix and one index is returned by which(mat < 1).

OTHER TIPS

Your problem is equivalent to randomly ordering the indices of a matrix that are equal to 0. You can do this in one line with sample(which(mat < 1)). I suppose if you wanted to get exactly the same sort of output, you might try something like:

set.seed(144)
idx <- sample(which(mat < 1))
for (i in seq_along(idx)) {
  print(paste0("i =", i))
  print(paste0("rnum =", idx[i]))
  print(sum(mat)+i)
}
# [1] "i =1"
# [1] "rnum =5"
# [1] 11
# [1] "i =2"
# [1] "rnum =70"
# [1] 12
# ...

The iteration repeats on level 99 because sample() behaves very differently when the first parameter is a vector of length 1 and when it is greater than 1. When it is length 1, it assumes you a random number from 1 to that number. When it has length >1, then you get a random number from that vector.

Compare

sample(c(99,100),1)

and

sample(c(100),1)

Of course, this is an inefficient way of filling your matrix. As @josilber pointed out, a single call to sample could do everything you need.

The issue comes from how sample and which do the sampling when you have only a single '0' value left.

For example, do this:

mat <- matrix(rep(1, 100), nrow = 10)

Now you have a matrix of all 1's. Now lets make two numbers 0:

mat[15]<-0
mat[18]<-0

and then sample

sample(which(mat<1))
[1] 18 15

by adding a size=1 argument you get one or the other

now lets try this:

mat[18]<-1
sample(which(mat<1))
[1]  3 13  8  2  4 14 11  9 10  5 15  7  1 12  6

Oops, you did not get [1] 15 . Instead what happens in only a single integer (15 in this case) is passed tosample. When you do sample(x) and x is an integer, it gives you a sample from 1:x with the integers in random order.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top