F7 : compter les paires opposées par dichotomie (O(n²) → O(n log n))

La double boucle testait les n(n-1)/2 = 450 millions de paires. Sur le
tableau trié par F1, deux recherches dichotomiques donnent en O(log n) le
nombre de transactions de montant -v. On somme ces effectifs, on retire
les montants nuls (comptés avec eux-mêmes) et on divise par 2 (chaque
paire est vue deux fois). F7 est donc appelée après le tri dans main.

Temps (-O2) : 0,92 s → 0,25 s. Empreinte inchangée (ff68d260a7a58441).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 10:55:16 +02:00
parent d913c2266f
commit c84ff59ec3
+28 -7
View File
@@ -163,15 +163,37 @@ const Transaction *rechercher_par_id(const Transaction *t, const int *position,
return &t[position[id]];
}
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0) */
/* Premier indice i tel que t[i].montant >= v, dans t trié par montant
* croissant (recherche dichotomique, O(log n)). */
int premier_indice(const Transaction *t, int n, long v)
{
int debut = 0, fin = n;
while (debut < fin) {
int milieu = (debut + fin) / 2;
if (t[milieu].montant < v)
debut = milieu + 1;
else
fin = milieu;
}
return debut;
}
/* Nombre de transactions de montant v, dans t trié par montant croissant */
int effectif(const Transaction *t, int n, long v)
{
return premier_indice(t, n, v + 1) - premier_indice(t, n, v);
}
/* F7 : nombre de paires de transactions de montants opposés (a + b = 0).
* t doit être trié par montant (F1). On compte, pour chaque transaction, les
* transactions de montant opposé : chaque paire est vue deux fois, et chaque
* montant nul se compte lui-même une fois en trop. */
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;
nb += effectif(t, n, -t[i].montant);
return (nb - effectif(t, n, 0)) / 2;
}
/* F8 : nombre de façons de rendre une somme avec les pièces disponibles */
@@ -279,13 +301,12 @@ int main(void)
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_paires = nb_paires_opposees(t, NB_TRANSACTIONS); /* a besoin du tri */
long nb_facons = nb_facons_rendre(MONTANT_MONNAIE, NB_PIECES);