Optimiser F7 par tri et parcours à deux pointeurs

This commit is contained in:
rabot
2026-10-21 11:15:03 +02:00
parent 1c7970b554
commit 02fcd4b443
3 changed files with 94 additions and 5 deletions
+18
View File
@@ -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.
+45 -5
View File
@@ -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;
}
+31
View File
@@ -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;
}