web-dev-qa-db-fra.com

Ajouter un vecteur à une matrice MATLAB vide

J'ai du code MATLAB qui insère des points à n dimensions (n> 1) dans une matrice (myPointMatrix) et qui réfléchit à la manière d'insérer le premier point.

À l'heure actuelle, le programme vérifie la taille de myPointMatrix avant d'insérer un point. S'il s'agit de 1x1, myPointMatrix est défini comme égal au point actuel. Sinon, le point actuel est ajouté. Cette instruction if- n'est vraie qu'une fois, mais elle est évaluée chaque fois que j'insère un point, ce qui est très très souvent.

Si vous supprimez if et essayez d’ajouter à myPointMatrix, MATLAB se plaint de manière compréhensible que les dimensions de la matrice ne sont pas cohérentes. Si vous supprimez à la fois l'instruction if- et l'inialisation de myPointMatrix = 0, MATLAB trouve myPointMatrix undefined. Aussi compréhensible.

Comment initialiser myPointMatrix afin de pouvoir supprimer l'instruction if-? Ou existe-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
16
AnnaR

Utilisez myPointMatrix = []; pour initialiser la matrice.

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

Il vaut alors mieux initialiser MyPointMatrix avec sa taille finale et insérer les points dans des positions données de la matrice.

14
AnnaR

Il existe plusieurs façons d’ajouter une matrice ou un vecteur à une matrice, vide ou non. Cela dépend beaucoup de la taille de la matrice et de la fréquence à laquelle vous allez l'ajouter. (Notez que les matrices clairsemées sont un animal totalement 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 uniquement à 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 assez élevé, beaucoup plus que je venais d'appeler directement Rand.

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

Les autres façons d'ajouter sont similaires dans le temps. Par exemple, vous pouvez également ajouter en indexant.

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

Ce sera similaire en termes de temps à l'ajout via la concaténation. Un fait intéressant à comprendre est que l’ajout de nouvelles lignes à un tableau est légèrement différent de l’ajout de nouvelles colonnes. Il faut un peu plus de temps pour ajouter une ligne qu'une colonne. Cela est dû à la manière dont les éléments sont stockés dans MATLAB. L'ajout d'une nouvelle ligne signifie que les éléments doivent être réellement 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 de toute opération d’ajout est que MATLAB doit réaffecter la mémoire requise pour A, et ce, CHAQUE FOIS que la taille de la matrice augmente. Étant donné que la taille de A augmente de façon linéaire, le temps total nécessaire augmente parallèlement avec n. Donc, si nous doublions la taille de n, le A développé dynamiquement prendra quatre fois plus de temps à construire. Ce comportement quadratique explique pourquoi les utilisateurs vous conseillent de pré-allouer vos tableaux MATLAB lorsqu'ils seront développés de manière dynamique. 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, consiste à pré-allouer A à sa taille finale. Ensuite, juste indexer.

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

Elapsed time is 0.156826 seconds.

Bien que cela soit bien meilleur que le tableau développé dynamiquement, il est encore bien pire qu’une utilisation vectorisée de Rand. Donc, dans la mesure du possible, utilisez la forme vectorisée de fonctions comme celle-ci.

Le problème est que, parfois, vous ne savez tout simplement pas combien d'éléments vous allez vous retrouver. Il existe encore plusieurs astuces pour éviter la mauvaise croissance du second degré.

Une astuce consiste à deviner la taille finale de A. Maintenant, utilisez l'indexation pour insérer de nouvelles valeurs dans A, mais surveillez attentivement le moment où les nouvelles entrées débordent des limites de A. Lorsque cela est sur le point d'arriver, DOUBLE la taille de A, en ajoutant un gros bloc de zéros à la fin. Maintenant, revenez à l'indexation de nouveaux éléments dans A. Conservez un décompte séparé du nombre d'éléments "ajoutés". À la toute fin de ce processus, supprimez les éléments inutilisés. Cela évite une grande partie du mauvais comportement du second degré, car seules quelques étapes annexes seront effectuées. (N'oubliez pas que vous doublez la taille de A lorsque vous devez effectuer un ajout.)

Une deuxième astuce consiste à utiliser des pointeurs. Bien 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 tableau développé. Pourquoi? Nous ne construisions qu'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, elle fonctionne toujours bien.

Un problème avec le tableau de cellules, est-il n'est pas terriblement efficace quand il y a des millions d'éléments à ajouter. C'est toujours une opération quadratique après tout, car nous agrandissons le tableau de 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 de la matrice de cellules comme ayant une taille modérément grande. Maintenant, utilisez l'indexation pour insérer de nouvelles lignes de A dans la cellule. Lorsque la cellule actuelle doit être agrandie avant la prochaine étape d'ajout, ajoutez simplement une nouvelle cellule au tableau de cellules. 

Il y a quelques années, cette discussion a eu lieu au sein du groupe de discussion MATLAB et plusieurs solutions allant dans ce sens ont été proposées. J'ai posté les solutions growdata & growdata2 sous forme de fichiers sur l'échange de fichiers MATLAB Central. Growdata2 a utilisé des poignées de fonction 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, l’utilisation de variables persistantes était un peu plus rapide.

tic
growdata
for i = 1:n
  growdata(Rand(1,3))
end
A = growdata;
toc

Elapsed time is 2.048584 seconds.

Depuis lors, l'implémentation des descripteurs de fonction s'est nettement améliorée dans MATLAB. Le descripteur de fonction est donc plus rapide.

L'un des avantages de ces systèmes est qu'ils n'entraîneront pas de pénalité de performances quadratiques, tout en permettant des millions d'étapes supplémentaires.

Eh bien, c’est sûrement plus d’informations que ce qui avait été demandé à l’origine lorsque la question a été posée. Peut-être que quelqu'un en tirera quelque chose.

28
user85109

Votre meilleure option consiste à pré-allouer la matrice et à utiliser une variable de boucle. Cela devrait être beaucoup 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
3
KennyMorton

Je crois que la solution que vous recherchez consiste à initialiser myPointMatrix sur une matrice de 0 ligne et 3 colonnes, c.-à-d.

myPointMatrix = zeros(0, 3);

Puis la première mission 

myPointMatrix = [myPointMatrix; tempPoint];

fonctionnera correctement, ainsi que les suivants. Une manière équivalente d’écrire la tâche est 

myPointMatrix(end+1,:) = tempPoint;

Cependant, gardez à l'esprit que développer une telle matrice n'est pas efficace et, comme le dit AnnaR, initialiser myPointMatrix avec une taille finale de if, si elle est connue, est une meilleure solution.

0
Fanfan
%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
0
Nima_k2

C'est ce dont tu as 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 effectuerez une opération non linéaire avec [x y z], avant de l’affecter. 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 veuille peut-être edit kron.m et remplacer un find par logical ... mais vous pouvez le faire vous-même, je suppose ...: D

0
user677656