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 :
- Cas de base : le plus petit problème résolu directement
- 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)
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
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 :Aggregateest 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.
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é
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ëlVoir la solution
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 --bracketVérification
Decompose(minSize: 1)sur 8 éléments → 8 séries de 1 élémentDecompose(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