Skip to content

Exercice 08 — Bracket de tournoi ​

Partie 8 — .Decompose() récursif + cas de base + règle de combinaison

Concepts théoriques ​

Contexte ​

Team Helvetia se qualifie pour les playoffs. Le bracket de tournoi fonctionne par élimination directe — on divise les équipes en deux moitiés, chaque moitié joue ses matchs, et on combine les résultats. C'est exactement la structure d'une récursion.

Decompose divise récursivement une StatSeries en sous-séries — utile pour l'analyse multi-échelle : voir les tendances à court terme (5 matchs) et long terme (toute la saison).


Concept FP : Récursion = décomposition fonctionnelle ​

Tout algorithme récursif suit le même schéma :

  1. Cas de base : le plus petit problème résolu directement
  2. Règle de combinaison : résoudre en combinant des solutions plus petites

Que se passerait-il sans le cas de base ? → Récursion et programmation fonctionnelle


Étape 1 — Implémenter .Decompose(minSize) ​

csharp
public IEnumerable<StatSeries> Decompose(int minSize)
{
    var points = _data.ToList();

    if (/* cas de base */)
        return // ...

    int mid   = // ...
    var left  = // ...
    var right = // ...

    return // ...
}
Voir la solution
csharp
public IEnumerable<StatSeries> Decompose(int minSize)
{
    var points = _data.ToList();

    if (points.Count <= minSize)
        return new[] { this };

    int mid   = points.Count / 2;
    var left  = new StatSeries(points.Take(mid));
    var right = new StatSeries(points.Skip(mid));

    return left.Decompose(minSize).Concat(right.Decompose(minSize));
}

Remarque : new StatSeries(...) appelle le constructeur privé depuis une méthode d'instance — c'est autorisé car on est dans la même classe.

La récursion et Aggregate (Fold, exercice 05) sont les deux faces de la même pièce : Aggregate est la récursion, généralisée et rendue itérative pour éviter les stack overflows. → Récursion et Fold


Étape 2 — Tracer l'arbre de décomposition ​

Prédire le résultat pour 8 éléments avec minSize = 2 :

Voir l'arbre
[K1 K2 K3 K4 K5 K6 K7 K8]   (8 valeurs KDA)
         ↓ Decompose(2)
   [K1 K2 K3 K4]      [K5 K6 K7 K8]
       ↓                    ↓
  [K1 K2] [K3 K4]     [K5 K6] [K7 K8]

4 sous-séries de 2 éléments.

csharp
var series8   = StatSeries.From(kdaLea.DataPoints.Take(8));
var subSeries = series8.Decompose(minSize: 2);

Console.WriteLine(subSeries.Count()); // 4
foreach (var s in subSeries)
    Console.WriteLine($"  [{string.Join(", ", s.Values.Select(v => v.ToString("F2")))}]");

DataPoints.Take(8) préserve les timestamps des 8 premiers matchs — les sous-séries restent datées.


Étape 3 — Bracket de tournoi simplifié ​

csharp
var series = StatSeries.From(kdaLea.Smooth(1).DataPoints.Take(8));

// Ronde 1 : 4 segments de 2 matchs — KDA moyen par paire
var round1 = series.Decompose(2).Select(s => s.Statistics().Mean).ToList();
Console.WriteLine("Ronde 1 (KDA moyen par paire) :");
round1.ForEach(m => Console.WriteLine($"  {m:F2}"));

// Ronde 2 : les moyennes de ronde 1 sont des valeurs synthétiques
var round2 = StatSeries.From(
    round1.Select((v, i) => (new DateTime(2024, 1, i + 1), v))
).Decompose(1).Select(s => s.Statistics().Mean);
Console.WriteLine("Ronde 2 (KDA moyen par quart) :");
foreach (var m in round2) Console.WriteLine($"  {m:F2}");

Étape 4 — Interface CLI ​

Ajouter --bracket <n> — le dernier flag de l'application.

Avant de coder : Comment enchaîner Decompose avec un Select pour calculer la moyenne de chaque sous-série ? Quelle taille de segment proposer si n représente le nombre total de matchs du bracket ?

dotnet run -- --bracket 8
dotnet run -- --bracket 8 --player Raphaël
Voir la solution
csharp
if (args.Contains("--bracket"))
{
    int n = int.Parse(args[Array.IndexOf(args, "--bracket") + 1]);
    var round = kdaLea
        .Decompose(n / 4)
        .Select(s => s.Statistics().Mean)
        .ToList();

    Console.WriteLine($"Bracket ({n} matchs, {round.Count} segments) :");
    round.ForEach(m => Console.WriteLine($"  KDA moy : {m:F2}"));
}

Récapitulatif de tous les flags reconnus :

--help  --game  --player  --filter  --stat  --window
--rank  --export  --audit  --generate  --bracket

Vérification ​

  • Decompose(minSize: 1) sur 8 éléments → 8 séries de 1 élément
  • Decompose(minSize: 8) sur 8 éléments → 1 série (cas de base immédiat)
  • Decompose(minSize: 2) sur 8 éléments → 4 séries de 2 éléments
  • L'arbre de décomposition est tracé dans les commentaires du code