From e337ed52366e6a2167a53b360da3bbadd69d9120 Mon Sep 17 00:00:00 2001 From: Maxime Menault Date: Wed, 21 Oct 2026 09:52:24 +0200 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9verser=20les=20fichiers=20vers=20?= =?UTF-8?q?"/"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- banque.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 banque.c diff --git a/banque.c b/banque.c new file mode 100644 index 0000000..02b0e39 --- /dev/null +++ b/banque.c @@ -0,0 +1,332 @@ +/* + * R5.A.04 - Qualité Algorithmique + * TD Complexité algorithmique : analyse des transactions d'une banque + * + * Le programme génère des transactions pseudo-aléatoires (toujours les + * mêmes, grâce à une graine fixe), puis calcule plusieurs indicateurs + * et produit un rapport dans rapport.txt. + * + * L'empreinte affichée à la fin doit rester IDENTIQUE après vos + * optimisations : elle prouve que le résultat n'a pas changé. + * + * Interdit : modifier les constantes, la génération des données, + * le format du rapport, ou écrire les résultats « en dur ». + * + * Compilation : gcc -O2 banque.c -o banque + * Profiling : gcc -O0 -g -pg banque.c -o banque && ./banque && gprof ./banque gmon.out + */ + +#include +#include + +#define NB_TRANSACTIONS 30000 +#define NB_CLIENTS 2000 +#define NB_JOURS 1000 +#define NB_REQUETES 30000 +#define MONTANT_MONNAIE 400 +#define GRAINE 20261020UL + +typedef struct { + int id; + int client; + int jour; + long montant; /* en centimes, négatif pour un débit */ +} Transaction; + +/* ------------------------------------------------------------------ */ +/* Données (ne pas modifier) */ +/* ------------------------------------------------------------------ */ + +static unsigned long etat_alea = GRAINE; + +unsigned long alea(void) +{ + etat_alea = etat_alea * 6364136223846793005UL + 1442695040888963407UL; + return etat_alea >> 33; +} + +Transaction *generer_transactions(int n) +{ + Transaction *t = malloc(n * sizeof(Transaction)); + for (int i = 0; i < n; i++) { + t[i].id = i; + t[i].client = alea() % NB_CLIENTS; + t[i].jour = alea() % NB_JOURS; + if (i > 0 && alea() % 20 == 0) + t[i].montant = -t[alea() % i].montant; /* opération inverse */ + else + t[i].montant = (long)(alea() % 100001) - 50000; + } + for (int i = n - 1; i > 0; i--) { /* mélange des id */ + int j = alea() % (i + 1); + int tmp = t[i].id; + t[i].id = t[j].id; + t[j].id = tmp; + } + return t; +} + +/* ------------------------------------------------------------------ */ +/* Fonctions à analyser et à optimiser */ +/* ------------------------------------------------------------------ */ + +/* F1 : trie les transactions par montant croissant, puis par id */ +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; + } + } + } +} + +/* F2 : nombre de clients ayant au moins une transaction */ +int nb_clients_distincts(const Transaction *t, int n) +{ + 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) + 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++) { + totaux[c] = 0; + for (int i = 0; i < n; i++) + if (t[i].client == c) + totaux[c] += 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++) { + totaux[j] = 0; + for (int i = 0; i < n; i++) + if (t[i].jour == j) + totaux[j] += t[i].montant; + } +} + +/* 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 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; + } + } + } + return meilleur; +} + +/* F6 : retrouve une transaction à partir de son id */ +const Transaction *rechercher_par_id(const Transaction *t, int n, int id) +{ + for (int i = 0; i < n; i++) + if (t[i].id == id) + return &t[i]; + return NULL; +} + +/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */ +long nb_paires_opposees(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; +} + +/* 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); +} + +/* F9 : catégorie d'une transaction (pour les statistiques) */ +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) { + 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; + } +} + +/* F10 : ajoute une ligne à la fin du rapport */ +int longueur(const char *s) +{ + 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'; +} + +/* ------------------------------------------------------------------ */ +/* Programme principal (à adapter si vous changez les signatures) */ +/* ------------------------------------------------------------------ */ + +unsigned long empreinte(const char *s) +{ + unsigned long h = 1469598103934665603UL; + for (int i = 0; s[i] != '\0'; i++) { + h ^= (unsigned char)s[i]; + h *= 1099511628211UL; + } + return h; +} + +int main(void) +{ + Transaction *t = generer_transactions(NB_TRANSACTIONS); + char *rapport = malloc(1 << 20); + char ligne[128]; + rapport[0] = '\0'; + + /* recherches par id, dans l'ordre d'origine */ + 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)); + if (r != NULL) + somme_requetes += r->montant; + } + + int nb_clients = nb_clients_distincts(t, NB_TRANSACTIONS); + + static long totaux_clients[NB_CLIENTS]; + total_par_client(t, NB_TRANSACTIONS, totaux_clients); + + static long totaux_jours[NB_JOURS]; + total_par_jour(t, NB_TRANSACTIONS, totaux_jours); + int debut, fin; + long meilleure = meilleure_periode(totaux_jours, NB_JOURS, &debut, &fin); + + long nb_paires = nb_paires_opposees(t, NB_TRANSACTIONS); + + int categories[12] = {0}; + for (int i = 0; i < NB_TRANSACTIONS; i++) + categories[categorie(&t[i])]++; + + trier_par_montant(t, NB_TRANSACTIONS); + + long nb_facons = nb_facons_rendre(MONTANT_MONNAIE, NB_PIECES); + + /* rapport */ + snprintf(ligne, sizeof(ligne), "clients distincts;%d\n", nb_clients); + ajouter_ligne(rapport, ligne); + snprintf(ligne, sizeof(ligne), "somme des requetes;%ld\n", somme_requetes); + ajouter_ligne(rapport, ligne); + snprintf(ligne, sizeof(ligne), "meilleure periode;%d;%d;%ld\n", debut, fin, meilleure); + ajouter_ligne(rapport, ligne); + snprintf(ligne, sizeof(ligne), "paires opposees;%ld\n", nb_paires); + ajouter_ligne(rapport, ligne); + snprintf(ligne, sizeof(ligne), "facons de rendre %d;%ld\n", MONTANT_MONNAIE, nb_facons); + ajouter_ligne(rapport, ligne); + for (int c = 0; c < 12; c++) { + snprintf(ligne, sizeof(ligne), "categorie;%d;%d\n", c, categories[c]); + ajouter_ligne(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); + } + for (int j = 0; j < NB_JOURS; j++) { + snprintf(ligne, sizeof(ligne), "jour;%d;%ld\n", j, totaux_jours[j]); + ajouter_ligne(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); + } + + FILE *f = fopen("rapport.txt", "w"); + if (f != NULL) { + fputs(rapport, f); + fclose(f); + } + + printf("Clients distincts : %d\n", nb_clients); + printf("Somme des requêtes : %ld\n", somme_requetes); + printf("Meilleure période : jours %d à %d (%ld)\n", debut, fin, meilleure); + printf("Paires opposées : %ld\n", nb_paires); + printf("Façons de rendre %d : %ld\n", MONTANT_MONNAIE, nb_facons); + printf("Empreinte : %016lx\n", empreinte(rapport)); + + free(rapport); + free(t); + return 0; +}