Files
2026_TD3_DEV51_Qualite_Algo/banque.c
T

400 lines
12 KiB
C
Raw Normal View History

2026-10-21 09:52:24 +02:00
/*
* 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 <stdio.h>
#include <stdlib.h>
#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 */
2026-10-21 11:04:07 +02:00
static int comparer_transactions(const void *a, const void *b)
{
const Transaction *x = a, *y = b;
if (x->montant != y->montant)
return (x->montant > y->montant) - (x->montant < y->montant);
return (x->id > y->id) - (x->id < y->id);
}
2026-10-21 09:52:24 +02:00
void trier_par_montant(Transaction *t, int n)
{
2026-10-21 11:04:07 +02:00
qsort(t, n, sizeof *t, comparer_transactions);
2026-10-21 09:52:24 +02:00
}
/* F2 : nombre de clients ayant au moins une transaction */
int nb_clients_distincts(const Transaction *t, int n)
{
2026-10-21 11:08:30 +02:00
int nbr_clients = 0;
int *deja_vu = (int*) calloc(NB_CLIENTS,sizeof(int));
for (int i = 0 ; i < n ; i++){
if (deja_vu[t[i].client] == 0){
deja_vu[t[i].client] = 1;
nbr_clients +=1;
}
2026-10-21 09:52:24 +02:00
}
2026-10-21 11:08:30 +02:00
free(deja_vu);
return nbr_clients;
2026-10-21 09:52:24 +02:00
}
/* 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;
}
2026-10-21 10:38:30 +02:00
for (int i = 0; i < n; i++)
totaux[t[i].client] += t[i].montant;
2026-10-21 09:52:24 +02:00
}
/* F4 : solde total de chaque jour */
void total_par_jour(const Transaction *t, int n, long *totaux)
{
2026-10-21 10:48:28 +02:00
for (int j = 0; j < NB_JOURS; j++)
2026-10-21 09:52:24 +02:00
totaux[j] = 0;
2026-10-21 10:48:28 +02:00
for (int i = 0; i < n; i++) {
int jour = t[i].jour;
if (jour >= 0 && jour < NB_JOURS)
totaux[jour] += t[i].montant;
2026-10-21 09:52:24 +02:00
}
}
/* 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];
2026-10-21 11:43:55 +02:00
long somme_courante = totaux[0];
int debut_courant = 0;
2026-10-21 09:52:24 +02:00
*debut = 0;
*fin = 0;
2026-10-21 11:43:55 +02:00
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;
2026-10-21 09:52:24 +02:00
}
}
return meilleur;
}
/* F6 : retrouve une transaction à partir de son id */
const Transaction *rechercher_par_id(const Transaction *t, int n, int id)
{
2026-10-21 11:39:51 +02:00
static const Transaction *index[NB_TRANSACTIONS];
static const Transaction *transactions_indexees;
static int taille_indexee;
if (n <= NB_TRANSACTIONS && (t != transactions_indexees || n != taille_indexee)) {
for (int i = 0; i < n; i++)
index[t[i].id] = &t[i];
transactions_indexees = t;
taille_indexee = n;
}
if (n <= NB_TRANSACTIONS && id >= 0 && id < n && t == transactions_indexees)
return index[id];
2026-10-21 09:52:24 +02:00
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) */
2026-10-21 11:43:23 +02:00
static int cmp_long(const void *a, const void *b)
{
long x = *(const long *)a, y = *(const long *)b;
return (x > y) - (x < y);
}
2026-10-21 09:52:24 +02:00
long nb_paires_opposees(const Transaction *t, int n)
{
2026-10-21 11:43:23 +02:00
if (n < 2)
return 0;
long *m = malloc(n * sizeof(long));
if (!m)
return 0;
2026-10-21 09:52:24 +02:00
for (int i = 0; i < n; i++)
2026-10-21 11:43:23 +02:00
m[i] = t[i].montant;
qsort(m, n, sizeof(long), cmp_long); /* O(n log n) */
long nb = 0;
/* zéros : tous les montants nuls forment un bloc contigu */
int deb = 0;
while (deb < n && m[deb] < 0)
deb++;
int fin = deb;
while (fin < n && m[fin] == 0)
fin++;
long z = fin - deb;
nb += z * (z - 1) / 2;
/* deux pointeurs : négatifs depuis la fin de la zone négative,
positifs depuis le début de la zone positive (fin) */
int i = deb - 1; /* plus grand négatif : le plus proche de 0 */
int j = fin; /* plus petit positif : le plus proche de 0 */
while (i >= 0 && j < n) {
long neg = -m[i], pos = m[j];
if (neg < pos) {
i--;
} else if (neg > pos) {
j++;
} else {
long v = pos, a = 0, b = 0;
while (i >= 0 && -m[i] == v) { a++; i--; }
while (j < n && m[j] == v) { b++; j++; }
nb += a * b;
}
}
free(m);
2026-10-21 09:52:24 +02:00
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)
{
2026-10-21 11:35:22 +02:00
if (somme < 0)
2026-10-21 09:52:24 +02:00
return 0;
2026-10-21 11:35:22 +02:00
if (nb_pieces == 0)
return somme == 0;
long dp[somme + 1];
for (int s = 0; s <= somme; s++)
dp[s] = 1; /* pièce de 1 : une seule façon */
for (int p = 1; p < nb_pieces; p++) {
int c = PIECES[p];
for (int s = c; s <= somme; s++)
dp[s] += dp[s - c];
}
return dp[somme];
2026-10-21 09:52:24 +02:00
}
2026-10-21 11:32:21 +02:00
static int categorie_debit(const Transaction *t)
{
if (t->montant < -20000) {
if (t->jour % 7 == 5 || t->jour % 7 == 6)
return 1;
return 2;
}
if (t->montant < -5000)
return 3;
if (t->client % 10 == 0)
return 4;
return 5;
}
2026-10-21 11:24:55 +02:00
static int categorie_credit(const Transaction *t)
{
2026-10-21 11:32:21 +02:00
if (t->montant > 30000) {
2026-10-21 11:24:55 +02:00
if (t->client % 10 == 0 && t->jour % 7 < 5)
2026-10-21 11:32:21 +02:00
return 6;
2026-10-21 11:24:55 +02:00
return 7;
2026-10-21 11:32:21 +02:00
}
2026-10-21 11:24:55 +02:00
if (t->montant > 10000)
2026-10-21 11:32:21 +02:00
return 8;
if (t->montant > 1000) {
2026-10-21 11:24:55 +02:00
if (t->jour % 30 == 0)
2026-10-21 11:32:21 +02:00
return 9;
2026-10-21 11:24:55 +02:00
return 10;
}
return 11;
}
2026-10-21 09:52:24 +02:00
/* F9 : catégorie d'une transaction (pour les statistiques) */
int categorie(const Transaction *t)
{
2026-10-21 11:24:55 +02:00
if (t->montant < 0)
2026-10-21 11:32:21 +02:00
return categorie_debit(t);
2026-10-21 11:24:55 +02:00
if (t->montant == 0)
2026-10-21 11:32:21 +02:00
return 0;
2026-10-21 11:24:55 +02:00
return categorie_credit(t);
2026-10-21 09:52:24 +02:00
}
2026-10-21 11:24:55 +02:00
2026-10-21 09:52:24 +02:00
/* F10 : ajoute une ligne à la fin du rapport */
2026-10-21 11:32:21 +02:00
void ajouter_ligne(char **fin_rapport, const char *ligne)
2026-10-21 09:52:24 +02:00
{
2026-10-21 11:32:21 +02:00
while (*ligne != '\0') {
**fin_rapport = *ligne;
(*fin_rapport)++;
ligne++;
2026-10-21 09:52:24 +02:00
}
2026-10-21 11:32:21 +02:00
**fin_rapport = '\0';
2026-10-21 09:52:24 +02:00
}
/* ------------------------------------------------------------------ */
/* 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);
2026-10-21 11:32:21 +02:00
char *fin_rapport = rapport;
2026-10-21 09:52:24 +02:00
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);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
snprintf(ligne, sizeof(ligne), "somme des requetes;%ld\n", somme_requetes);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
snprintf(ligne, sizeof(ligne), "meilleure periode;%d;%d;%ld\n", debut, fin, meilleure);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
snprintf(ligne, sizeof(ligne), "paires opposees;%ld\n", nb_paires);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
snprintf(ligne, sizeof(ligne), "facons de rendre %d;%ld\n", MONTANT_MONNAIE, nb_facons);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
for (int c = 0; c < 12; c++) {
snprintf(ligne, sizeof(ligne), "categorie;%d;%d\n", c, categories[c]);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
}
for (int c = 0; c < NB_CLIENTS; c++) {
snprintf(ligne, sizeof(ligne), "client;%d;%ld\n", c, totaux_clients[c]);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
}
for (int j = 0; j < NB_JOURS; j++) {
snprintf(ligne, sizeof(ligne), "jour;%d;%ld\n", j, totaux_jours[j]);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
}
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);
2026-10-21 11:32:21 +02:00
ajouter_ligne(&fin_rapport, ligne);
2026-10-21 09:52:24 +02:00
}
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;
}