Files
2026_TD3_DEV51_Qualite_Algo/COMPTE_RENDU.md
T

15 KiB
Raw Blame History

Rapport d'optimisation

1. Mesurer

Avant

Indicateur Valeur
Clients distincts 2000
Somme des requêtes -2624578
Meilleure période jours 650 à 986 (3548830)
Paires opposées 5832
Façons de rendre 400 1960497
Empreinte ff68d260a7a58441

Temps : real 2.332 s, user 2.298 s, sys 0.004 s

Après

Indicateur Valeur
Clients distincts 2000
Somme des requêtes -2624578
Meilleure période jours 650 à 986 (3548830)
Paires opposées 5832
Façons de rendre 400 1960497
Empreinte ff68d260a7a58441

Temps : real 0.048 s, user 0.036 s, sys 0.004 s

Les résultats et l'empreinte sont identiques avant et après : le comportement est préservé, et le temps d'exécution passe de 2,332 s à 0,048 s.


2. Profiler

Profil gprof avant (flat profile)

Chaque échantillon compte pour 0,01 seconde.

% temps Cumulé (s) Self (s) Appels Self s/appel Total s/appel Fonction
50.66 8.00 8.00 1 8.00 8.00 trier_par_montant
12.76 10.02 2.02 1 2.02 2.02 nb_paires_opposees
12.41 11.97 1.96 30000 0.00 0.00 rechercher_par_id
9.97 13.55 1.57 1 1.57 1.57 nb_facons_rendre
6.33 14.55 1.00 6017 0.00 0.00 longueur
3.86 15.16 0.61 1 0.61 0.61 meilleure_periode
1.65 15.42 0.26 1 0.26 0.26 nb_clients_distincts
1.65 15.68 0.26 1 0.26 0.26 total_par_client
0.70 15.79 0.11 1 0.11 0.11 total_par_jour
0.00 15.79 0.00 149998 0.00 0.00 alea
0.00 15.79 0.00 30000 0.00 0.00 categorie
0.00 15.79 0.00 6017 0.00 0.00 ajouter_ligne
0.00 15.79 0.00 1 0.00 0.00 empreinte
0.00 15.79 0.00 1 0.00 0.00 generer_transactions

Call graph :

index % time    self  children    called     name
                                                 <spontaneous>
[1]    100.0    0.00   15.79                 main [1]
                8.00    0.00       1/1           trier_par_montant [2]
                2.02    0.00       1/1           nb_paires_opposees [3]
                1.96    0.00   30000/30000       rechercher_par_id [4]
                1.57    0.00       1/1           nb_facons_rendre [5]
                0.00    1.00    6017/6017        ajouter_ligne [6]
                0.61    0.00       1/1           meilleure_periode [8]
                0.26    0.00       1/1           nb_clients_distincts [9]
                0.26    0.00       1/1           total_par_client [10]
                0.11    0.00       1/1           total_par_jour [11]
                0.00    0.00   30000/30000       categorie [13]
                0.00    0.00       1/1           generer_transactions [15]
                0.00    0.00       1/1           empreinte [14]
-----------------------------------------------
                8.00    0.00       1/1           main [1]
[2]     50.7    8.00    0.00       1         trier_par_montant [2]
-----------------------------------------------
                2.02    0.00       1/1           main [1]
[3]     12.8    2.02    0.00       1         nb_paires_opposees [3]
-----------------------------------------------
                1.96    0.00   30000/30000       main [1]
[4]     12.4    1.96    0.00   30000         rechercher_par_id [4]
-----------------------------------------------
                             266723738             nb_facons_rendre [5]
                1.57    0.00       1/1           main [1]
[5]     10.0    1.57    0.00       1+266723738 nb_facons_rendre [5]
                             266723738             nb_facons_rendre [5]
-----------------------------------------------
                0.00    1.00    6017/6017        main [1]
[6]      6.3    0.00    1.00    6017         ajouter_ligne [6]
                1.00    0.00    6017/6017        longueur [7]
-----------------------------------------------
                1.00    0.00    6017/6017        ajouter_ligne [6]
[7]      6.3    1.00    0.00    6017         longueur [7]
-----------------------------------------------
                0.61    0.00       1/1           main [1]
[8]      3.9    0.61    0.00       1         meilleure_periode [8]
-----------------------------------------------
                0.26    0.00       1/1           main [1]
[9]      1.6    0.26    0.00       1         nb_clients_distincts [9]
-----------------------------------------------
                0.26    0.00       1/1           main [1]
[10]     1.6    0.26    0.00       1         total_par_client [10]
-----------------------------------------------
                0.11    0.00       1/1           main [1]
[11]     0.7    0.11    0.00       1         total_par_jour [11]
-----------------------------------------------
                0.00    0.00  149998/149998      generer_transactions [15]
[12]     0.0    0.00    0.00  149998         alea [12]
-----------------------------------------------
                0.00    0.00   30000/30000       main [1]
[13]     0.0    0.00    0.00   30000         categorie [13]
-----------------------------------------------
                0.00    0.00       1/1           main [1]
[14]     0.0    0.00    0.00       1         empreinte [14]
-----------------------------------------------
                0.00    0.00       1/1           main [1]
[15]     0.0    0.00    0.00       1         generer_transactions [15]
                0.00    0.00  149998/149998      alea [12]
-----------------------------------------------

Question : les fonctions les plus coûteuses ont-elles les pires complexités ?

Non, pas systématiquement. F1 et F7 sont parmi les fonctions les plus coûteuses et ont une complexité élevée en O(n²). Cependant, F5, malgré sa complexité en O(J³), ne représente que 3,86 % du temps d'exécution. Les fonctions les plus coûteuses n'ont donc pas nécessairement les pires complexités algorithmiques.

Profil gprof après (flat profile)

Chaque échantillon compte pour 0,01 seconde.

% temps Cumulé (s) Self (s) Appels Self ms/appel Total ms/appel Fonction
100.00 0.30 0.30 1 300.00 300.00 total_par_jour
0.00 0.30 0.00 149998 0.00 0.00 alea
0.00 0.30 0.00 30000 0.00 0.00 categorie
0.00 0.30 0.00 30000 0.00 0.00 rechercher_par_id
0.00 0.30 0.00 15066 0.00 0.00 categorie_negative
0.00 0.30 0.00 14934 0.00 0.00 categorie_positive
0.00 0.30 0.00 6017 0.00 0.00 ajouter_ligne
0.00 0.30 0.00 1 0.00 0.00 empreinte
0.00 0.30 0.00 1 0.00 0.00 generer_transactions
0.00 0.30 0.00 1 0.00 0.00 meilleure_periode
0.00 0.30 0.00 1 0.00 0.00 nb_clients_distincts
0.00 0.30 0.00 1 0.00 0.00 nb_facons_rendre
0.00 0.30 0.00 1 0.00 0.00 nb_paires_opposees
0.00 0.30 0.00 1 0.00 0.00 total_par_client
0.00 0.30 0.00 1 0.00 0.00 trier_par_montant

Call graph :

index % time    self  children    called     name
                0.30    0.00       1/1           main [2]
[1]    100.0    0.30    0.00       1         total_par_jour [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    0.30                 main [2]
                0.30    0.00       1/1           total_par_jour [1]
                0.00    0.00   30000/30000       rechercher_par_id [5]
                0.00    0.00   30000/30000       categorie [4]
                0.00    0.00    6017/6017        ajouter_ligne [8]
                0.00    0.00       1/1           generer_transactions [10]
                0.00    0.00       1/1           nb_clients_distincts [12]
                0.00    0.00       1/1           total_par_client [15]
                0.00    0.00       1/1           meilleure_periode [11]
                0.00    0.00       1/1           nb_paires_opposees [14]
                0.00    0.00       1/1           trier_par_montant [16]
                0.00    0.00       1/1           nb_facons_rendre [13]
                0.00    0.00       1/1           empreinte [9]
-----------------------------------------------
                0.00    0.00  149998/149998      generer_transactions [10]
[3]      0.0    0.00    0.00  149998         alea [3]
-----------------------------------------------
                0.00    0.00   30000/30000       main [2]
[4]      0.0    0.00    0.00   30000         categorie [4]
                0.00    0.00   15066/15066       categorie_negative [6]
                0.00    0.00   14934/14934       categorie_positive [7]
-----------------------------------------------
                0.00    0.00   30000/30000       main [2]
[5]      0.0    0.00    0.00   30000         rechercher_par_id [5]
-----------------------------------------------
                0.00    0.00   15066/15066       categorie [4]
[6]      0.0    0.00    0.00   15066         categorie_negative [6]
-----------------------------------------------
                0.00    0.00   14934/14934       categorie [4]
[7]      0.0    0.00    0.00   14934         categorie_positive [7]
-----------------------------------------------
                0.00    0.00    6017/6017        main [2]
[8]      0.0    0.00    0.00    6017         ajouter_ligne [8]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[9]      0.0    0.00    0.00       1         empreinte [9]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[10]     0.0    0.00    0.00       1         generer_transactions [10]
                0.00    0.00  149998/149998      alea [3]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[11]     0.0    0.00    0.00       1         meilleure_periode [11]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[12]     0.0    0.00    0.00       1         nb_clients_distincts [12]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[13]     0.0    0.00    0.00       1         nb_facons_rendre [13]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[14]     0.0    0.00    0.00       1         nb_paires_opposees [14]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[15]     0.0    0.00    0.00       1         total_par_client [15]
-----------------------------------------------
                0.00    0.00       1/1           main [2]
[16]     0.0    0.00    0.00       1         trier_par_montant [16]
-----------------------------------------------

3. Calculer

Légende : n = nombre de transactions, C = nombre de clients, J = nombre de jours, S = somme à rendre, k = nombre de types de pièces, L = taille du rapport.

Fonction M (complexité cyclomatique) avant M (complexité algorithmique) avant M (complexité algorithmique) après
F1 n² n log n
F2 n² n + C
F3 C·n n + C
F4 J·n n + J
F5 J³ J
F6 n 1
F7 n² n log n
F8 S·k S·k
F9 M = 14 1 M ≤ 10
F10 L L

4. Optimisation

F1 : tri des transactions

Optimisation : utilisation de qsort() avec un comparateur (montant croissant, puis identifiant).

Justification : la fonction utilise qsort() pour trier les n transactions par montant croissant, puis par identifiant en cas d'égalité. Le tri nécessite en moyenne O(n log n) opérations.

F2 : nombre de clients distincts

Optimisation : tableau vus indexé par client, une seule passe sur les transactions.

Justification : la fonction parcourt les n transactions une seule fois. Pour chaque transaction, elle consulte et met éventuellement à jour le tableau des clients déjà rencontrés. L'initialisation du tableau nécessite O(C) opérations et le parcours des transactions O(n).

F3 : total par client

Optimisation : une seule passe, le montant est ajouté directement à la case du client.

Justification : chaque transaction ne concerne qu'un seul client, donc une seule passe suffit : on ajoute son montant directement à la case de son client.

F4 : total par jour

Optimisation : même principe que F3, appliqué aux jours.

Justification : même raisonnement que F3, avec les jours à la place des clients.

F5 : meilleure période

Optimisation : sommes préfixes P et suffixes maximaux, parcours en une passe.

Justification : l'original testait toutes les périodes (i, j) et recalculait chaque somme avec une troisième boucle : O(J³). Avec les sommes préfixes, la somme de la période [i, j] vaut P[j+1] − P[i], soit un calcul en O(1). Pour chaque fin j, la meilleure somme s'obtient en retranchant le plus petit préfixe déjà rencontré, ce qui donne le maximum en une passe.

F6 : recherche par identifiant

Optimisation : tableau d'index permettant l'accès direct à une transaction par son ID.

Justification : F6 passe de O(n) à O(1) grâce à un tableau d'index permettant d'accéder directement à une transaction par son ID. La création de l'index coûte O(n), mais les recherches sont beaucoup plus rapides.

F7 : paires de montants opposés

Optimisation : copie des montants, tri, puis parcours à deux indices (gauche / droite), avec gestion des valeurs répétées. Un repli en O(n²) est conservé en cas d'échec d'allocation mémoire.

Justification : la complexité passe de O(n²) à O(n log n) en triant les montants puis en utilisant deux indices pour rechercher les paires opposées.

F8 : façons de rendre la monnaie

Optimisation : programmation dynamique sur un tableau dp avec les pièces {1, 2, 5, 10, 20, 50, 100, 200}.

Justification : la programmation dynamique évite de recalculer plusieurs fois les mêmes sous-problèmes. La complexité passe d'une approche récursive exponentielle à O(S·k), où S est la somme à rendre et k le nombre de types de pièces.

F9 : catégorie d'une transaction

Optimisation : découpage en trois fonctions : categorie, categorie_negative et categorie_positive.

Justification : la fonction initiale a été découpée en trois fonctions. Ce découpage répartit les conditions entre plusieurs fonctions afin de limiter le nombre de points de décision dans chacune d'elles. Avec la méthode de calcul retenue, on obtient :

  • categorie : M = 4 ;
  • categorie_negative : M = 4 ;
  • categorie_positive : M = 5, en comptant l'opérateur logique && comme un point de décision supplémentaire.

Ainsi, chaque fonction respecte individuellement la contrainte M ≤ 10.

F10 : ajout d'une ligne au rapport

Optimisation : mémorisation de la position de fin du rapport (fin), copie directe de la ligne à cette position.

Justification : F10 optimise ajouter_ligne en mémorisant la position de fin du rapport grâce à fin, évitant ainsi de reparcourir tout le texte à chaque ajout. La complexité totale passe de potentiellement O(n²) à O(n), où n est la taille totale du rapport. L'empreinte reste identique, confirmant que le résultat est préservé.