Salut,
La première chose qui me pique les yeux, c'est de voir que tu essayes de créer... un vecteur de vecteur de double
Sais-tu qu'il est, la plupart du temps, tout à fait possible de réduire un tableau à deux dimensions, contenant nombre_de_lignes lignes et nombre_de_colonnes colonnes à un tableau à une seule dimension de nombre_de_lignes lignes * nombre_de_colonnes élément
L'accès à un élément donné se faisant alors en suivant la formule index_element_recherché = ligne_voulue * nombre_colonnes + colonne_voulue.
Tu as tout à y gagner.
D'abord, la gestion de ton tableau en est simplifiée : A partir du moment où tu as créé ton vecteur de nombre_de_lignes * nombre_de_colonnes, tu es sur de pouvoir représenter l'ensemble des éléments, sans avoir à t'inquiéter du reste.
Ensuite, cela te permet, pour autant que tu aies une fonction qui soit dédiée au calcul de l'index, de décider de modifier la manière dont les éléments sont organisés sans risquer de "tout casser" : ils peuvent, selon les besoins, aussi bien être organisés par ligne que par colonne, et le tout, de manière strictement transparente pour l'utilisateur (qui invoquera toujours une fonction proche de size_t toIndex(int ligne_voulue, int colonne_voulue).
Enfin, cela t'assurera d'avoir des données strictement contiguës en mémoire. Cela pourrait avoir pour conséquence d'éviter de nombreux cahe-miss, ou, si tu préfères, d'éviter que le processeur ne passe presque plus de temps à charger une page de cache qu'à accéder aux données qui s'y trouvent.
Dans le pire des cas, tu obtiendras des performances similaires à celles que tu obtiendrais avec un tableau à deux dimensions, mais, bien souvent, tu pourras observer un gain appréciable en termes de performances .
Malgré les apparences, ce n'est pas de l'optimisation prématurée, parce que, ainsi que je l'ai dit, cette approche arrive avec quelques avantages en termes de développement
Et puis, dans le code même de MegaMatrice, il y a certaines choses qui piquent tout autant les yeux.
vector<double> kdernieres(k);
Par exemple.
Bon, d'accord, c'est souvent une bonne idée que de décider de définir la taille d'un vecteur lorsqu'on le déclare. Mais le fait est que tu l'utilises directement après sous la forme de
kdernieres=Isoleleskdernieres(donnees,k);
Or, si j'en crois le code, la fonction Isoleleskdernieres va elle-même créer un tableau de k données qu'elle va s'occuper de remplir.
Ce n'est pas faux en soi, mais, si on y regarde d'un peu plus près, cette logique va:
- forcer l'allocation dynamique d'un espace mémoire suffisant pour permettre de représenter les k élément de kdernieres
- forcer (dans Isoleleskdernieres) l'allocation dynamique d'un espace mémoire suffisant pour permettre de représenter les k élément d'une variable temporaire
- intervertir (au niveau de l'opérateur = ) les membres (dont l'espace mémoire suffisant pour représenter le k élément) entre le vecteur renvoyé par Isoleleskdernieres (ou peut être une copie de ce vecteur) et kdernieres
- (juste au moment de quitter l'opérateur = ) détruire la variable temporaire (qui contient à ce moment là l'espace mémoire alloué à l'origine pour kdernieres) et donc forcer la libération de la mémoire
Pourquoi ne pas te "contenter" d'un code proche de
vector<double> kdernieres=Isoleleskdernieres(donnees,k);
Tu n'auras que des avantages:
- le code, ne perd absolument pas en clarté (au contraire, il est d'une certaine manière plus clair)
- kdernieres est correctement initialisé, mais en plus
- kdernieres est directement initialisé avec les données qui seront réellement utilisées.
- Tu pourras au passage profiter de l'élision de copie et, qui sait, peut être même de la sémantique de mouvement (si tu peux profiter des bienfaits de C++11)
En deux mots : ton code sera plus efficace et plus simple. Elle est pas belle la vie
Continuons... Je ne vois nulle part où la variable
int const nombrededonnees(donnees.size());
est utilisée
Elle est correctement initialisée, mais -- je suis peut être très fatigué -- je ne vois absolument pas à quoi elle sert. Si elle n'est effectivement pas utilisée, elle n'a strictement rien à faire dans le code .
On en vient maintenant à la logique proprement dite.
Comme je te l'ai fait remarquer, il est tout à fait possible de gérer avec un tableau à une dimension l'ensemble des éléments que l'on retrouverait dans un tableau à deux dimensions.
Nous pourrions donc avoir une logique proche de
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| std::vector<double> megamatrice(ligne * taillematrice); // ici, l'ordre n'a pas d'importance :
// ligne * taillematrice == taillematrice * ligne :D
for (int i(0) ; i < taillemegamatrice*ligne ; i+=ligne){ // on n'est absolument pas obligé d'incrémenter i...
// on peut tout à fait le faire varier du nombre d'éléments à "sauter" :D
megamatrice[i][1]=donnees[i+k-1]; // Pour les deux dernières lignes on a l'observation et l'observation+1 (nécéssaire pour la prévision)
megamatrice[i+2]=donnee[i+k];
/* simplifions nous la vie... std::vector vient avec une foule de constructeurs plus
* sympas les uns que les autres, dont un qui prend deux itérateurs (le premier sur l'élément
* qui correspond au premier élément du tableau à créé et le deuxième sur "ce qui suit" le
* dernier élément du tableau à créer).
* Ce constructeur nous permet tout à la fois de créer le vecteur kvaleursprecedantsi et
* de l'initialiser directement avec les données qui nous intéresseront :D
*
* Dans le cas présent, nous pouvons parfaitement utiliser les adresses des éléments
* à prendre en compte comme itérateur.
*/
std::vector<double> kvaleursprecedantsi(&donnees[i-k],&donnée[i]);//vérifier la logique pour etre sur ;-)
megamatrice[i]=Normeeuclidienne(kdernieres,kvaleursprecedantsi); // La première ligne de la matrice contient les distances euclidiennes
} |
Tu peux le remarquer, j'ai ajouté énormément de commentaires qui ne sont là que pour te permettre de comprendre ce que j'ai fait (et qui ne devraient jamais aparaitre dans un code de production ). Si tu enlèves ces commentaires, tu remarqueras que le code est tout à la fois plus lisible et finalement beaucoup plus simple :-D
Il ne me reste plus qu'une information à te donner en ce qui concerne l'utilisation du vecteur ainsi rempli :
Tu l'auras compris, les données relatives à chaque éléments sont toutes regroupées, ce qui fait que pour tout i plus petit que megamatrice.size() qui est égale à 0 ou un multiple de 3, tu as:
- megamatrice[i] correspond à la valeur renoyée par la fonction Normeeuclidienne
- megamatrice[i+1] correspond à l'observation -1
- megamatrice[i+2] correspond à l'oservation elle-même.
autrement dit, tu peux parfaitement envisager une logique qui se rapprocherait de
1 2 3 4 5 6 7
| for(int i = 0; i< megamatrice.size(), i+=3){
/* Voici les correspondance observées
megamatrice[i] --> Normeeuclidienne
megamatrice[i+1] --> donnees[i+k-1];
megamatrice[i+2] --> donnees[i+k];
*/
} |
Elle est pas belle la vie
Partager