From 41f6308b653d45ed499d6389fd98ee5f7c7fb71c Mon Sep 17 00:00:00 2001 From: Benicia Bounni Loubelo Date: Mon, 26 Oct 2026 23:31:26 +0100 Subject: [PATCH] Ajouter COMPTE_RENDU.md --- COMPTE_RENDU.md | 307 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 COMPTE_RENDU.md diff --git a/COMPTE_RENDU.md b/COMPTE_RENDU.md new file mode 100644 index 0000000..b760b8c --- /dev/null +++ b/COMPTE_RENDU.md @@ -0,0 +1,307 @@ +# 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 :** + +```text +index % time self children called name + +[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 :** + +```text +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] +----------------------------------------------- + +[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é. \ No newline at end of file