forked from abraham/2026_TD3_DEV51_Qualite_Algo
Compare commits
2 Commits
82a77ec17c
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cbc0cf780e | |||
| 64e3432b33 |
@@ -1,337 +1,3 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
/*
|
|
||||||
* 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 */
|
|
||||||
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 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 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++)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
/*
|
/*
|
||||||
* R5.A.04 - Qualité Algorithmique
|
* R5.A.04 - Qualité Algorithmique
|
||||||
* TD Complexité algorithmique : analyse des transactions d'une banque
|
* TD Complexité algorithmique : analyse des transactions d'une banque
|
||||||
@@ -422,28 +88,29 @@ void trier_par_montant(Transaction *t, int n)
|
|||||||
/* F2 : nombre de clients ayant au moins une transaction */
|
/* F2 : nombre de clients ayant au moins une transaction */
|
||||||
int nb_clients_distincts(const Transaction *t, int n)
|
int nb_clients_distincts(const Transaction *t, int n)
|
||||||
{
|
{
|
||||||
int clients_vus[NB_CLIENTS] = {0};
|
|
||||||
int nb = 0;
|
int nb = 0;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
int client = t[i].client;
|
int deja_vu = 0;
|
||||||
if (!clients_vus[client]) {
|
for (int j = 0; j < i; j++) {
|
||||||
clients_vus[client] = 1;
|
if (t[j].client == t[i].client) {
|
||||||
nb++;
|
deja_vu = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!deja_vu)
|
||||||
|
nb++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nb;
|
return nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* F3 : solde total de chaque client */
|
/* F3 : solde total de chaque client en O(n + c) */
|
||||||
void total_par_client(const Transaction *t, int n, long *totaux)
|
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;
|
totaux[c] = 0;
|
||||||
for (int i = 0; i < n; i++)
|
}
|
||||||
if (t[i].client == c)
|
for (int i = 0; i < n; i++) {
|
||||||
totaux[c] += t[i].montant;
|
totaux[t[i].client] += t[i].montant;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,30 +119,36 @@ 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;
|
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 */
|
/* F5 : période de jours consécutifs dont la somme des soldes est maximale en O(J) */
|
||||||
long meilleure_periode(const long *totaux, int nb_jours, int *debut, int *fin)
|
long meilleure_periode(const long *totaux, int nb_jours, int *debut, int *fin)
|
||||||
{
|
{
|
||||||
long meilleur = totaux[0];
|
long meilleur = totaux[0];
|
||||||
*debut = 0;
|
*debut = 0;
|
||||||
*fin = 0;
|
*fin = 0;
|
||||||
for (int i = 0; i < nb_jours; i++) {
|
|
||||||
for (int j = i; j < nb_jours; j++) {
|
long somme_courante = totaux[0];
|
||||||
long somme = 0;
|
int debut_courant = 0;
|
||||||
for (int k = i; k <= j; k++)
|
|
||||||
somme += totaux[k];
|
for (int j = 1; j < nb_jours; j++) {
|
||||||
if (somme > meilleur) {
|
if (somme_courante < 0) {
|
||||||
meilleur = somme;
|
somme_courante = totaux[j];
|
||||||
*debut = i;
|
debut_courant = j;
|
||||||
*fin = j;
|
} else {
|
||||||
}
|
somme_courante += totaux[j];
|
||||||
|
}
|
||||||
|
if (somme_courante > meilleur) {
|
||||||
|
meilleur = somme_courante;
|
||||||
|
*debut = debut_courant;
|
||||||
|
*fin = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return meilleur;
|
return meilleur;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,13 +162,45 @@ const Transaction *rechercher_par_id(const Transaction *t, int n, int id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */
|
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */
|
||||||
|
|
||||||
|
|
||||||
|
static int cmp_long(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
long x = *(const long *)a;
|
||||||
|
long y = *(const long *)b;
|
||||||
|
return (x > y) - (x < y);
|
||||||
|
}
|
||||||
|
|
||||||
long nb_paires_opposees(const Transaction *t, int n)
|
long nb_paires_opposees(const Transaction *t, int n)
|
||||||
{
|
{
|
||||||
|
/* 1. Copier et trier les montants : O(n log n) */
|
||||||
|
long *m = malloc(n * sizeof(long));
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
m[i] = t[i].montant;
|
||||||
|
}
|
||||||
|
qsort(m, n, sizeof(long), cmp_long);
|
||||||
|
|
||||||
|
/* 2. Deux pointeurs : O(n) au lieu de O(n²) */
|
||||||
long nb = 0;
|
long nb = 0;
|
||||||
for (int i = 0; i < n; i++)
|
int g = 0, d = n - 1;
|
||||||
for (int j = i + 1; j < n; j++)
|
|
||||||
if (t[i].montant + t[j].montant == 0)
|
while (g < d) {
|
||||||
|
long somme = m[g] + m[d];
|
||||||
|
|
||||||
|
if (somme < 0) {
|
||||||
|
g++;
|
||||||
|
} else if (somme > 0) {
|
||||||
|
d--;
|
||||||
|
} else {
|
||||||
|
/* somme == 0 : on compte tous les m[d] identiques en face de m[g] */
|
||||||
|
for (int k = d; k > g && m[k] == m[d]; k--) {
|
||||||
nb++;
|
nb++;
|
||||||
|
}
|
||||||
|
g++; /* Puis on passe simplement au m[g] suivant ! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(m);
|
||||||
return nb;
|
return nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,4 +368,3 @@ int main(void)
|
|||||||
free(t);
|
free(t);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
>>>>>>> 97155f26889184ac9ab593d6aadb23cbdbce96c2
|
|
||||||
|
|||||||
Reference in New Issue
Block a user