2009-04-23 18 views
14

J'ai un code MATLAB qui insère des points n-dimensionnels (n> 1) dans une matrice (myPointMatrix) et je réfléchis à la façon d'insérer le premier point.Ajout d'un vecteur à une matrice MATLAB vide

À l'heure actuelle, le programme vérifie la taille de myPointMatrix avant d'insérer un point. Si c'est 1x1, myPointMatrix est défini égal au point actuel. Sinon, le point actuel est ajouté. Cet état if n'est vrai qu'une seule fois, mais est évalué chaque fois que j'insère un point, ce qui est très très souvent.

En supprimant le if et en essayant d'ajouter à myPointMatrix, MATLAB se plaint de manière compréhensible que les dimensions de la matrice ne soient pas cohérentes. En supprimant à la fois l'état if et l'initialisation de myPointMatrix = 0, MATLAB trouve myPointMatrix non défini. Aussi compréhensible. Comment initialiser myPointMatrix pour que je puisse supprimer l'état if? Ou y a-t-il une autre solution intelligente?

myPointMatrix = 0; 
for x=0:limit 
    for y=0:limit 
     for z=0:limit 
      tempPoint = [x y z]; 
      if (length(myPointMatrix) == 1) 
       myPointMatrix = tempPoint; 
      else 
       myPointMatrix = [myPointMatrix; tempPoint]; 
      end 
     end 
    end 
end 

Répondre

13

Utilisez myPointMatrix = []; pour initialiser la matrice.

Plus le myPointMatrix est grand, plus l'ajout sera lent. Cela devient de plus en plus lent, car à chaque fois que vous ajoutez un point, matlab alloue une nouvelle matrice de la nouvelle taille et copie les informations de votre ancienne matrice + votre nouveau point dans la nouvelle matrice.

Il est alors préférable d'initialiser MyPointMatrix avec sa taille finale, et d'insérer les points dans des positions données dans la matrice.

+2

+1 pour votre dernière phrase. C'est le moyen le plus efficace d'initialiser une matrice dans MATLAB. –

3

Votre meilleure option est de pré-aligner la matrice et d'utiliser une variable de boucle. Cela devrait être nettement plus rapide.

limit = 9; 
myPointMatrix = nan((limit+1)^3,3); 

loopVar = 1; 
for x=0:limit 
    for y=0:limit 
     for z=0:limit 
      myPointMatrix(loopVar,:) = [x y z]; 
      loopVar = loopVar + 1; 
     end 
    end 
end 
0

Je crois que la solution que vous recherchez est d'initialiser le myPointMatrix à une matrice avec 0 lignes et 3 colonnes, à savoir

myPointMatrix = zeros(0, 3); 

Ensuite, la première affectation

myPointMatrix = [myPointMatrix; tempPoint]; 

va fonctionner correctement, ainsi que les suivants. Une façon équivalente à écrire l'affectation est

myPointMatrix(end+1,:) = tempPoint; 

Cependant, gardez à l'esprit que la croissance d'une matrice comme ce n'est pas efficace et, comme le dit Annar, l'initialisation myPointMatrix avec ifs taille finale, si elle est connue, est une meilleure solution.

28

Il existe plusieurs façons d'ajouter une matrice ou un vecteur à une matrice, vide ou non. Beaucoup dépend de la taille de la matrice, et à quelle fréquence vous allez faire l'appendice. (Notez que les matrices creuses sont un animal entièrement différent, elles doivent être traitées séparément.)

Le schéma simple utiliserait la concaténation. Par exemple, je vais créer un tableau aléatoire. Bien que je sache qu'un appel à rand serait la bonne solution ici, je le fais seulement à des fins de comparaison.

n = 10000; 
tic 
A = []; 
for i = 1:n 
    Ai = rand(1,3); 
    A = [A;Ai]; 
end 
toc 

Elapsed time is 9.537194 seconds. 

Voir que le temps requis était raisonnablement élevé, beaucoup plus que si je venais d'appeler rand directement.

tic,rand(n,3);toc 
Elapsed time is 0.008036 seconds. 

D'autres façons d'ajouter sont similaires dans le temps. Par exemple, vous pouvez également ajouter par indexation.

A = []; 
A(end+1,:) = rand(1,3); 
A 
A = 
     0.91338  0.63236  0.09754 

Ce sera similaire en termes de temps par concaténation annexant. Un fait intéressant à comprendre est que l'ajout de nouvelles lignes à un tableau est subtilement différent de l'ajout de nouvelles colonnes. Il faut un peu plus de temps pour ajouter une ligne qu'une colonne. C'est à cause de la façon dont les éléments sont stockés dans MATLAB. L'ajout d'une nouvelle ligne signifie que les éléments doivent effectivement être mélangés en mémoire.

A = zeros(10000,3); 
B = zeros(3,10000); 

tic,for i = 1:100,A(end+1,:) = rand(1,3);end,toc 
Elapsed time is 0.124814 seconds. 

tic,for i = 1:100,B(:,end+1) = rand(3,1);end,toc 
Elapsed time is 0.116209 seconds. 

Le problème avec toute opération append tout est que Matlab doit réallouer la mémoire requise pour A, et ce, chaque fois que la matrice croît en taille. Puisque la taille de A augmente linéairement, le temps global nécessaire croît quadratiquement avec n. Donc, si nous devions doubler la taille de n, le A développé dynamiquement prendra quatre fois plus de temps à construire. Ce comportement quadratique est la raison pour laquelle les gens vous demandent de pré-allouer vos tableaux MATLAB quand ils seront développés dynamiquement. En fait, si vous regardez les drapeaux mlint dans l'éditeur, MATLAB vous avertit quand cela se produit.

Une meilleure solution, si vous connaissez la taille finale de A, est de pré-allouer A à sa taille finale. Ensuite, il suffit index.

tic 
A = zeros(n,3); 
for i = 1:n 
    A(i,:) = rand(1,3); 
end 
toc 

Elapsed time is 0.156826 seconds. 

Bien que ce soit beaucoup mieux que le tableau augmenté de façon dynamique, il est encore bien pire qu'une utilisation vectorisée de rand. Donc, dans la mesure du possible, utilisez la forme vectorisée des fonctions comme celle-ci. Le problème est, parfois vous ne savez tout simplement pas combien d'éléments vous finirez avec. Il y a encore plusieurs astuces que l'on peut utiliser pour éviter la croissance quadratique désagréable. Une astuce consiste à faire une estimation de la taille finale de A. Maintenant, utilisez l'indexation pour insérer de nouvelles valeurs dans A, mais surveillez attentivement quand les nouvelles entrées déborderont sur les limites de A. pour arriver, DOUBLE la taille de A, en ajoutant un gros bloc de zéros à la fin. Revenez maintenant à l'indexation des nouveaux éléments dans A. Gardez un compte séparé du nombre d'éléments qui ont été "ajoutés". À la toute fin de ce processus, supprimez les éléments inutilisés. Cela évite une bonne partie du comportement quadratique désagréable, car seules quelques étapes d'ajout seront possibles. (Rappelez-vous que vous doublez la taille de A lorsque vous devez faire un ajout.)

Une deuxième astuce consiste à utiliser des pointeurs. Alors que MATLAB n'offre pas vraiment beaucoup de possibilités en termes de pointeurs, un tableau de cellules est un pas dans cette direction.

tic 
C = {}; 
for i = 1:n 
    C{end+1} = rand(1,3); 
end 
A = cat(1,C{:}); 
toc 

Elapsed time is 3.042742 seconds. 

Cela a pris moins de temps à accomplir que le réseau développé. Pourquoi? Nous construisions seulement un tableau de pointeurs sur les cellules. Une bonne chose à ce sujet est que si chaque étape d'ajout a un nombre variable de lignes, cela fonctionne toujours bien.

Un problème avec le tableau de cellules, c'est qu'il n'est pas très efficace lorsqu'il y a des MILLIONS d'éléments à ajouter. Après tout, c'est encore une opération quadratique, car nous élargissons le tableau des pointeurs à chaque étape.

Une solution à ce problème consiste à utiliser un amalgame des deux styles présentés ci-dessus. Ainsi, définissez chaque cellule du tableau de cellules pour qu'elle soit de taille modérément grande.Utilisez maintenant l'indexation pour insérer de nouvelles lignes de A dans la cellule. Lorsque la cellule actuelle doit être agrandie à la prochaine étape d'ajout, ajoutez simplement une nouvelle cellule au tableau de cellules.

Il y a quelques années, cette discussion a été soulevée sur le groupe de discussion MATLAB, et plusieurs solutions dans ce sens ont été proposées. J'ai posté les solutions growdata & growdata2 en tant que fichiers sur l'échange de fichiers central MATLAB. Growdata2 fonction utilisée poignées pour résoudre le problème:

tic 
Ahandle = growdata2; 
for i = 1:n 
    Ahandle(rand(1,3)) 
end 
% unpack the object into a normal array 
A = Ahandle(); 
toc 

Elapsed time is 1.572798 seconds. 

À l'époque, il était une approche un peu plus rapide d'utiliser des variables persistantes. Depuis lors, l'implémentation des poignées de fonction s'est clairement améliorée dans MATLAB, donc la poignée de fonction est maintenant plus rapide.

Une des caractéristiques de ces schémas est qu'ils n'auront pas de pénalité de performance quadratique, tout en permettant des millions d'étapes d'ajout.

Eh bien, c'est certainement plus d'informations que ce qui a été demandé à l'origine lorsque la question a été posée. Peut-être que quelqu'un s'en sortira quand même.

0

C'est ce que vous avez besoin

myPointMatrix=[]; 
for x=0:limit 
for y=0:limit 
for x=0:limit 
    myPointMatrix(:,end+1)=[x y z]; 
end 
end 
end 

mais seulement dans le cas où vous faites une opération non linéaire avec [x y z], avant de l'assigner. Sinon, vous pouvez écrire les lignes ci-dessus comme suit:

myPointMatrix=[]; 
myPointMatrix(1,:)=kron([1:limit],ones(1,limit^2)); 
myPointMatrix(2,:)=kron([1:limit^2],ones(1,limit)); 
myPointMatrix(3,:)=kron(ones(1,limit^2),[1:limit]); 

Ce qui précède est entièrement vectorisé, bien que l'on pourrait vouloir edit kron.m et remplacer certains find avec logical ... mais vous pouvez faire vous-même, je suppose .. .: D

0
%appending to matlab array "f": 

lfg=[697 770 852 941]; 
hfg=[1209 1336 1477]; 
f=[]; 
for i=1:4, 
    for j=1:3, 
     %f = [ f [lfg(i);hfg(j)] ]; 
     append(f , [lfg(i);hfg(j)]); 
    end 
end 
f 
Questions connexes