Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 41f6308b65 | |||
| 19a8770b2a | |||
| 3f301c825b | |||
| 39d184707a | |||
| 2a9ee49204 | |||
| 7390921cdc | |||
| 39d33fd2b6 | |||
| 78abd1dc3b | |||
| ca02726bb8 | |||
| 3cbbca5735 | |||
| 6eb65d3903 | |||
| 60d0a676f1 |
+307
@@ -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
|
||||
<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 :**
|
||||
|
||||
```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]
|
||||
-----------------------------------------------
|
||||
<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é.
|
||||
@@ -71,47 +71,57 @@ Transaction *generer_transactions(int n)
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* F1 : trie les transactions par montant croissant, puis par id */
|
||||
int comparer_transactions(const void *a, const void *b)
|
||||
{
|
||||
const Transaction *t1 = a;
|
||||
const Transaction *t2 = b;
|
||||
|
||||
if (t1->montant < t2->montant)
|
||||
return -1;
|
||||
|
||||
if (t1->montant > t2->montant)
|
||||
return 1;
|
||||
|
||||
if (t1->id < t2->id)
|
||||
return -1;
|
||||
|
||||
if (t1->id > t2->id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trier_par_montant(Transaction *t, int n)
|
||||
{
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
for (int j = 0; j < n - 1 - i; j++) {
|
||||
if (t[j].montant > t[j + 1].montant ||
|
||||
(t[j].montant == t[j + 1].montant && t[j].id > t[j + 1].id)) {
|
||||
Transaction tmp = t[j];
|
||||
t[j] = t[j + 1];
|
||||
t[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
qsort(t, n, sizeof(Transaction), comparer_transactions);
|
||||
}
|
||||
|
||||
|
||||
/* F2 : nombre de clients ayant au moins une transaction */
|
||||
int nb_clients_distincts(const Transaction *t, int n)
|
||||
{
|
||||
int nb = 0;
|
||||
int vus[NB_CLIENTS] = {0};
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int deja_vu = 0;
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (t[j].client == t[i].client) {
|
||||
deja_vu = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!deja_vu)
|
||||
int client = t[i].client;
|
||||
|
||||
if (!vus[client]) {
|
||||
vus[client] = 1;
|
||||
nb++;
|
||||
}
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
|
||||
/* F3 : solde total de chaque client */
|
||||
void total_par_client(const Transaction *t, int n, long *totaux)
|
||||
{
|
||||
for (int c = 0; c < NB_CLIENTS; c++) {
|
||||
for (int c = 0; c < NB_CLIENTS; c++)
|
||||
totaux[c] = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
if (t[i].client == c)
|
||||
totaux[c] += t[i].montant;
|
||||
}
|
||||
for (int i = 0; i < n; i++)
|
||||
totaux[t[i].client] += t[i].montant;
|
||||
}
|
||||
|
||||
/* F4 : solde total de chaque jour */
|
||||
@@ -128,110 +138,194 @@ void total_par_jour(const Transaction *t, int n, long *totaux)
|
||||
/* F5 : période de jours consécutifs dont la somme des soldes est maximale */
|
||||
long meilleure_periode(const long *totaux, int nb_jours, int *debut, int *fin)
|
||||
{
|
||||
long P[NB_JOURS + 1];
|
||||
long sufmax[NB_JOURS + 1];
|
||||
|
||||
P[0] = 0;
|
||||
for (int k = 0; k < nb_jours; k++)
|
||||
P[k + 1] = P[k] + totaux[k];
|
||||
|
||||
long min_prefixe = P[0];
|
||||
long meilleur = totaux[0];
|
||||
*debut = 0;
|
||||
*fin = 0;
|
||||
for (int i = 0; i < nb_jours; i++) {
|
||||
for (int j = i; j < nb_jours; j++) {
|
||||
long somme = 0;
|
||||
for (int k = i; k <= j; k++)
|
||||
somme += totaux[k];
|
||||
if (somme > meilleur) {
|
||||
meilleur = somme;
|
||||
*debut = i;
|
||||
*fin = j;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < nb_jours; j++) {
|
||||
if (P[j] < min_prefixe) min_prefixe = P[j];
|
||||
if (P[j + 1] - min_prefixe > meilleur)
|
||||
meilleur = P[j + 1] - min_prefixe;
|
||||
}
|
||||
|
||||
sufmax[nb_jours - 1] = P[nb_jours];
|
||||
for (int i = nb_jours - 2; i >= 0; i--)
|
||||
sufmax[i] = P[i + 1] > sufmax[i + 1] ? P[i + 1] : sufmax[i + 1];
|
||||
|
||||
int i = 0;
|
||||
while (sufmax[i] - P[i] != meilleur) i++;
|
||||
int j = i;
|
||||
while (P[j + 1] - P[i] != meilleur) j++;
|
||||
|
||||
*debut = i;
|
||||
*fin = j;
|
||||
return meilleur;
|
||||
}
|
||||
|
||||
/* F6 : retrouve une transaction à partir de son id */
|
||||
const Transaction *rechercher_par_id(const Transaction *t, int n, int id)
|
||||
/* F6 : retrouve une transaction à partir de son id en O(1) */
|
||||
const Transaction *rechercher_par_id(const Transaction *t, const int *index, int n, int id)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
if (t[i].id == id)
|
||||
return &t[i];
|
||||
return NULL;
|
||||
if (id < 0 || id >= n)
|
||||
return NULL;
|
||||
return &t[index[id]];
|
||||
}
|
||||
|
||||
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */
|
||||
/* Comparaison de montants pour qsort */
|
||||
static int comparer_montants(const void *a, const void *b)
|
||||
{
|
||||
long x = *(const long *)a;
|
||||
long y = *(const long *)b;
|
||||
if (x < y) return -1;
|
||||
if (x > y) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* F7 optimisée : compte les paires de montants opposés (a + b = 0) */
|
||||
long nb_paires_opposees(const Transaction *t, int n)
|
||||
{
|
||||
long nb = 0;
|
||||
if (n < 2)
|
||||
return 0;
|
||||
|
||||
long *montants = malloc(n * sizeof(long));
|
||||
if (montants == NULL) {
|
||||
/* En cas d'échec d'allocation, conserve un résultat correct. */
|
||||
long nb = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
for (int j = i + 1; j < n; j++)
|
||||
if (t[i].montant + t[j].montant == 0)
|
||||
nb++;
|
||||
return nb;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
for (int j = i + 1; j < n; j++)
|
||||
if (t[i].montant + t[j].montant == 0)
|
||||
nb++;
|
||||
montants[i] = t[i].montant;
|
||||
|
||||
qsort(montants, n, sizeof(long), comparer_montants);
|
||||
|
||||
long nb = 0;
|
||||
int gauche = 0;
|
||||
int droite = n - 1;
|
||||
|
||||
while (gauche < droite) {
|
||||
long somme = montants[gauche] + montants[droite];
|
||||
|
||||
if (somme < 0) {
|
||||
gauche++;
|
||||
} else if (somme > 0) {
|
||||
droite--;
|
||||
} else if (montants[gauche] == montants[droite]) {
|
||||
long nombre = droite - gauche + 1;
|
||||
nb += nombre * (nombre - 1) / 2;
|
||||
break;
|
||||
} else {
|
||||
long valeur_gauche = montants[gauche];
|
||||
long valeur_droite = montants[droite];
|
||||
long compte_gauche = 0;
|
||||
long compte_droite = 0;
|
||||
|
||||
while (gauche <= droite && montants[gauche] == valeur_gauche) {
|
||||
compte_gauche++;
|
||||
gauche++;
|
||||
}
|
||||
while (droite >= gauche && montants[droite] == valeur_droite) {
|
||||
compte_droite++;
|
||||
droite--;
|
||||
}
|
||||
nb += compte_gauche * compte_droite;
|
||||
}
|
||||
}
|
||||
|
||||
free(montants);
|
||||
return nb;
|
||||
}
|
||||
|
||||
/* F8 : nombre de façons de rendre une somme avec les pièces disponibles */
|
||||
/* F8 : nombre de façons de rendre une somme avec les pièces disponibles */
|
||||
static const int PIECES[] = {1, 2, 5, 10, 20, 50, 100, 200};
|
||||
#define NB_PIECES 8
|
||||
|
||||
long nb_facons_rendre(int somme, int nb_pieces)
|
||||
{
|
||||
if (somme == 0)
|
||||
return 1;
|
||||
if (somme < 0 || nb_pieces == 0)
|
||||
return 0;
|
||||
return nb_facons_rendre(somme - PIECES[nb_pieces - 1], nb_pieces) +
|
||||
nb_facons_rendre(somme, nb_pieces - 1);
|
||||
long dp[MONTANT_MONNAIE + 1] = {0};
|
||||
dp[0] = 1;
|
||||
|
||||
for (int i = 0; i < nb_pieces; i++) {
|
||||
for (int s = PIECES[i]; s <= somme; s++) {
|
||||
dp[s] += dp[s - PIECES[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return dp[somme];
|
||||
}
|
||||
|
||||
/* F9 : catégorie d'une transaction (pour les statistiques) */
|
||||
/* F9 : catégorie des transactions négatives */
|
||||
static int categorie_negative(const Transaction *t)
|
||||
{
|
||||
if (t->montant < -20000) {
|
||||
if (t->jour % 7 >= 5)
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (t->montant < -5000)
|
||||
return 3;
|
||||
|
||||
if (t->client % 10 == 0)
|
||||
return 4;
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
/* F9 : catégorie des transactions positives */
|
||||
static int categorie_positive(const Transaction *t)
|
||||
{
|
||||
if (t->montant > 30000) {
|
||||
if (t->client % 10 == 0 && t->jour % 7 < 5)
|
||||
return 6;
|
||||
return 7;
|
||||
}
|
||||
|
||||
if (t->montant > 10000)
|
||||
return 8;
|
||||
|
||||
if (t->montant > 1000) {
|
||||
if (t->jour % 30 == 0)
|
||||
return 9;
|
||||
return 10;
|
||||
}
|
||||
|
||||
return 11;
|
||||
}
|
||||
|
||||
/* F9 : catégorie d'une transaction */
|
||||
int categorie(const Transaction *t)
|
||||
{
|
||||
if (t->montant < 0) {
|
||||
if (t->montant < -20000) {
|
||||
if (t->jour % 7 == 5 || t->jour % 7 == 6)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
} else if (t->montant < -5000) {
|
||||
return 3;
|
||||
} else {
|
||||
if (t->client % 10 == 0)
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
} else if (t->montant == 0) {
|
||||
if (t->montant < 0)
|
||||
return categorie_negative(t);
|
||||
|
||||
if (t->montant == 0)
|
||||
return 0;
|
||||
} else {
|
||||
if (t->montant > 30000) {
|
||||
if (t->client % 10 == 0 && t->jour % 7 < 5)
|
||||
return 6;
|
||||
return 7;
|
||||
} else if (t->montant > 10000) {
|
||||
return 8;
|
||||
} else if (t->montant > 1000) {
|
||||
if (t->jour % 30 == 0)
|
||||
return 9;
|
||||
return 10;
|
||||
}
|
||||
return 11;
|
||||
}
|
||||
|
||||
return categorie_positive(t);
|
||||
}
|
||||
|
||||
/* F10 : ajoute une ligne à la fin du rapport */
|
||||
int longueur(const char *s)
|
||||
/* F10 : ajoute une ligne à la fin du rapport en conservant sa position */
|
||||
void ajouter_ligne(char *rapport, int *fin, const char *ligne)
|
||||
{
|
||||
int n = 0;
|
||||
while (s[n] != '\0')
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
void ajouter_ligne(char *rapport, const char *ligne)
|
||||
{
|
||||
int fin = longueur(rapport);
|
||||
int i = 0;
|
||||
|
||||
while (ligne[i] != '\0') {
|
||||
rapport[fin + i] = ligne[i];
|
||||
rapport[*fin + i] = ligne[i];
|
||||
i++;
|
||||
}
|
||||
rapport[fin + i] = '\0';
|
||||
|
||||
*fin += i;
|
||||
rapport[*fin] = '\0';
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -251,8 +345,22 @@ unsigned long empreinte(const char *s)
|
||||
int main(void)
|
||||
{
|
||||
Transaction *t = generer_transactions(NB_TRANSACTIONS);
|
||||
int *index = malloc(NB_TRANSACTIONS * sizeof(int));
|
||||
char *rapport = malloc(1 << 20);
|
||||
char ligne[128];
|
||||
int fin_rapport = 0;
|
||||
|
||||
if (t == NULL || index == NULL || rapport == NULL) {
|
||||
fprintf(stderr, "Erreur d'allocation mémoire.\n");
|
||||
free(t);
|
||||
free(index);
|
||||
free(rapport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NB_TRANSACTIONS; i++)
|
||||
index[t[i].id] = i;
|
||||
|
||||
rapport[0] = '\0';
|
||||
|
||||
/* recherches par id, dans l'ordre d'origine */
|
||||
@@ -260,7 +368,8 @@ int main(void)
|
||||
unsigned long etat = 12345;
|
||||
for (int q = 0; q < NB_REQUETES; q++) {
|
||||
etat = etat * 1103515245UL + 12345UL;
|
||||
const Transaction *r = rechercher_par_id(t, NB_TRANSACTIONS, (int)((etat >> 8) % NB_TRANSACTIONS));
|
||||
const Transaction *r = rechercher_par_id(t, index, NB_TRANSACTIONS,
|
||||
(int)((etat >> 8) % NB_TRANSACTIONS));
|
||||
if (r != NULL)
|
||||
somme_requetes += r->montant;
|
||||
}
|
||||
@@ -287,30 +396,30 @@ int main(void)
|
||||
|
||||
/* rapport */
|
||||
snprintf(ligne, sizeof(ligne), "clients distincts;%d\n", nb_clients);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
snprintf(ligne, sizeof(ligne), "somme des requetes;%ld\n", somme_requetes);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
snprintf(ligne, sizeof(ligne), "meilleure periode;%d;%d;%ld\n", debut, fin, meilleure);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
snprintf(ligne, sizeof(ligne), "paires opposees;%ld\n", nb_paires);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
snprintf(ligne, sizeof(ligne), "facons de rendre %d;%ld\n", MONTANT_MONNAIE, nb_facons);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
for (int c = 0; c < 12; c++) {
|
||||
snprintf(ligne, sizeof(ligne), "categorie;%d;%d\n", c, categories[c]);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
}
|
||||
for (int c = 0; c < NB_CLIENTS; c++) {
|
||||
snprintf(ligne, sizeof(ligne), "client;%d;%ld\n", c, totaux_clients[c]);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
}
|
||||
for (int j = 0; j < NB_JOURS; j++) {
|
||||
snprintf(ligne, sizeof(ligne), "jour;%d;%ld\n", j, totaux_jours[j]);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
}
|
||||
for (int i = 0; i < NB_TRANSACTIONS; i += 10) {
|
||||
snprintf(ligne, sizeof(ligne), "rang;%d;%d;%ld\n", i, t[i].id, t[i].montant);
|
||||
ajouter_ligne(rapport, ligne);
|
||||
ajouter_ligne(rapport, &fin_rapport, ligne);
|
||||
}
|
||||
|
||||
FILE *f = fopen("rapport.txt", "w");
|
||||
@@ -327,6 +436,7 @@ int main(void)
|
||||
printf("Empreinte : %016lx\n", empreinte(rapport));
|
||||
|
||||
free(rapport);
|
||||
free(index);
|
||||
free(t);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user