Compare commits

..

10 Commits

13 changed files with 1188 additions and 207 deletions
+271 -5
View File
@@ -1,11 +1,11 @@
# Compte rendu du TD de complexité algorithmique
Ce compte rendu traite les trois étapes du palier 1 du TD
(page 68 de `R5A04_Algorithmique_MM.pdf`) : mesurer la version initiale,
la profiler, puis calculer les complexités algorithmiques et cyclomatiques.
Ce compte rendu traite le palier 1 et l'étape 4 du TD
(page 68 de `R5A04_Algorithmique_MM.pdf`) : analyser la version initiale,
puis atteindre les objectifs avec un commit par optimisation.
Les mesures ont été réalisées le 21 octobre 2026 sur Linux x86_64, avec
GCC 16.2.1 et GNU gprof 2.47. Le fichier `banque.c` est dans sa version initiale.
GCC 16.2.1 et GNU gprof 2.47. Les sections 1 à 3 décrivent la version initiale.
Les données sont reproductibles grâce à la graine fixe du générateur.
| Paramètre | Valeur |
@@ -163,7 +163,7 @@ Le tableau reprend l'analyse initiale de `notes.md`, avec la complexité
cyclomatique de chaque fonction. Les complexités temporelles sont données
au pire cas, en faisant varier les tailles des données, même si leurs valeurs
sont fixées par des constantes dans le programme. Les objectifs sont ceux
du tableau de la page 67 du TD ; ils restent à atteindre.
du tableau de la page 67 du TD ; la section 4 présente leur réalisation.
| ID | Fonction | Calcule | Actuelle | M | Objectif |
|:---:|---|---|---|:---:|---|
@@ -232,3 +232,269 @@ Les décisions d'une fonction appelée se comptent dans cette fonction.
- **F10** : 1 `while` = 1 décision, donc **M = 2**.
La fonction auxiliaire `longueur` contient elle aussi un `while` et a
**M = 2**, compté séparément.
## 4. Atteindre les objectifs
Chaque optimisation est vérifiée avant son commit : compilation avec les
avertissements du Makefile, tests de cas limites via `make test`, exécution
en `-O2`, empreinte **`ff68d260a7a58441`** et comparaison du rapport complet
avec celui de la version initiale. Les constantes, `alea`,
`generer_transactions` et le format du rapport sont conservés.
### F1 — Tri fusion
Le tri à bulles est remplacé par un tri fusion : chaque moitié est triée,
puis les deux moitiés sont fusionnées en un passage. La comparaison porte
sur le montant, puis sur l'identifiant, sans soustraction susceptible de
déborder. La récurrence `T(n) = 2T(n/2) + O(n)` donne **`O(n log n)` au pire
cas**, avec `O(n)` de mémoire auxiliaire et `O(log n)` de pile.
La complexité cyclomatique de F1 passe de **6 à 3** ; les auxiliaires
`comparer_transactions` et `trier_intervalle` ont respectivement **M = 2**
et **M = 6**. Les tests comparent le résultat à un tri de référence pour
les tailles 0 à 128, des doublons, des entrées déjà triées ou inversées,
et les bornes des types `int` et `long`.
### F2 — Tableau des clients rencontrés
Un tableau de `C` indicateurs, initialisé à zéro, remplace la recherche
dans les transactions précédentes. Une transaction marque son client à son
premier passage et incrémente le compteur. Le coût devient **`O(n + C)`**
au lieu de `O(n²)`, avec `O(C)` de mémoire. Les identifiants de clients sont
compris entre 0 et `C - 1`, comme le garantit le générateur.
La complexité cyclomatique passe de **5 à 3**. Les tests couvrent l'absence
de transactions, les doublons, les identifiants extrêmes et tous les clients.
### F3 — Accumulation directe par client
Après la remise à zéro des `C` soldes, un seul parcours des transactions
ajoute chaque montant dans `totaux[t[i].client]`. Les deux boucles sont
successives : **`O(n + C)`** au lieu de `O(n × C)`, avec `O(1)` de mémoire
auxiliaire en dehors du tableau de sortie. La complexité cyclomatique passe
de **4 à 3**. Les tests vérifient les clients sans transaction, les débits,
les crédits, les annulations et la réinitialisation entre deux appels.
### F4 — Accumulation directe par jour
Le même principe s'applique aux jours : initialisation des `J` soldes,
puis ajout de chaque montant dans `totaux[t[i].jour]`. Le coût passe de
`O(n × J)` à **`O(n + J)`**, avec `O(1)` de mémoire auxiliaire hors sortie.
La complexité cyclomatique passe de **4 à 3**. Les tests comparent chaque
solde à un calcul de référence, y compris aux bornes du tableau, puis
vérifient qu'un appel sans transaction remet tous les soldes à zéro.
### F5 — Meilleure période en un passage (Kadane)
La somme de la meilleure période se terminant au jour courant est conservée.
Si cette somme est négative, le prochain jour démarre une nouvelle période ;
sinon, elle est prolongée. Le coût passe de `O(J³)` à **`O(J)`**, avec
**`O(1)`** de mémoire auxiliaire, et la complexité cyclomatique de **5 à 4**.
La somme initiale est celle du premier jour : une série entièrement négative
renvoie bien un intervalle non vide. On redémarre uniquement pour une somme
strictement négative et on remplace le meilleur résultat uniquement pour
une somme strictement supérieure. Une somme nulle conserve ainsi le début
le plus ancien ; à égalité, le premier début puis la première fin sont
conservés comme dans les boucles initiales. Les **97 655 séries** de longueur
1 à 7 à valeurs dans `[-2, 2]` sont comparées à la version cubique, sur la
somme et sur les deux bornes : elles couvrent notamment les zéros, les
égalités et les séries entièrement négatives.
### F6 — Index direct par identifiant
Les identifiants sont une permutation de `[0, n - 1]`. Un index de pointeurs
est construit en **`O(n)`** avant la boucle des requêtes. Chaque recherche
contrôle les bornes et renvoie `index_ids[id]` en **`O(1)`**, au lieu de
parcourir les transactions en `O(n)`. Le coût des `Q` requêtes, construction
comprise, devient **`O(n + Q)`**, avec `O(n)` de mémoire supplémentaire.
Les recherches ont toutes lieu avant le tri des transactions : un déplacement
des transactions imposerait de reconstruire l'index. F6 garde **M = 3** et
`indexer_par_id` a **M = 2**. Les tests comparent les pointeurs renvoyés
à une recherche linéaire sur des identifiants permutés pour les tailles
1 à 128, et vérifient les recherches hors bornes et dans un tableau vide.
### F7 — Tri et deux pointeurs pour les paires opposées
Une copie des transactions est triée avec F1 pour préserver l'ordre du
tableau d'entrée. Deux pointeurs partent ensuite des extrémités : une somme
négative fait avancer le pointeur gauche, une somme positive fait reculer
le droit. Pour deux montants opposés, on compte leurs multiplicités `a` et
`b` et on ajoute `a × b`. Pour un groupe de `z` zéros, on ajoute
`z(z - 1) / 2` : chaque paire d'indices distincts est comptée une seule fois.
Le tri coûte `O(n log n)` et chaque pointeur traverse au plus `n` éléments,
soit **`O(n log n)`** au total au lieu de `O(n²)`, avec `O(n)` de mémoire.
F7 passe de **M = 4 à M = 3** ; l'auxiliaire `compter_paires_triees` a
**M = 9**. Les tests comparent le compteur à l'énumération de toutes les
paires sur les **97 655 tableaux** de longueur 1 à 7 à montants dans
`[-2, 2]`, ainsi que sur une entrée vide. Ils vérifient aussi que les
transactions d'origine restent inchangées, notamment en présence de
doublons et de plusieurs zéros.
### F8 — Programmation dynamique pour le rendu de monnaie
`facons[s]` contient le nombre de façons de rendre `s` avec les pièces déjà
traitées. Il vaut initialement 1 pour la somme nulle et 0 ailleurs. Pour
chaque pièce, les sommes sont parcourues dans l'ordre croissant et l'on
ajoute `facons[s - piece]` à `facons[s]`. Ce sens permet la réutilisation
d'une pièce ; la boucle des pièces à l'extérieur compte des combinaisons,
sans distinguer l'ordre des pièces.
Chaque état est calculé une seule fois par type de pièce : **`O(S × k)`**
pour `k ≥ 1` (et `O(S × k + S)` en incluant l'initialisation si `k = 0`),
avec **`O(S)`** de mémoire. La récursion et ses sous-problèmes répétés
disparaissent. F8 passe de **M = 4 à M = 5** : l'amélioration temporelle
n'implique pas une baisse de M. Les tests comparent les résultats à la
récursion initiale pour les sommes de -2 à 40 et de 0 à 8 types de pièces,
y compris la somme nulle et l'absence de pièces.
### F9 — Séparer les règles des débits et des crédits
`categorie` répartit les transactions entre débit, montant nul et crédit.
Les règles propres aux débits et aux crédits sont regroupées dans deux
fonctions auxiliaires, avec des retours directs. Le test du week-end
`jour % 7 >= 5` remplace les deux comparaisons reliées par `||`.
Le temps reste **`O(1)`** et la complexité cyclomatique de F9 passe de
**14 à 3**, sous l'objectif de 10. Les auxiliaires `categorie_debit`
et `categorie_credit` ont respectivement **M = 5** et **M = 7** : les
décisions ne sont pas simplement déplacées vers une autre fonction
dépassant l'objectif. Les tests confrontent les 20 montants situés aux
seuils et à leurs voisins, y compris `LONG_MIN` et `LONG_MAX`, à la fonction
initiale, pour 20 clients et 210 jours (cycle commun des modulos 7 et 30),
soit **84 000 classifications**.
### F10 — Conserver la fin du rapport
`ajouter_ligne` reçoit le pointeur sur le caractère nul final, copie la ligne
et renvoie le nouveau pointeur de fin. `main` le conserve entre les appels.
La fonction `longueur` disparaît : les caractères déjà écrits ne sont plus
relus. Un ajout coûte **`O(l)`** pour une ligne de longueur `l`, et la
construction complète **`O(L)`** au lieu de `O(L²)` pour des lignes de taille
bornée. La mémoire auxiliaire reste **`O(1)`** et F10 garde **M = 2**.
Les tests vérifient la concaténation et le pointeur renvoyé après 200 ajouts,
les lignes vides et une ligne remplissant exactement le tampon disponible.
Le rapport complet garde ses **6 017 lignes et 120 348 octets**.
### Bilan des complexités
| Fonction | Avant | Après | M avant → après |
|---|---|---|:---:|
| F1 | `O(n²)` | `O(n log n)` au pire cas | 6 → 3 |
| F2 | `O(n²)` | `O(n + C)` | 5 → 3 |
| F3 | `O(n × C)` | `O(n + C)` | 4 → 3 |
| F4 | `O(n × J)` | `O(n + J)` | 4 → 3 |
| F5 | `O(J³)` | `O(J)` | 5 → 4 |
| F6 | `O(n)` par requête | `O(1)` par requête après indexation `O(n)` | 3 → 3 |
| F7 | `O(n²)` | `O(n log n)` | 4 → 3 |
| F8 | `O(2^(S + k))`, borne générale | `O(S × k)` pour `k ≥ 1` | 4 → 5 |
| F9 | `O(1)` | `O(1)` | 14 → 3 |
| F10 | `O(L²)` au total, lignes de taille bornée | `O(L)` au total | 2 → 2 |
M des auxiliaires ajoutés : `comparer_transactions` **2**,
`trier_intervalle` **6**, `indexer_par_id` **2**,
`compter_paires_triees` **9**, `categorie_debit` **5**,
`categorie_credit` **7**. Avec **M = 9** pour `main`, le maximum hors
génération est désormais **9**, contre **14** initialement.
### Validation à chaque commit et mesure finale
Le journal [validations_optimisations.csv](build/validations_optimisations.csv)
conserve les dix vérifications successives : fonction optimisée, empreinte,
égalité du rapport, temps d'une exécution et SHA-256 du fichier `banque.c`
testé. Chaque ligne correspond au code du commit de cette optimisation.
Les dix empreintes sont **`ff68d260a7a58441`**, et les dix rapports sont
identiques octet par octet à celui de la version initiale.
La validation finale comprend :
- `make test` : tests de cas limites et comparaisons aux versions de référence ;
- `make check` : compilation en `-O2` et vérification de l'empreinte ;
- `make profile` : compilation en `-O0 -g -pg`, même sortie et même rapport ;
- les tests et une exécution complète compilés avec
`-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer` : aucune erreur
mémoire ou de comportement indéfini signalée ;
- comparaison du code : constantes, génération des données et chaînes de
formatage du rapport inchangées.
La commande pour relancer les tests avec les vérifications mémoire est :
```bash
make test CFLAGS='-std=c11 -Wall -Wextra -Wpedantic -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer'
```
Une nouvelle exécution de la version initiale prend **2,437683 s**. Pour
la version finale compilée avec les mêmes options en `-O2`, les cinq temps
réels sont **0,010910 ; 0,011111 ; 0,010734 ; 0,011242 ; 0,010525 s**,
soit une médiane de **0,010910 s**, environ **11 ms**. Les mesures utilisent
`time.perf_counter()` autour du processus entier et incluent l'écriture du
rapport ; elles sont conservées dans [temps_apres.txt](build/temps_apres.txt).
La référence étant une seule exécution et les temps dépendant de la machine,
cette comparaison donne un ordre de grandeur du gain, pas un classement.
### Profil gprof après optimisation
Le profil d'une exécution finale est conservé dans
[profil_apres.txt](build/profil/profil_apres.txt), ainsi que dans le fichier
courant [profil.txt](build/profil/profil.txt). Il a été obtenu avec :
```bash
make profile
cp build/profil/profil.txt build/profil/profil_apres.txt
```
Cette exécution ne fournit qu'environ **0,01 s échantillonnée**, attribuée
au tri fusion. Un seul échantillon ne permet pas d'interpréter les parts de
temps. Pour mieux distinguer les coûts restants, le programme complet est
donc répété **200 fois dans le même processus instrumenté**, avec remise à
la graine initiale entre les passages. La sortie standard est redirigée,
le rapport est toujours écrit, et le dernier rapport est comparé à la
référence. Cette répétition concerne uniquement le pilote de mesure ;
`banque.c` et ses constantes ne sont pas modifiés.
Le profil cumulé est enregistré dans
[profil_apres_repete.txt](build/profil/profil_apres_repete.txt). Il représente
environ **2,07 s de temps CPU échantillonné pour 200 traitements** : les
nombres d'appels et les temps du tableau suivant sont ceux du lot entier.
| Fonction | Appels hors récursion | Temps propre cumulé (s) | Part du temps |
|---|---:|---:|---:|
| `trier_intervalle` | 400 | 1,19 | 57,25 % |
| `comparer_transactions` | 163 486 000 | 0,29 | 14,01 % |
| `generer_transactions` | 200 | 0,14 | 6,76 % |
| `compter_paires_triees` | 200 | 0,08 | 3,86 % |
| `ajouter_ligne` | 1 203 400 | 0,04 | 1,93 % |
| `rechercher_par_id` | 6 000 000 | 0,01 | 0,48 % |
| `meilleure_periode` | 200 | 0,00 | 0,00 % |
| `nb_facons_rendre` | 200 | 0,00 | 0,00 % |
Les deux tris de chaque traitement, celui de F7 sur une copie et celui du
rapport, expliquent les 400 appels initiaux de `trier_intervalle`. Le tri
fusion et ses comparaisons représentent désormais l'essentiel du coût.
Les centaines de millions d'appels récursifs de F8 ont disparu, les
recherches F6 coûtent très peu et F10 ne rescane plus les préfixes du
rapport. Les valeurs à 0,00 s restent des arrondis de l'échantillonnage.
Les durées brutes de ce lot ne se comparent pas à celles du profil initial
sur une seule exécution, ni aux temps réels mesurés en `-O2`.
Pour reproduire ce profil cumulé depuis la racine :
```bash
tmp_profil=$(mktemp -d)
gcc -std=c11 -Wall -Wextra -Wpedantic -O0 -g -pg -x c -o "$tmp_profil/banque" - <<EOF
#define main banque_main
#include "$(pwd)/banque.c"
#undef main
int main(void) {
for (int i = 0; i < 200; i++) {
etat_alea = GRAINE;
if (banque_main() != 0) return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
EOF
(cd "$tmp_profil" && ./banque > /dev/null && gprof -b ./banque gmon.out) > build/profil/profil_apres_repete.txt
rm -rf "$tmp_profil"
```
+7 -1
View File
@@ -8,7 +8,7 @@ PROGRAMME_PROFIL = build/profil/banque
EMPREINTE_ATTENDUE = ff68d260a7a58441
NOM ?= $(shell whoami)
.PHONY: all run check bench profile clean help
.PHONY: all run check test bench profile clean help
all: $(PROGRAMME)
@@ -29,6 +29,11 @@ check: $(PROGRAMME)
fi
@printf 'Empreinte correcte.\n'
test: $(SOURCE) tests/test_banque.c
@programme=$$(mktemp); \
trap 'rm -f "$$programme"' EXIT HUP INT TERM; \
$(CC) $(CPPFLAGS) $(CFLAGS) tests/test_banque.c -o "$$programme" $(LDFLAGS) $(LDLIBS) && "$$programme"
# Le script impose gcc -O2, vérifie l'empreinte et mesure 5 exécutions.
bench: $(SOURCE) evaluer.sh
mkdir -p build
@@ -52,6 +57,7 @@ help:
'make Compiler banque.c avec -O2' \
'make run Exécuter et produire build/rapport.txt' \
"make check Vérifier l'empreinte attendue" \
'make test Vérifier les cas limites des fonctions optimisées' \
'make bench NOM=nom Évaluer les performances (médiane de 5 exécutions)' \
'make profile Générer le profil gprof avec -O0 -g -pg' \
'make clean Supprimer build, y compris les mesures et profils'
+183 -105
View File
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NB_TRANSACTIONS 30000
#define NB_CLIENTS 2000
@@ -71,34 +72,57 @@ Transaction *generer_transactions(int n)
/* ------------------------------------------------------------------ */
/* F1 : trie les transactions par montant croissant, puis par id */
static int comparer_transactions(const Transaction *a, const Transaction *b)
{
if (a->montant != b->montant)
return (a->montant > b->montant) - (a->montant < b->montant);
return (a->id > b->id) - (a->id < b->id);
}
static void trier_intervalle(Transaction *t, Transaction *tampon, int debut, int fin)
{
if (fin - debut < 2)
return;
int milieu = debut + (fin - debut) / 2;
trier_intervalle(t, tampon, debut, milieu);
trier_intervalle(t, tampon, milieu, fin);
int gauche = debut;
int droite = milieu;
for (int i = debut; i < fin; i++) {
if (gauche < milieu &&
(droite == fin || comparer_transactions(&t[gauche], &t[droite]) <= 0))
tampon[i] = t[gauche++];
else
tampon[i] = t[droite++];
}
memcpy(t + debut, tampon + debut, (size_t)(fin - debut) * sizeof(*t));
}
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;
}
}
if (n < 2)
return;
Transaction *tampon = malloc((size_t)n * sizeof(*tampon));
if (tampon == NULL) {
perror("Allocation du tampon de tri");
exit(EXIT_FAILURE);
}
trier_intervalle(t, tampon, 0, n);
free(tampon);
}
/* F2 : nombre de clients ayant au moins une transaction */
int nb_clients_distincts(const Transaction *t, int n)
{
unsigned char vus[NB_CLIENTS] = {0};
int nb = 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)
if (!vus[t[i].client]) {
vus[t[i].client] = 1;
nb++;
}
}
return nb;
}
@@ -106,63 +130,109 @@ int nb_clients_distincts(const Transaction *t, int n)
/* 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 */
void total_par_jour(const Transaction *t, int n, long *totaux)
{
for (int j = 0; j < NB_JOURS; j++) {
for (int j = 0; j < NB_JOURS; j++)
totaux[j] = 0;
for (int i = 0; i < n; i++)
if (t[i].jour == j)
totaux[j] += t[i].montant;
}
for (int i = 0; i < n; i++)
totaux[t[i].jour] += t[i].montant;
}
/* F5 : période de jours consécutifs dont la somme des soldes est maximale */
/* Précondition : nb_jours > 0. En cas d'égalité, garder le premier intervalle. */
long meilleure_periode(const long *totaux, int nb_jours, int *debut, int *fin)
{
long meilleur = totaux[0];
long somme = totaux[0];
int debut_courant = 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 = 1; j < nb_jours; j++) {
if (somme < 0) {
somme = totaux[j];
debut_courant = j;
} else {
somme += totaux[j];
}
if (somme > meilleur) {
meilleur = somme;
*debut = debut_courant;
*fin = j;
}
}
return meilleur;
}
/* F6 : retrouve une transaction à partir de son id */
const Transaction *rechercher_par_id(const Transaction *t, int n, int id)
/* Les id forment une permutation de 0 à n - 1. Reconstruire après tout tri. */
void indexer_par_id(const Transaction *t, int n, const Transaction **index_ids)
{
for (int i = 0; i < n; i++)
if (t[i].id == id)
return &t[i];
return NULL;
index_ids[t[i].id] = &t[i];
}
const Transaction *rechercher_par_id(const Transaction *const *index_ids, int n, int id)
{
if (id < 0 || id >= n)
return NULL;
return index_ids[id];
}
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */
long nb_paires_opposees(const Transaction *t, int n)
static long compter_paires_triees(const Transaction *t, int n)
{
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++;
int gauche = 0;
int droite = n - 1;
while (gauche < droite) {
long somme = t[gauche].montant + t[droite].montant;
if (somme < 0) {
gauche++;
} else if (somme > 0) {
droite--;
} else if (t[gauche].montant == 0) {
long taille = droite - gauche + 1;
nb += taille * (taille - 1) / 2;
break;
} else {
long nb_gauche = 1;
long nb_droite = 1;
while (gauche + 1 < droite && t[gauche + 1].montant == t[gauche].montant) {
gauche++;
nb_gauche++;
}
while (droite - 1 > gauche && t[droite - 1].montant == t[droite].montant) {
droite--;
nb_droite++;
}
nb += nb_gauche * nb_droite;
gauche++;
droite--;
}
}
return nb;
}
long nb_paires_opposees(const Transaction *t, int n)
{
if (n < 2)
return 0;
Transaction *copie = malloc((size_t)n * sizeof(*copie));
if (copie == NULL) {
perror("Allocation de la copie des transactions");
exit(EXIT_FAILURE);
}
memcpy(copie, t, (size_t)n * sizeof(*copie));
trier_par_montant(copie, n);
long nb = compter_paires_triees(copie, n);
free(copie);
return nb;
}
@@ -172,66 +242,71 @@ static const int PIECES[] = {1, 2, 5, 10, 20, 50, 100, 200};
long nb_facons_rendre(int somme, int nb_pieces)
{
if (somme == 0)
return 1;
if (somme < 0 || nb_pieces == 0)
if (somme < 0)
return 0;
return nb_facons_rendre(somme - PIECES[nb_pieces - 1], nb_pieces) +
nb_facons_rendre(somme, nb_pieces - 1);
long *facons = calloc((size_t)somme + 1, sizeof(*facons));
if (facons == NULL) {
perror("Allocation du tableau de rendu de monnaie");
exit(EXIT_FAILURE);
}
facons[0] = 1;
for (int p = 0; p < nb_pieces; p++)
for (int s = PIECES[p]; s <= somme; s++)
facons[s] += facons[s - PIECES[p]];
long resultat = facons[somme];
free(facons);
return resultat;
}
/* F9 : catégorie d'une transaction (pour les statistiques) */
static int categorie_debit(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;
}
static int categorie_credit(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;
}
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_debit(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_credit(t);
}
/* F10 : ajoute une ligne à la fin du rapport */
int longueur(const char *s)
/* fin_rapport désigne le '\0' courant ; l'appelant conserve la nouvelle fin. */
char *ajouter_ligne(char *fin_rapport, 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];
i++;
}
rapport[fin + i] = '\0';
while (*ligne != '\0')
*fin_rapport++ = *ligne++;
*fin_rapport = '\0';
return fin_rapport;
}
/* ------------------------------------------------------------------ */
@@ -254,13 +329,16 @@ int main(void)
char *rapport = malloc(1 << 20);
char ligne[128];
rapport[0] = '\0';
char *fin_rapport = rapport;
/* recherches par id, dans l'ordre d'origine */
static const Transaction *index_ids[NB_TRANSACTIONS];
indexer_par_id(t, NB_TRANSACTIONS, index_ids);
long somme_requetes = 0;
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(index_ids, NB_TRANSACTIONS, (int)((etat >> 8) % NB_TRANSACTIONS));
if (r != NULL)
somme_requetes += r->montant;
}
@@ -287,30 +365,30 @@ int main(void)
/* rapport */
snprintf(ligne, sizeof(ligne), "clients distincts;%d\n", nb_clients);
ajouter_ligne(rapport, ligne);
fin_rapport = ajouter_ligne(fin_rapport, ligne);
snprintf(ligne, sizeof(ligne), "somme des requetes;%ld\n", somme_requetes);
ajouter_ligne(rapport, ligne);
fin_rapport = ajouter_ligne(fin_rapport, ligne);
snprintf(ligne, sizeof(ligne), "meilleure periode;%d;%d;%ld\n", debut, fin, meilleure);
ajouter_ligne(rapport, ligne);
fin_rapport = ajouter_ligne(fin_rapport, ligne);
snprintf(ligne, sizeof(ligne), "paires opposees;%ld\n", nb_paires);
ajouter_ligne(rapport, ligne);
fin_rapport = ajouter_ligne(fin_rapport, ligne);
snprintf(ligne, sizeof(ligne), "facons de rendre %d;%ld\n", MONTANT_MONNAIE, nb_facons);
ajouter_ligne(rapport, ligne);
fin_rapport = ajouter_ligne(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);
fin_rapport = ajouter_ligne(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);
fin_rapport = ajouter_ligne(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);
fin_rapport = ajouter_ligne(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);
fin_rapport = ajouter_ligne(fin_rapport, ligne);
}
FILE *f = fopen("rapport.txt", "w");
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+109 -82
View File
@@ -1,99 +1,126 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
48.60 3.13 3.13 1 3.13 3.13 trier_par_montant
13.20 3.98 0.85 30000 0.00 0.00 rechercher_par_id
11.80 4.74 0.76 1 0.76 0.76 nb_paires_opposees
10.17 5.39 0.66 1 0.66 0.66 nb_facons_rendre
6.37 5.80 0.41 6017 0.00 0.00 longueur
4.97 6.12 0.32 1 0.32 0.32 meilleure_periode
1.71 6.24 0.11 1 0.11 0.11 nb_clients_distincts
1.71 6.34 0.11 1 0.11 0.11 total_par_client
0.78 6.39 0.05 1 0.05 0.05 total_par_jour
0.39 6.42 0.03 30000 0.00 0.00 categorie
0.31 6.44 0.02 _init
0.00 6.44 0.00 149998 0.00 0.00 alea
0.00 6.44 0.00 6017 0.00 0.00 ajouter_ligne
0.00 6.44 0.00 1 0.00 0.00 empreinte
0.00 6.44 0.00 1 0.00 0.00 generer_transactions
% cumulative self self total
time seconds seconds calls ms/call ms/call name
100.00 0.01 0.01 2 5.00 5.00 trier_intervalle
0.00 0.01 0.00 817430 0.00 0.00 comparer_transactions
0.00 0.01 0.00 149998 0.00 0.00 alea
0.00 0.01 0.00 30000 0.00 0.00 categorie
0.00 0.01 0.00 30000 0.00 0.00 rechercher_par_id
0.00 0.01 0.00 15066 0.00 0.00 categorie_debit
0.00 0.01 0.00 14934 0.00 0.00 categorie_credit
0.00 0.01 0.00 6017 0.00 0.00 ajouter_ligne
0.00 0.01 0.00 2 0.00 5.00 trier_par_montant
0.00 0.01 0.00 1 0.00 0.00 compter_paires_triees
0.00 0.01 0.00 1 0.00 0.00 empreinte
0.00 0.01 0.00 1 0.00 0.00 generer_transactions
0.00 0.01 0.00 1 0.00 0.00 indexer_par_id
0.00 0.01 0.00 1 0.00 0.00 meilleure_periode
0.00 0.01 0.00 1 0.00 0.00 nb_clients_distincts
0.00 0.01 0.00 1 0.00 0.00 nb_facons_rendre
0.00 0.01 0.00 1 0.00 5.00 nb_paires_opposees
0.00 0.01 0.00 1 0.00 0.00 total_par_client
0.00 0.01 0.00 1 0.00 0.00 total_par_jour
Call graph
granularity: each sample hit covers 4 byte(s) for 0.16% of 6.44 seconds
granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds
index % time self children called name
<spontaneous>
[1] 99.7 0.00 6.42 main [1]
3.13 0.00 1/1 trier_par_montant [2]
0.85 0.00 30000/30000 rechercher_par_id [3]
0.76 0.00 1/1 nb_paires_opposees [4]
0.66 0.00 1/1 nb_facons_rendre [5]
0.00 0.41 6017/6017 ajouter_ligne [6]
0.32 0.00 1/1 meilleure_periode [8]
0.11 0.00 1/1 nb_clients_distincts [9]
0.11 0.00 1/1 total_par_client [10]
0.05 0.00 1/1 total_par_jour [11]
0.03 0.00 30000/30000 categorie [12]
0.00 0.00 1/1 generer_transactions [16]
0.00 0.00 1/1 empreinte [15]
119996 trier_intervalle [1]
0.01 0.00 2/2 trier_par_montant [2]
[1] 100.0 0.01 0.00 2+119996 trier_intervalle [1]
0.00 0.00 817430/817430 comparer_transactions [5]
119996 trier_intervalle [1]
-----------------------------------------------
3.13 0.00 1/1 main [1]
[2] 48.6 3.13 0.00 1 trier_par_montant [2]
-----------------------------------------------
0.85 0.00 30000/30000 main [1]
[3] 13.2 0.85 0.00 30000 rechercher_par_id [3]
-----------------------------------------------
0.76 0.00 1/1 main [1]
[4] 11.8 0.76 0.00 1 nb_paires_opposees [4]
-----------------------------------------------
266723738 nb_facons_rendre [5]
0.66 0.00 1/1 main [1]
[5] 10.2 0.66 0.00 1+266723738 nb_facons_rendre [5]
266723738 nb_facons_rendre [5]
-----------------------------------------------
0.00 0.41 6017/6017 main [1]
[6] 6.4 0.00 0.41 6017 ajouter_ligne [6]
0.41 0.00 6017/6017 longueur [7]
-----------------------------------------------
0.41 0.00 6017/6017 ajouter_ligne [6]
[7] 6.4 0.41 0.00 6017 longueur [7]
-----------------------------------------------
0.32 0.00 1/1 main [1]
[8] 5.0 0.32 0.00 1 meilleure_periode [8]
-----------------------------------------------
0.11 0.00 1/1 main [1]
[9] 1.7 0.11 0.00 1 nb_clients_distincts [9]
-----------------------------------------------
0.11 0.00 1/1 main [1]
[10] 1.7 0.11 0.00 1 total_par_client [10]
-----------------------------------------------
0.05 0.00 1/1 main [1]
[11] 0.8 0.05 0.00 1 total_par_jour [11]
-----------------------------------------------
0.03 0.00 30000/30000 main [1]
[12] 0.4 0.03 0.00 30000 categorie [12]
0.00 0.01 1/2 nb_paires_opposees [4]
0.00 0.01 1/2 main [3]
[2] 100.0 0.00 0.01 2 trier_par_montant [2]
0.01 0.00 2/2 trier_intervalle [1]
-----------------------------------------------
<spontaneous>
[13] 0.3 0.02 0.00 _init [13]
[3] 100.0 0.00 0.01 main [3]
0.00 0.01 1/1 nb_paires_opposees [4]
0.00 0.01 1/2 trier_par_montant [2]
0.00 0.00 30000/30000 rechercher_par_id [8]
0.00 0.00 30000/30000 categorie [7]
0.00 0.00 6017/6017 ajouter_ligne [11]
0.00 0.00 1/1 generer_transactions [14]
0.00 0.00 1/1 indexer_par_id [15]
0.00 0.00 1/1 nb_clients_distincts [17]
0.00 0.00 1/1 total_par_client [19]
0.00 0.00 1/1 total_par_jour [20]
0.00 0.00 1/1 meilleure_periode [16]
0.00 0.00 1/1 nb_facons_rendre [18]
0.00 0.00 1/1 empreinte [13]
-----------------------------------------------
0.00 0.00 149998/149998 generer_transactions [16]
[14] 0.0 0.00 0.00 149998 alea [14]
0.00 0.01 1/1 main [3]
[4] 50.0 0.00 0.01 1 nb_paires_opposees [4]
0.00 0.01 1/2 trier_par_montant [2]
0.00 0.00 1/1 compter_paires_triees [12]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[15] 0.0 0.00 0.00 1 empreinte [15]
0.00 0.00 817430/817430 trier_intervalle [1]
[5] 0.0 0.00 0.00 817430 comparer_transactions [5]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[16] 0.0 0.00 0.00 1 generer_transactions [16]
0.00 0.00 149998/149998 alea [14]
0.00 0.00 149998/149998 generer_transactions [14]
[6] 0.0 0.00 0.00 149998 alea [6]
-----------------------------------------------
0.00 0.00 30000/30000 main [3]
[7] 0.0 0.00 0.00 30000 categorie [7]
0.00 0.00 15066/15066 categorie_debit [9]
0.00 0.00 14934/14934 categorie_credit [10]
-----------------------------------------------
0.00 0.00 30000/30000 main [3]
[8] 0.0 0.00 0.00 30000 rechercher_par_id [8]
-----------------------------------------------
0.00 0.00 15066/15066 categorie [7]
[9] 0.0 0.00 0.00 15066 categorie_debit [9]
-----------------------------------------------
0.00 0.00 14934/14934 categorie [7]
[10] 0.0 0.00 0.00 14934 categorie_credit [10]
-----------------------------------------------
0.00 0.00 6017/6017 main [3]
[11] 0.0 0.00 0.00 6017 ajouter_ligne [11]
-----------------------------------------------
0.00 0.00 1/1 nb_paires_opposees [4]
[12] 0.0 0.00 0.00 1 compter_paires_triees [12]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[13] 0.0 0.00 0.00 1 empreinte [13]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[14] 0.0 0.00 0.00 1 generer_transactions [14]
0.00 0.00 149998/149998 alea [6]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[15] 0.0 0.00 0.00 1 indexer_par_id [15]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[16] 0.0 0.00 0.00 1 meilleure_periode [16]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[17] 0.0 0.00 0.00 1 nb_clients_distincts [17]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[18] 0.0 0.00 0.00 1 nb_facons_rendre [18]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[19] 0.0 0.00 0.00 1 total_par_client [19]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[20] 0.0 0.00 0.00 1 total_par_jour [20]
-----------------------------------------------
Index by function name
[13] _init [16] generer_transactions [4] nb_paires_opposees
[6] ajouter_ligne [7] longueur [3] rechercher_par_id
[14] alea [8] meilleure_periode [10] total_par_client
[12] categorie [9] nb_clients_distincts [11] total_par_jour
[15] empreinte [5] nb_facons_rendre [2] trier_par_montant
[11] ajouter_ligne [13] empreinte [8] rechercher_par_id
[6] alea [14] generer_transactions [19] total_par_client
[7] categorie [15] indexer_par_id [20] total_par_jour
[10] categorie_credit (banque.c) [16] meilleure_periode [1] trier_intervalle (banque.c)
[9] categorie_debit (banque.c) [17] nb_clients_distincts [2] trier_par_montant
[5] comparer_transactions (banque.c) [18] nb_facons_rendre
[12] compter_paires_triees (banque.c) [4] nb_paires_opposees
+126
View File
@@ -0,0 +1,126 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
100.00 0.01 0.01 2 5.00 5.00 trier_intervalle
0.00 0.01 0.00 817430 0.00 0.00 comparer_transactions
0.00 0.01 0.00 149998 0.00 0.00 alea
0.00 0.01 0.00 30000 0.00 0.00 categorie
0.00 0.01 0.00 30000 0.00 0.00 rechercher_par_id
0.00 0.01 0.00 15066 0.00 0.00 categorie_debit
0.00 0.01 0.00 14934 0.00 0.00 categorie_credit
0.00 0.01 0.00 6017 0.00 0.00 ajouter_ligne
0.00 0.01 0.00 2 0.00 5.00 trier_par_montant
0.00 0.01 0.00 1 0.00 0.00 compter_paires_triees
0.00 0.01 0.00 1 0.00 0.00 empreinte
0.00 0.01 0.00 1 0.00 0.00 generer_transactions
0.00 0.01 0.00 1 0.00 0.00 indexer_par_id
0.00 0.01 0.00 1 0.00 0.00 meilleure_periode
0.00 0.01 0.00 1 0.00 0.00 nb_clients_distincts
0.00 0.01 0.00 1 0.00 0.00 nb_facons_rendre
0.00 0.01 0.00 1 0.00 5.00 nb_paires_opposees
0.00 0.01 0.00 1 0.00 0.00 total_par_client
0.00 0.01 0.00 1 0.00 0.00 total_par_jour
Call graph
granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds
index % time self children called name
119996 trier_intervalle [1]
0.01 0.00 2/2 trier_par_montant [2]
[1] 100.0 0.01 0.00 2+119996 trier_intervalle [1]
0.00 0.00 817430/817430 comparer_transactions [5]
119996 trier_intervalle [1]
-----------------------------------------------
0.00 0.01 1/2 nb_paires_opposees [4]
0.00 0.01 1/2 main [3]
[2] 100.0 0.00 0.01 2 trier_par_montant [2]
0.01 0.00 2/2 trier_intervalle [1]
-----------------------------------------------
<spontaneous>
[3] 100.0 0.00 0.01 main [3]
0.00 0.01 1/1 nb_paires_opposees [4]
0.00 0.01 1/2 trier_par_montant [2]
0.00 0.00 30000/30000 rechercher_par_id [8]
0.00 0.00 30000/30000 categorie [7]
0.00 0.00 6017/6017 ajouter_ligne [11]
0.00 0.00 1/1 generer_transactions [14]
0.00 0.00 1/1 indexer_par_id [15]
0.00 0.00 1/1 nb_clients_distincts [17]
0.00 0.00 1/1 total_par_client [19]
0.00 0.00 1/1 total_par_jour [20]
0.00 0.00 1/1 meilleure_periode [16]
0.00 0.00 1/1 nb_facons_rendre [18]
0.00 0.00 1/1 empreinte [13]
-----------------------------------------------
0.00 0.01 1/1 main [3]
[4] 50.0 0.00 0.01 1 nb_paires_opposees [4]
0.00 0.01 1/2 trier_par_montant [2]
0.00 0.00 1/1 compter_paires_triees [12]
-----------------------------------------------
0.00 0.00 817430/817430 trier_intervalle [1]
[5] 0.0 0.00 0.00 817430 comparer_transactions [5]
-----------------------------------------------
0.00 0.00 149998/149998 generer_transactions [14]
[6] 0.0 0.00 0.00 149998 alea [6]
-----------------------------------------------
0.00 0.00 30000/30000 main [3]
[7] 0.0 0.00 0.00 30000 categorie [7]
0.00 0.00 15066/15066 categorie_debit [9]
0.00 0.00 14934/14934 categorie_credit [10]
-----------------------------------------------
0.00 0.00 30000/30000 main [3]
[8] 0.0 0.00 0.00 30000 rechercher_par_id [8]
-----------------------------------------------
0.00 0.00 15066/15066 categorie [7]
[9] 0.0 0.00 0.00 15066 categorie_debit [9]
-----------------------------------------------
0.00 0.00 14934/14934 categorie [7]
[10] 0.0 0.00 0.00 14934 categorie_credit [10]
-----------------------------------------------
0.00 0.00 6017/6017 main [3]
[11] 0.0 0.00 0.00 6017 ajouter_ligne [11]
-----------------------------------------------
0.00 0.00 1/1 nb_paires_opposees [4]
[12] 0.0 0.00 0.00 1 compter_paires_triees [12]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[13] 0.0 0.00 0.00 1 empreinte [13]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[14] 0.0 0.00 0.00 1 generer_transactions [14]
0.00 0.00 149998/149998 alea [6]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[15] 0.0 0.00 0.00 1 indexer_par_id [15]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[16] 0.0 0.00 0.00 1 meilleure_periode [16]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[17] 0.0 0.00 0.00 1 nb_clients_distincts [17]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[18] 0.0 0.00 0.00 1 nb_facons_rendre [18]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[19] 0.0 0.00 0.00 1 total_par_client [19]
-----------------------------------------------
0.00 0.00 1/1 main [3]
[20] 0.0 0.00 0.00 1 total_par_jour [20]
-----------------------------------------------
Index by function name
[11] ajouter_ligne [13] empreinte [8] rechercher_par_id
[6] alea [14] generer_transactions [19] total_par_client
[7] categorie [15] indexer_par_id [20] total_par_jour
[10] categorie_credit (banque.c) [16] meilleure_periode [1] trier_intervalle (banque.c)
[9] categorie_debit (banque.c) [17] nb_clients_distincts [2] trier_par_montant
[5] comparer_transactions (banque.c) [18] nb_facons_rendre
[12] compter_paires_triees (banque.c) [4] nb_paires_opposees
+135
View File
@@ -0,0 +1,135 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
57.25 1.19 1.19 400 2.96 3.69 trier_intervalle
14.01 1.48 0.29 163486000 0.00 0.00 comparer_transactions
6.76 1.61 0.14 200 0.70 0.75 generer_transactions
4.35 1.71 0.09 200 0.45 10.00 banque_main
3.86 1.78 0.08 200 0.40 0.40 compter_paires_triees
3.38 1.85 0.07 _init
2.90 1.92 0.06 200 0.30 0.30 empreinte
1.93 1.96 0.04 1203400 0.00 0.00 ajouter_ligne
0.97 1.98 0.02 6000000 0.00 0.00 categorie
0.97 2.00 0.02 200 0.10 0.10 nb_clients_distincts
0.97 2.02 0.02 200 0.10 0.10 total_par_client
0.48 2.02 0.01 29999600 0.00 0.00 alea
0.48 2.04 0.01 6000000 0.00 0.00 rechercher_par_id
0.48 2.04 0.01 3013200 0.00 0.00 categorie_debit
0.48 2.06 0.01 200 0.05 0.05 indexer_par_id
0.48 2.06 0.01 200 0.05 0.05 total_par_jour
0.24 2.07 0.01 400 0.01 3.70 trier_par_montant
0.00 2.07 0.00 2986800 0.00 0.00 categorie_credit
0.00 2.07 0.00 200 0.00 0.00 meilleure_periode
0.00 2.07 0.00 200 0.00 0.00 nb_facons_rendre
0.00 2.07 0.00 200 0.00 4.10 nb_paires_opposees
Call graph
granularity: each sample hit covers 4 byte(s) for 0.48% of 2.07 seconds
index % time self children called name
0.09 1.91 200/200 main [2]
[1] 96.6 0.09 1.91 200 banque_main [1]
0.00 0.82 200/200 nb_paires_opposees [5]
0.00 0.74 200/400 trier_par_montant [3]
0.14 0.01 200/200 generer_transactions [7]
0.06 0.00 200/200 empreinte [10]
0.04 0.00 1203400/1203400 ajouter_ligne [11]
0.02 0.01 6000000/6000000 categorie [12]
0.02 0.00 200/200 nb_clients_distincts [13]
0.02 0.00 200/200 total_par_client [14]
0.01 0.00 6000000/6000000 rechercher_par_id [16]
0.01 0.00 200/200 indexer_par_id [18]
0.01 0.00 200/200 total_par_jour [19]
0.00 0.00 200/200 meilleure_periode [21]
0.00 0.00 200/200 nb_facons_rendre [22]
-----------------------------------------------
<spontaneous>
[2] 96.6 0.00 2.00 main [2]
0.09 1.91 200/200 banque_main [1]
-----------------------------------------------
0.00 0.74 200/400 nb_paires_opposees [5]
0.00 0.74 200/400 banque_main [1]
[3] 71.5 0.01 1.48 400 trier_par_montant [3]
1.19 0.29 400/400 trier_intervalle [4]
-----------------------------------------------
23999200 trier_intervalle [4]
1.19 0.29 400/400 trier_par_montant [3]
[4] 71.3 1.19 0.29 400+23999200 trier_intervalle [4]
0.29 0.00 163486000/163486000 comparer_transactions [6]
23999200 trier_intervalle [4]
-----------------------------------------------
0.00 0.82 200/200 banque_main [1]
[5] 39.6 0.00 0.82 200 nb_paires_opposees [5]
0.00 0.74 200/400 trier_par_montant [3]
0.08 0.00 200/200 compter_paires_triees [8]
-----------------------------------------------
0.29 0.00 163486000/163486000 trier_intervalle [4]
[6] 14.0 0.29 0.00 163486000 comparer_transactions [6]
-----------------------------------------------
0.14 0.01 200/200 banque_main [1]
[7] 7.2 0.14 0.01 200 generer_transactions [7]
0.01 0.00 29999600/29999600 alea [15]
-----------------------------------------------
0.08 0.00 200/200 nb_paires_opposees [5]
[8] 3.9 0.08 0.00 200 compter_paires_triees [8]
-----------------------------------------------
<spontaneous>
[9] 3.4 0.07 0.00 _init [9]
-----------------------------------------------
0.06 0.00 200/200 banque_main [1]
[10] 2.9 0.06 0.00 200 empreinte [10]
-----------------------------------------------
0.04 0.00 1203400/1203400 banque_main [1]
[11] 1.9 0.04 0.00 1203400 ajouter_ligne [11]
-----------------------------------------------
0.02 0.01 6000000/6000000 banque_main [1]
[12] 1.4 0.02 0.01 6000000 categorie [12]
0.01 0.00 3013200/3013200 categorie_debit [17]
0.00 0.00 2986800/2986800 categorie_credit [20]
-----------------------------------------------
0.02 0.00 200/200 banque_main [1]
[13] 1.0 0.02 0.00 200 nb_clients_distincts [13]
-----------------------------------------------
0.02 0.00 200/200 banque_main [1]
[14] 1.0 0.02 0.00 200 total_par_client [14]
-----------------------------------------------
0.01 0.00 29999600/29999600 generer_transactions [7]
[15] 0.5 0.01 0.00 29999600 alea [15]
-----------------------------------------------
0.01 0.00 6000000/6000000 banque_main [1]
[16] 0.5 0.01 0.00 6000000 rechercher_par_id [16]
-----------------------------------------------
0.01 0.00 3013200/3013200 categorie [12]
[17] 0.5 0.01 0.00 3013200 categorie_debit [17]
-----------------------------------------------
0.01 0.00 200/200 banque_main [1]
[18] 0.5 0.01 0.00 200 indexer_par_id [18]
-----------------------------------------------
0.01 0.00 200/200 banque_main [1]
[19] 0.5 0.01 0.00 200 total_par_jour [19]
-----------------------------------------------
0.00 0.00 2986800/2986800 categorie [12]
[20] 0.0 0.00 0.00 2986800 categorie_credit [20]
-----------------------------------------------
0.00 0.00 200/200 banque_main [1]
[21] 0.0 0.00 0.00 200 meilleure_periode [21]
-----------------------------------------------
0.00 0.00 200/200 banque_main [1]
[22] 0.0 0.00 0.00 200 nb_facons_rendre [22]
-----------------------------------------------
Index by function name
[9] _init [6] comparer_transactions (banque.c) [22] nb_facons_rendre
[11] ajouter_ligne [8] compter_paires_triees (banque.c) [5] nb_paires_opposees
[15] alea [10] empreinte [16] rechercher_par_id
[1] banque_main [7] generer_transactions [14] total_par_client
[12] categorie [18] indexer_par_id [19] total_par_jour
[20] categorie_credit (banque.c) [21] meilleure_periode [4] trier_intervalle (banque.c)
[17] categorie_debit (banque.c) [13] nb_clients_distincts [3] trier_par_montant
+9
View File
@@ -0,0 +1,9 @@
Mesures en secondes, gcc -std=c11 -Wall -Wextra -Wpedantic -O2
Commande : build/banque, rapport écrit sur disque, temps réel (perf_counter).
Référence initiale, une exécution : 2.437683
Exécution 1 : 0.010910
Exécution 2 : 0.011111
Exécution 3 : 0.010734
Exécution 4 : 0.011242
Exécution 5 : 0.010525
Médiane après optimisation : 0.010910
+11
View File
@@ -0,0 +1,11 @@
fonction;empreinte;rapport_identique;temps_secondes;sha256_source
F1;ff68d260a7a58441;oui;0.941548;951d59178fee42db808259d2885504913006f2459be99a734619b322bf58011e
F2;ff68d260a7a58441;oui;0.939984;82088e5e81e3cd14319bbc3a3789ab76b3b1cdc54e92bf9e4811eadbce77d89a
F3;ff68d260a7a58441;oui;0.936668;e451e8d251c2194f63c631532d13b278242910977d7c597b9f27f5bbccb45522
F4;ff68d260a7a58441;oui;0.925947;d8ee027417c8bb3a49184191a27fcd4bd903c85341368c8574152171fae6086b
F5;ff68d260a7a58441;oui;0.844800;3ce8f93c61a31cea27e3da75c1edec7bde6ef0255037e1e39aa95680552e3f81
F6;ff68d260a7a58441;oui;0.643082;f3478bfeae098be4a35132c4e1b176c73ebe336134402419d1ddcc4607f3c45f
F7;ff68d260a7a58441;oui;0.382976;19a0887a08d835d94e0bf88491e9fa91810848eec0deb65cccdb7787e00a74f2
F8;ff68d260a7a58441;oui;0.131249;3e46c05cc55725fb26aa2b1f1e16b8882e6000708654638ef7f165c80ef0a2e8
F9;ff68d260a7a58441;oui;0.130851;98c643f223e4b721e01267431cb1bf8848a6772b6b3931ecd99731d0a7d0ffa8
F10;ff68d260a7a58441;oui;0.010753;504e876d5868aece17c7379d2e92f2e8dc8c49042f300bfe0b1114a0d8b13680
1 fonction empreinte rapport_identique temps_secondes sha256_source
2 F1 ff68d260a7a58441 oui 0.941548 951d59178fee42db808259d2885504913006f2459be99a734619b322bf58011e
3 F2 ff68d260a7a58441 oui 0.939984 82088e5e81e3cd14319bbc3a3789ab76b3b1cdc54e92bf9e4811eadbce77d89a
4 F3 ff68d260a7a58441 oui 0.936668 e451e8d251c2194f63c631532d13b278242910977d7c597b9f27f5bbccb45522
5 F4 ff68d260a7a58441 oui 0.925947 d8ee027417c8bb3a49184191a27fcd4bd903c85341368c8574152171fae6086b
6 F5 ff68d260a7a58441 oui 0.844800 3ce8f93c61a31cea27e3da75c1edec7bde6ef0255037e1e39aa95680552e3f81
7 F6 ff68d260a7a58441 oui 0.643082 f3478bfeae098be4a35132c4e1b176c73ebe336134402419d1ddcc4607f3c45f
8 F7 ff68d260a7a58441 oui 0.382976 19a0887a08d835d94e0bf88491e9fa91810848eec0deb65cccdb7787e00a74f2
9 F8 ff68d260a7a58441 oui 0.131249 3e46c05cc55725fb26aa2b1f1e16b8882e6000708654638ef7f165c80ef0a2e8
10 F9 ff68d260a7a58441 oui 0.130851 98c643f223e4b721e01267431cb1bf8848a6772b6b3931ecd99731d0a7d0ffa8
11 F10 ff68d260a7a58441 oui 0.010753 504e876d5868aece17c7379d2e92f2e8dc8c49042f300bfe0b1114a0d8b13680
+18 -14
View File
@@ -1,16 +1,20 @@
# Analyse des fonctions avec valeurs initiales
# Complexités avant et après l'étape 4
| ID | Fonction | Calcule | Actuelle | Objectif |
|:---:|---|---|:---:|---|
| F1 | `trier_par_montant` | tri des transactions | `O(n²)` | `O(n log n)` |
| F2 | `nb_clients_distincts` | clients différents | `O(n²)` | `O(n + C)` |
| F3 | `total_par_client` | solde par client | `O(n * c)` | `O(n + C)` |
| F4 | `total_par_jour` | solde par jour | `O(n * J)` | `O(n + J)` |
| F5 | `meilleure_periode` | période la plus rentable | `O(J³)` | `O(J)` |
| F6 | `rechercher_par_id` | recherche par identifiant | `O(n)` | `O(1)` par requête |
| F7 | `nb_paires_opposees` | paires `a + b = 0` | `O(n²)` | `O(n log n)` |
| F8 | `nb_facons_rendre` | rendu de monnaie | `O(Sⁿ)` | `O(S × k)` |
| F9 | `categorie` | catégorie d'une transaction | M = 14 | `M ≤ 10` |
| F10 | `ajouter_ligne` | construction du rapport | `O(n)` | `O(L)` au total |
| ID | Fonction | Avant | Après (objectif atteint) | M avant → après |
|:---:|---|---|---|:---:|
| F1 | `trier_par_montant` | `O(n²)` | `O(n log n)` au pire cas | 6 → 3 |
| F2 | `nb_clients_distincts` | `O(n²)` | `O(n + C)` | 5 → 3 |
| F3 | `total_par_client` | `O(n × C)` | `O(n + C)` | 4 → 3 |
| F4 | `total_par_jour` | `O(n × J)` | `O(n + J)` | 4 → 3 |
| F5 | `meilleure_periode` | `O(J³)` | `O(J)` | 5 → 4 |
| F6 | `rechercher_par_id` | `O(n)` par requête | `O(1)` par requête, après indexation `O(n)` | 3 → 3 |
| F7 | `nb_paires_opposees` | `O(n²)` | `O(n log n)` | 4 → 3 |
| F8 | `nb_facons_rendre` | `O(2^(S + k))`, borne générale | `O(S × k)` pour `k ≥ 1` | 4 → 5 |
| F9 | `categorie` | `O(1)`, M = 14 | `O(1)`, M = 3 (auxiliaires : 5 et 7) | 14 → 3 |
| F10 | `ajouter_ligne` | `O(L²)` au total, lignes de taille bornée | `O(L)` au total | 2 → 2 |
`n` transactions · `C` clients · `J` jours · `S` somme · `k` pièces · `L` longueur du rapport · **F5, F7, F8** : les plus difficiles
`n` transactions · `C` clients · `J` jours · `S` somme · `k` pièces · `L` longueur du rapport.
Justifications, mémoire auxiliaire, M des fonctions auxiliaires et validations
dans `COMPTE_RENDU.md`, section 4. Les constantes et le format du rapport
sont inchangés ; l'empreinte reste `ff68d260a7a58441` à chaque optimisation.
+319
View File
@@ -0,0 +1,319 @@
/* Cas limites et comparaisons avec des algorithmes de référence simples. */
#define main banque_main
#include "../banque.c"
#undef main
#include <assert.h>
#include <limits.h>
static int comparer_reference(const void *a, const void *b)
{
const Transaction *ta = a;
const Transaction *tb = b;
if (ta->montant < tb->montant)
return -1;
if (ta->montant > tb->montant)
return 1;
if (ta->id < tb->id)
return -1;
return ta->id > tb->id;
}
static void verifier_tri(Transaction *t, int n)
{
Transaction attendu[128];
assert(n <= 128);
memcpy(attendu, t, (size_t)n * sizeof(*t));
qsort(attendu, (size_t)n, sizeof(*attendu), comparer_reference);
trier_par_montant(t, n);
for (int i = 0; i < n; i++) {
assert(t[i].id == attendu[i].id);
assert(t[i].client == attendu[i].client);
assert(t[i].jour == attendu[i].jour);
assert(t[i].montant == attendu[i].montant);
}
}
static void tester_tri(void)
{
trier_par_montant(NULL, 0);
Transaction limites[] = {
{INT_MAX, 1, 2, LONG_MAX}, {3, 4, 5, 0},
{INT_MIN, 6, 7, LONG_MIN}, {1, 8, 9, 0}, {2, 10, 11, LONG_MAX}
};
verifier_tri(limites, 5);
verifier_tri(limites, 5); /* déjà trié */
Transaction t[128];
for (int n = 1; n <= 128; n++) {
for (int i = 0; i < n; i++)
t[i] = (Transaction){n - i, i, i + 1, (i * 37) % 11 - 5};
verifier_tri(t, n);
for (int i = 0; i < n; i++)
t[i] = (Transaction){i, i, i + 1, n - i};
verifier_tri(t, n); /* ordre inverse */
}
}
static void tester_clients_distincts(void)
{
assert(nb_clients_distincts(NULL, 0) == 0);
Transaction t[NB_CLIENTS];
for (int i = 0; i < NB_CLIENTS; i++)
t[i] = (Transaction){i, NB_CLIENTS - 1, 0, 0};
assert(nb_clients_distincts(t, NB_CLIENTS) == 1);
t[0].client = 0;
assert(nb_clients_distincts(t, NB_CLIENTS) == 2);
for (int i = 0; i < NB_CLIENTS; i++)
t[i].client = i;
assert(nb_clients_distincts(t, NB_CLIENTS) == NB_CLIENTS);
assert(nb_clients_distincts(t, 1) == 1);
}
static void tester_totaux_clients(void)
{
Transaction t[] = {
{0, 0, 0, 100}, {1, NB_CLIENTS - 1, 1, -50},
{2, 0, 2, -40}, {3, NB_CLIENTS - 1, 3, 50}, {4, 7, 4, 0}
};
long totaux[NB_CLIENTS];
for (int i = 0; i < NB_CLIENTS; i++)
totaux[i] = 123;
total_par_client(t, 5, totaux);
for (int c = 0; c < NB_CLIENTS; c++) {
long attendu = 0;
for (int i = 0; i < 5; i++)
if (t[i].client == c)
attendu += t[i].montant;
assert(totaux[c] == attendu);
}
total_par_client(NULL, 0, totaux);
for (int c = 0; c < NB_CLIENTS; c++)
assert(totaux[c] == 0);
}
static void tester_totaux_jours(void)
{
Transaction t[] = {
{0, 0, NB_JOURS - 1, 100}, {1, 1, 0, -50},
{2, 2, NB_JOURS - 1, -40}, {3, 3, 0, 50}, {4, 4, 7, 0}
};
long totaux[NB_JOURS];
for (int i = 0; i < NB_JOURS; i++)
totaux[i] = 123;
total_par_jour(t, 5, totaux);
for (int j = 0; j < NB_JOURS; j++) {
long attendu = 0;
for (int i = 0; i < 5; i++)
if (t[i].jour == j)
attendu += t[i].montant;
assert(totaux[j] == attendu);
}
total_par_jour(NULL, 0, totaux);
for (int j = 0; j < NB_JOURS; j++)
assert(totaux[j] == 0);
}
static long periode_reference(const long *t, int n, int *debut, int *fin)
{
long meilleur = t[0];
*debut = 0;
*fin = 0;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
long somme = 0;
for (int k = i; k <= j; k++)
somme += t[k];
if (somme > meilleur) {
meilleur = somme;
*debut = i;
*fin = j;
}
}
}
return meilleur;
}
static void tester_periodes(void)
{
long t[7];
int nb_cas = 1;
for (int n = 1; n <= 7; n++) {
nb_cas *= 5;
for (int cas = 0; cas < nb_cas; cas++) {
int valeurs = cas;
for (int i = 0; i < n; i++) {
t[i] = valeurs % 5 - 2;
valeurs /= 5;
}
int debut, fin, debut_attendu, fin_attendue;
long attendu = periode_reference(t, n, &debut_attendu, &fin_attendue);
assert(meilleure_periode(t, n, &debut, &fin) == attendu);
assert(debut == debut_attendu);
assert(fin == fin_attendue);
}
}
}
static void tester_recherches(void)
{
assert(rechercher_par_id(NULL, 0, 0) == NULL);
assert(rechercher_par_id(NULL, 0, -1) == NULL);
Transaction t[128];
const Transaction *index_ids[128];
for (int n = 1; n <= 128; n++) {
for (int i = 0; i < n; i++)
t[i] = (Transaction){n - 1 - i, i, i, i * 77 - 12};
indexer_par_id(t, n, index_ids);
for (int id = 0; id < n; id++) {
const Transaction *attendu = NULL;
for (int i = 0; i < n; i++)
if (t[i].id == id)
attendu = &t[i];
assert(rechercher_par_id(index_ids, n, id) == attendu);
}
assert(rechercher_par_id(index_ids, n, -1) == NULL);
assert(rechercher_par_id(index_ids, n, n) == NULL);
assert(rechercher_par_id(index_ids, n, INT_MAX) == NULL);
}
}
static long paires_reference(const Transaction *t, int n)
{
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;
}
static void tester_paires(void)
{
assert(nb_paires_opposees(NULL, 0) == 0);
Transaction t[7], original[7];
int nb_cas = 1;
for (int n = 1; n <= 7; n++) {
nb_cas *= 5;
for (int cas = 0; cas < nb_cas; cas++) {
int valeurs = cas;
for (int i = 0; i < n; i++) {
t[i] = (Transaction){i, i, i, valeurs % 5 - 2};
valeurs /= 5;
}
memcpy(original, t, (size_t)n * sizeof(*t));
assert(nb_paires_opposees(t, n) == paires_reference(t, n));
assert(memcmp(original, t, (size_t)n * sizeof(*t)) == 0);
}
}
}
static long monnaie_reference(int somme, int nb_pieces)
{
if (somme == 0)
return 1;
if (somme < 0 || nb_pieces == 0)
return 0;
return monnaie_reference(somme - PIECES[nb_pieces - 1], nb_pieces) +
monnaie_reference(somme, nb_pieces - 1);
}
static void tester_monnaie(void)
{
for (int s = -2; s <= 40; s++)
for (int k = 0; k <= NB_PIECES; k++)
assert(nb_facons_rendre(s, k) == monnaie_reference(s, k));
}
static int categorie_reference(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) {
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;
}
}
static void tester_categories(void)
{
const long montants[] = {
LONG_MIN, -20001, -20000, -19999, -5001, -5000, -4999, -1,
0, 1, 999, 1000, 1001, 9999, 10000, 10001, 29999, 30000, 30001, LONG_MAX
};
for (size_t m = 0; m < sizeof(montants) / sizeof(*montants); m++) {
for (int c = 0; c < 20; c++) {
for (int j = 0; j < 210; j++) {
Transaction t = {0, c, j, montants[m]};
assert(categorie(&t) == categorie_reference(&t));
}
}
}
}
static void tester_rapport(void)
{
char rapport[4097] = "";
char attendu[4097] = "";
char *fin = rapport;
size_t taille = 0;
assert(ajouter_ligne(fin, "") == fin);
for (int i = 0; i < 200; i++) {
char ligne[20];
int longueur_ligne = snprintf(ligne, sizeof(ligne), "ligne;%d\n", i);
assert(longueur_ligne > 0);
assert((size_t)longueur_ligne < sizeof(ligne));
memcpy(attendu + taille, ligne, (size_t)longueur_ligne + 1);
taille += (size_t)longueur_ligne;
fin = ajouter_ligne(fin, ligne);
assert(fin == rapport + taille);
assert(*fin == '\0');
assert(strcmp(rapport, attendu) == 0);
assert(ajouter_ligne(fin, "") == fin);
}
char longue_ligne[4097];
memset(longue_ligne, 'x', sizeof(longue_ligne) - 1);
longue_ligne[sizeof(longue_ligne) - 1] = '\0';
rapport[0] = '\0';
fin = ajouter_ligne(rapport, longue_ligne);
assert(fin == rapport + sizeof(rapport) - 1);
assert(strcmp(rapport, longue_ligne) == 0);
}
int main(void)
{
tester_tri();
tester_clients_distincts();
tester_totaux_clients();
tester_totaux_jours();
tester_periodes();
tester_recherches();
tester_paires();
tester_monnaie();
tester_categories();
tester_rapport();
puts("Tests banque : OK");
return 0;
}