2014-04-30 3 views
3

Soit A une matrice n * n diagonale. Dis, n = 5:Meilleure façon de randomiser une matrice diagonale binaire, en gardant les 1 en diagonale

A <- diag(1, 5) 
A 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 1 0 0 0 0 
[2,] 0 1 0 0 0 
[3,] 0 0 1 0 0 
[4,] 0 0 0 1 0 
[5,] 0 0 0 0 1 

J'ai besoin randomiser A par des rangées, en gardant les 1 hors de la diagonale. J'ai trouvé cette solution:

n <- 5 
count <- c(1:n) 
for (i in count) { 
    while (A[count[i], count[i]] == 1) { #avoid 1 in diagonal 
    A[count[i],] <- sample(A[count[i],]) #permutes ith row 
    } 
} 

mais je suis assez sûr qu'il devrait y avoir une façon plus efficace de le faire. Merci de votre attention.

+0

Voulez-vous dire simplement que vous voulez un seul dans chaque rangée, mais pas sur la diagonale? – jbaums

+0

Correct. C'est exactement le point. – JABalbuena

+0

Ne voulez-vous pas aussi un '1' dans chaque colonne? Je vois que la réponse @jbaums ne conserve pas cette propriété. – flodel

Répondre

5

Vous pouvez échantillonner un nombre aléatoire de la séquence entière 1:n, n fois, mais exclure à chaque fois le numéro de cellule correspondant à la diagonale.

Une façon de faire est la suivante:

n <- 5 
rnd <- sample(n-1, repl=T) 
i <- c(rnd + (rnd >= seq_len(n-1)) * 1, sample(n-1, 1)) 

i 
# [1] 3 3 4 5 1 

Ici, chaque élément de i est empêché d'être égal à l'indice de l'élément (par exemple, élément 1 ne peut pas être 1, élément 2 ne peut pas être 2, etc. .). Nous pouvons considérer chaque élément de i comme la colonne sélectionnée pour chaque ligne à tour de rôle.

Ensuite, nous a mis en place une matrice de zéros, et (pour l'exemple ci-dessus) remplir les cellules [1, 3], [2, 3], [3, 4], [4, 5] et [5, 1] à 1.

m <- matrix(0, nc=n, nr=n) 
m[cbind(seq_len(n), i)] <- 1 

m 
#  [,1] [,2] [,3] [,4] [,5] 
# [1,] 0 0 1 0 0 
# [2,] 0 0 1 0 0 
# [3,] 0 0 0 1 0 
# [4,] 0 0 0 0 1 
# [5,] 1 0 0 0 0 

La dernière ligne de code utilise la matrice subsetting pour sous-ensembles de la matrice m aux cellules pertinentes.


EDIT

Afin de faire en sorte que chaque colonne et chaque ligne ont une seule 1, et que ceux-ci sont maintenus hors de la diagonale, l'approche vectorisé suivante fonctionne. L'astuce ici est de permuter le vecteur n-1, et de considérer chaque élément de ce vecteur permuté comme l'indice, pour chaque ligne, des n-1 éléments non-diagonaux que nous affecterons 1. Nous vérifions ensuite, pour chaque élément de notre vecteur permuté, si la valeur est inférieure à l'indice de la diagonale de la ligne correspondante. Si c'est le cas, nous laissons l'élément tel quel, sinon nous ajoutons 1. Cela détermine les indices de colonne pour les premières lignes n-1. L'index de la dernière ligne est alors simplement l'index de la colonne qui n'a pas encore de 1.

n <- 5 
rnd <- sample(n-1) 

i <- union(rnd + (rnd >= seq_len(n-1)) * 1, seq_len(n)) 

m <- matrix(0, nc=n, nr=n) 
m[cbind(seq_len(n), i)] <- 1 

m 
#  [,1] [,2] [,3] [,4] [,5] 
# [1,] 0 0 1 0 0 
# [2,] 0 0 0 0 1 
# [3,] 0 0 0 1 0 
# [4,] 1 0 0 0 0 
# [5,] 0 1 0 0 0 
+0

Merci beaucoup. Il fait exactement ce que je voulais – JABalbuena

+0

Après avoir ajouté la deuxième partie de cette solution, j'ai réalisé qu'une approche similaire peut être appliquée à la première partie. Je l'ai mis à jour en conséquence car c'est beaucoup plus efficace. – jbaums

+0

Merci encore. Tu es génial. – JABalbuena

1

Je ne sais pas c'est le meilleur ou le plus élégant, mais vous pouvez choisir un élément aléatoire (mais non en diagonale) avec laquelle pour remplacer l'élément en diagonale sur chaque ligne, puis attribuez-lui les non-diagonales avec le reste :

count <- c(1:n) 
for (i in count) { 
    off <- sample(count[-i],1) 
    perm <- sample(count[-off]) 
    tmp <- A[i,off] 
    A[i, count[-i]] <- A[i,perm] 
    A[i,i] <- tmp 
} 
+0

Merci beaucoup pour votre publication. Je l'ai essayé et il semble fonctionner comme il se doit. Je préfère quand même la deuxième réponse car elle évite la boucle for. – JABalbuena

+0

Pas de problème - soyez conscient que tel qu'il est écrit, celui-ci fonctionnera pour les matrices diagonales générales, pas seulement les matrices d'identité - l'autre, plutôt plus élégante, nécessitera la dernière modification du <- 1' –

+0

Je vois votre point et ont donné un vote à votre réponse. Merci encore. – JABalbuena

Questions connexes