diff --git a/COMPTE_RENDU.md b/COMPTE_RENDU.md index d8b58f9..6a9163c 100644 --- a/COMPTE_RENDU.md +++ b/COMPTE_RENDU.md @@ -313,3 +313,21 @@ 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. diff --git a/banque.c b/banque.c index 9c06b76..0a23675 100644 --- a/banque.c +++ b/banque.c @@ -186,13 +186,53 @@ const Transaction *rechercher_par_id(const Transaction *const *index_ids, int n, } /* 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; } diff --git a/tests/test_banque.c b/tests/test_banque.c index b98ade6..e5f7db7 100644 --- a/tests/test_banque.c +++ b/tests/test_banque.c @@ -177,6 +177,36 @@ static void tester_recherches(void) } } +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); + } + } +} + int main(void) { tester_tri(); @@ -185,6 +215,7 @@ int main(void) tester_totaux_jours(); tester_periodes(); tester_recherches(); + tester_paires(); puts("Tests banque : OK"); return 0; }