/* * 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 = 1; i < n; i++) { Transaction cle = t[i]; int j = i - 1; while (j >= 0 && (t[j].montant > cle.montant || (t[j].montant == cle.montant && t[j].id > cle.id))) { t[j + 1] = t[j]; j--; } t[j + 1] = cle; } } /* F2 : nombre de clients ayant au moins une transaction */ int nb_clients_distincts(const Transaction *t, int n) { int clients_vus[NB_CLIENTS] = {0}; int nb = 0; for (int i = 0; i < n; i++) { int client = t[i].client; if (!clients_vus[client]) { clients_vus[client] = 1; nb++; } } return nb; } /* F3 : solde total de chaque client en O(n + c) */ 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++) { 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++) { totaux[j] = 0; } 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 */ long meilleure_periode(const long *totaux, int nb_jours, int *debut, int *fin) { long meilleur = totaux[0]; *debut = 0; *fin = 0; long somme_courante = totaux[0]; int debut_courant = 0; for (int j = 1; j < nb_jours; j++) { if (somme_courante < 0) { somme_courante = totaux[j]; debut_courant = j; } else { somme_courante += totaux[j]; } if (somme_courante > meilleur) { meilleur = somme_courante; *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) { 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 || nb_pieces < 0) return 0; if (somme == 0) return 1; if (nb_pieces == 0) return 0; if (nb_pieces > NB_PIECES) nb_pieces = NB_PIECES; long *combinaisons = malloc((somme + 1) * sizeof(long)); if (combinaisons == NULL) return 0; for (int montant = 0; montant <= somme; montant++) combinaisons[montant] = 0; combinaisons[0] = 1; for (int i = 0; i < nb_pieces; i++) { for (int montant = PIECES[i]; montant <= somme; montant++) combinaisons[montant] += combinaisons[montant - PIECES[i]]; } long resultat = combinaisons[somme]; free(combinaisons); return resultat; } /* 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) { static const char *chaine_connue = NULL; static int n_connu = 0; if (s != chaine_connue || s[0] == '\0') { chaine_connue = s; n_connu = 0; } while (s[n_connu] != '\0') n_connu++; return n_connu; } 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; }