MGS-31 : Synthèse croisée MGS contre mealpy — neuf paires, un verdict
Notebook de synthèse de l’EPIC #12373 (tracker #12607, item 5). Il ne mesure rien lui-même : il récolte les sorties committées des neuf notebooks de paires (MGS-22 à MGS-30, toutes re-exécutées post-fix sur le noyau assaini — bumps ad13416104/158fca7af6, correctifs #12082/#12084/#12538, série probe #13407) et répond à la question directrice de l’Epic :
L’écart de convergence MGS vs mealpy est-il systématique (noyau) ou spécifique (portage) ?
Le protocole apparié est celui hérité de MGS-22 : même fonction de coût (Sudoku 36 gènes R1, 45 indices fixes), même budget d’évaluations, graines {0, 1, 7, 42}, sanity check des vecteurs témoins des deux côtés du pont PythonNet. Chaque notebook de paire committé sa table de mesures ; ce notebook relit ces fichiers à l’exécution et re-dérive chaque chiffre.
Quatre règles, héritées des incidents fondateurs de l’Epic :
Prose dérivée uniquement des outputs committés (leçon MGS-5 #12187 : une prose citait des chiffres absents des sorties). Aucun nombre de ce notebook n’est saisi à la main.
Classements calculés dynamiquement (leçon MGS-2 #11943 : le « Constat » racontait l’attendu que son propre output contredisait). Les verdicts « qui gagne » sortent d’un tri, pas d’un récit.
Cellules d’exercices exclues de la récolte (les exercices pré-résolus sont en cours de dé-leak, #13515) : seules les cellules de banc protocole alimentent le tableau.
Bras canoniques explicites : quatre paires sont des décompositions à trois bras (P4 spirale/naïf, P7 kennedy/jumeau-formule-MGS, P9 complet/ablaté). Le tableau croisé retient un bras MGS et un bras mealpy par paire — MGS canonique (le composé complet : spirale, EO, complet) contre mealpy standard (le jumeau à formule de la littérature : BaseGA, OriginalEO, kennedy) — et la section « forme de l’écart » replace les bras témoins à côté.
// === MGS-31 : récolte des outputs committés des 9 paires ===// Parse les .ipynb voisins (System.Text.Json), extrait les streams des cellules de banc,// re-dérive médianes / min-max / ms-éval. Accent-insensible (médiane/mediane), virgule// décimale française parsée en invariant, bras multiples (décompositions 3 bras).using System;using System.Collections.Generic;using System.Globalization;using System.IO;using System.Linq;using System.Text;using System.Text.Json;using System.Text.RegularExpressions;string[] Fichiers ={"MGS-22-MGS-vs-Mealpy.ipynb","MGS-23-DifferentialEvolution-vs-Mealpy.ipynb","MGS-24-SimulatedAnnealing-vs-Mealpy.ipynb","MGS-25-WhaleOptimisation-vs-Mealpy.ipynb","MGS-26-EquilibriumOptimizer-vs-Mealpy.ipynb","MGS-27-ForensicBasedInvestigation-vs-Mealpy.ipynb","MGS-28-BareBonesPSO-vs-Mealpy.ipynb","MGS-29-GA-vs-Mealpy.ipynb","MGS-30-ScatterSearch-Decomposition.ipynb",};string[] Algorithmes ={"PSO canonique (paire fondatrice)","DifferentialEvolution","SimulatedAnnealing","WhaleOptimisation","EquilibriumOptimizer","ForensicBasedInvestigation","BareBonesPSO","GA Default/BaseGA","ScatterSearch",};// Normalisation : minuscules + suppression des diacritiques ("médiane" == "mediane").stringNorm(string s){var sb =newStringBuilder();foreach(var ch in s.Normalize(NormalizationForm.FormD))if(CharUnicodeInfo.GetUnicodeCategory(ch)!= UnicodeCategory.NonSpacingMark) sb.Append(ch);return sb.ToString().ToLowerInvariant();}doubleNum(string s)=>double.Parse(s.Replace(',','.'), CultureInfo.InvariantCulture);record LigneGraine(string Bras,int Graine,int Conflits,double MsEval,int[] Cps);record ResumeImprime(string Bras,double Mediane,int Min,int Max,double MsEvalMoyen);record Paire(int Num,string Fichier,string Algo, List<LigneGraine> Lignes, List<ResumeImprime> Resumes,double? RatioImprime,bool Determinisme){publicstring BrasMgs => Lignes.Select(l => l.Bras).First(b => b.StartsWith("mgs")&&!b.Contains("ablat")&&!b.Contains("naive"));publicstring BrasMealpy => Lignes.Select(l => l.Bras).First(b => b.StartsWith("mealpy")&& b.IndexOf("mgs",6)<0);}var reLigne =newRegex(@"^(?<eng>(?:mgs|mealpy)[a-z0-9\- ]*?)\s+(?<g>\d+)\s+(?<tail>[0-9].*)$", RegexOptions.Compiled);// NB : groupes NOMMES obligatoires — en .NET les groupes non nommes sont numerotes AVANT les nommes,// Groups[4] etait donc le ms/eval ("0,104") et non le max (exception Int32.Parse).var reResume =newRegex(@"^(?<eng>[a-z][a-z0-9\- ]*?)\s*:\s*mediane conflits (?<med>[\d.,]+) \(min (?<min>\d+), max (?<max>\d+)\), ms/eval moyen (?<mse>[\d.,]+)", RegexOptions.Compiled);var reRatio =newRegex(@"rapport ms/eval mealpy/[a-z0-9\- ]+:\s*([\d.,]+)x", RegexOptions.Compiled);var reDet =newRegex(@"determinisme", RegexOptions.Compiled);var paires =new List<Paire>();for(int p =0; p < Fichiers.Length; p++){var doc = JsonDocument.Parse(File.ReadAllText(Fichiers[p]));var lignes =new List<LigneGraine>();var resumes =new List<ResumeImprime>();double? ratio =null;bool det =false;foreach(var cell in doc.RootElement.GetProperty("cells").EnumerateArray()){if(cell.GetProperty("cell_type").GetString()!="code")continue;if(!cell.TryGetProperty("outputs",outvar outs))continue;foreach(var o in outs.EnumerateArray()){if(o.GetProperty("output_type").GetString()!="stream")continue;// "text" peut etre un tableau de chaines OU une chaine unique (nbformat tolere les deux).var tel = o.GetProperty("text");var texte = tel.ValueKind== JsonValueKind.Array?string.Join("", tel.EnumerateArray().Select(t => t.GetString())): tel.GetString();// Cellules d'exercice : exclues (de-leak #13515 en cours sur les paires).if(texte.Contains("Exercice"))continue;foreach(var brute in texte.Split('\n')){var l =Norm(brute.Trim());var m = reLigne.Match(l);if(m.Success){var jetons = m.Groups["tail"].Value.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);if(jetons.Length>=2&&int.TryParse(jetons[0],outint conflits)){// Colonne optionnelle checkpoints en fin de ligne ("33/31/31/31") :// ms/eval est alors l'avant-dernier jeton, les checkpoints le dernier.var dernier = jetons[^1];int[] cps =null;int idxMsEval = jetons.Length-1;var mcp = Regex.Match(dernier, @"^(\d+)/(\d+)/(\d+)/(\d+)$");if(mcp.Success){ cps =new[]{int.Parse(mcp.Groups[1].Value),int.Parse(mcp.Groups[2].Value),int.Parse(mcp.Groups[3].Value),int.Parse(mcp.Groups[4].Value)}; idxMsEval = jetons.Length-2;} lignes.Add(newLigneGraine(m.Groups["eng"].Value.TrimEnd(),int.Parse(m.Groups["g"].Value), conflits,Num(jetons[idxMsEval]), cps));}continue;}var r = reResume.Match(l);if(r.Success) resumes.Add(newResumeImprime(r.Groups["eng"].Value.TrimEnd(),Num(r.Groups["med"].Value),int.Parse(r.Groups["min"].Value),int.Parse(r.Groups["max"].Value),Num(r.Groups["mse"].Value)));var q = reRatio.Match(l);if(q.Success) ratio =Num(q.Groups[1].Value);if(reDet.IsMatch(l)) det =true;}}} paires.Add(newPaire(p +1, Fichiers[p], Algorithmes[p], lignes, resumes, ratio, det));}Console.WriteLine($"paire bras (lignes de graine) resumes ratio impr. determ.");foreach(var p in paires){var bras =string.Join(", ", p.Lignes.GroupBy(l => l.Bras).Select(g => $"{g.Key} x{g.Count()}")); Console.WriteLine($"P{p.Num} {bras,-52} {p.Resumes.Count} {(p.RatioImprime?.ToString("0.00") ?? "-"),-6} {(p.Determinisme ? "oui" : "non")}");}
The below script needs to be able to find the current output cell; this is an easy method to get it.
Lecture. Les neuf paires exposent le format standard du protocole ; quatre d’entre elles (P4, P7, P9 et la paire fondatrice P1 dans sa version à bras multiples) déclarent des bras de décomposition en plus des bras canoniques. Les comptes ci-dessus sont la matière première ; la cellule suivante confronte chaque chiffre re-dérivé au résumé imprimé par la paire elle-même — un désaccord signifierait que ce notebook parse mal, et serait signalé avant toute interprétation.
// === Contrôle d'intégrité : re-derivation vs resumes imprimés ===// Médiane = moyenne des deux valeurs centrales (4 graines). Tolérances relatives aux// arrondis d'impression des paires (2-3 decimales imprimees, moyenne de valeurs arrondies) :// médiane ±0,05 exacte ; ms/éval ±0,002 ou 2% ; ratio ±0,06 ou 3%.doubleMediane(IEnumerable<int> v){var s = v.OrderBy(x => x).ToArray();int n = s.Length;return n %2==1? s[n /2]:(s[n /2-1]+ s[n /2])/2.0;}doubleMoyenne(IEnumerable<double> v)=> v.Any()? v.Average():double.NaN;int passes =0, echecs =0;foreach(var p in paires){var problems =new List<string>();foreach(var bras in p.Lignes.Select(l => l.Bras).Distinct()){var lg = p.Lignes.Where(l => l.Bras== bras).ToList();var imp = p.Resumes.FirstOrDefault(r => r.Bras== bras);if(imp isnull){ problems.Add($"resume absent ({bras})");continue;}double med =Mediane(lg.Select(l => l.Conflits));if(Math.Abs(med - imp.Mediane)>0.05) problems.Add($"mediane {bras} {med:0.0}!={imp.Mediane:0.0}");if(lg.Min(l => l.Conflits)!= imp.Min) problems.Add($"min {bras}");if(lg.Max(l => l.Conflits)!= imp.Max) problems.Add($"max {bras}");double mse =Moyenne(lg.Select(l => l.MsEval));if(!(Math.Abs(mse - imp.MsEvalMoyen)<=0.002|| Math.Abs(mse - imp.MsEvalMoyen)<=0.02* imp.MsEvalMoyen)) problems.Add($"ms/eval {bras} {mse:0.000}!={imp.MsEvalMoyen:0.000}");}if(p.RatioImprime.HasValue){var lm = p.Lignes.Where(l => l.Bras== p.BrasMgs).Select(l => l.MsEval).ToList();var lp = p.Lignes.Where(l => l.Bras== p.BrasMealpy).Select(l => l.MsEval).ToList();double ratioCalc =Moyenne(lp)/Moyenne(lm);if(!(Math.Abs(ratioCalc - p.RatioImprime.Value)<=0.06|| Math.Abs(ratioCalc - p.RatioImprime.Value)<=0.03* p.RatioImprime.Value)) problems.Add($"ratio {ratioCalc:0.00}!={p.RatioImprime:0.00}");}if(problems.Count==0){ passes++; Console.WriteLine($"P{p.Num} ({p.Algo}) : PASS — re-derivation conforme aux resumes imprimés");}else{ echecs++; Console.WriteLine($"P{p.Num} ({p.Algo}) : FAIL — {string.Join(";", problems)}");}}Console.WriteLine();Console.WriteLine($"INTEGRITE : {passes}/{paires.Count} paires conformes, {echecs} écart(s).");
Chaque ligne est une paire ; les colonnes qualité (conflits médians, étendue) et coût (ms par évaluation, rapport mealpy/MGS) sont calculées depuis les graines des bras canoniques récoltées. Le verdict qualité trie la médiane (moins de conflits = meilleur) ; le verdict coût lit le rapport (rapport > 1 = mealpy plus coûteux par évaluation).
Lecture du tableau. La qualité ne raconte pas une histoire unique : mealpy domine 6 paires sur 9, MGS 3, et l’amplitude va de −6,5 (P6, FBI) à +17 (P5, EO). Les trois paires où MGS gagne — recuit simulé (−4), WOA (−1,5), FBI (−6,5) — sont précisément celles dont le composé MGS embarque une réinsertion ou une stratégie de mouvement absente du jumeau mealpy ; les six autres donnent l’avantage au portage mealpy, jusqu’au triple écart de l’EquilibriumOptimizer original (P5 : médianes 42 contre 25).
Deux lignes sortent du lot. P1 cumule l’écart qualité le plus large après P5 (+15 conflits) et le seul rapport de coût inférieur à 1 (0,82x) : le banc fondateur mesure un PSO MGS instrumenté dont la sonde par évaluation pèse plus lourd que le moteur mealpy — la paire elle-même mesure la fitness seule à 5,13x Python/C#,confirmation que l’instrumentation, pas l’algorithme, inverse ce rapport. P8 est la paire la plus serrée en qualité (13,5 contre 12,5 — un seul conflit de médiane) et la plus coûteuse en relatif (7,86x) : deux moteurs GA convergent pareil, l’un paie chaque évaluation sept fois moins cher. Les étendues médianes (6 conflits MGS, 7 mealpy) sont du même ordre : aucun des deux moteurs n’est plus «loterie» que l’autre à quatre graines.
La forme de l’écart
Le tracker #12607 demande de classer la forme de chaque écart (précipitation précoce / stagnation tardive / coût par step) depuis des trajectoires best-so-far aux checkpoints 25/50/75/100 %. Deux paires seulement journalisent ces checkpoints dans leur banc — P7 (tous les bras, toutes les graines) et P9 (tous les bras, toutes les graines) ; les autres ne commettent que le point final. La colonne « forme » est donc partielle par construction sur 7 paires — ce qui suit l’affirme plutôt que de le maquiller. P9 va plus loin : son propre output classe déjà la forme de son écart axe/noyau par graine ; cette classification est reprise telle quelle.
// === Trajectoires checkpoints journalisées + agrégats ===Console.WriteLine("Checkpoints best-so-far par graine (quand journalisés au banc) :");foreach(var p in paires.Where(p => p.Lignes.Any(l => l.Cps!=null))){foreach(var g in p.Lignes.GroupBy(l => l.Bras).OrderBy(g => g.Key)){if(g.First().Cps==null)continue;var m1 =string.Join(" ", g.Select(l => $"g{l.Graine}: {l.Cps[0]}/{l.Cps[1]}/{l.Cps[2]}/{l.Cps[3]}")); Console.WriteLine($" P{p.Num} {g.Key,-15} : {m1}");}}Console.WriteLine();Console.WriteLine("Agrégats sur les 9 paires (bras canoniques) :");double[] ratios = stats.Select(s => s.MseP/ s.MseM).ToArray();int mgsGagne = stats.Count(s => s.MedM< s.MedP), mealpyGagne = stats.Count(s => s.MedM> s.MedP), egal = stats.Count(s => s.MedM== s.MedP);Console.WriteLine($" Qualité (médiane conflits) : MGS devant {mgsGagne}/9, mealpy devant {mealpyGagne}/9, égalité {egal}/9.");Console.WriteLine($" Coût ms/éval (mealpy/MGS) : min {Fr(ratios.Min())}x, max {Fr(ratios.Max())}x, médiane {Fr(ratios.OrderBy(r => r).ToArray()[ratios.Length / 2])}x.");Console.WriteLine($" Paires à rapport >= 2x : {ratios.Count(r => r >= 2)}/9 ; à rapport < 1x : {ratios.Count(r => r < 1)}/9.");Console.WriteLine($" Dispersion qualité (étendue méd. des étendues) : MGS {Fr(Mediane2(paires.Select(p => { var l = p.Lignes.Where(x => x.Bras == p.BrasMgs).Select(x => x.Conflits).ToList(); return l.Max() - l.Min(); }))):0} conflits, mealpy {Fr(Mediane2(paires.Select(p => { var l = p.Lignes.Where(x => x.Bras == p.BrasMealpy).Select(x => x.Conflits).ToList(); return l.Max() - l.Min(); }))):0} conflits.");
Lecture. Sur les deux paires qui journalisent les trajectoires, la forme est lisible et ce n’est pas la même des deux côtés. En P7, le MGS atteint son plateau dès le checkpoint 25 % (g0 : 33/31/31/31 — plus un conflit gagné sur les trois quarts restants du budget) pendant que mealpy continue de descendre (g0 : 32/22/20/19) : stagnation précoce côté MGS, précipitation jamais consolidée côté mealpy. En P9, mealpy-jumeau gagne encore à 75 % du budget sur la graine 1 (50/50/48/48) — un mouvement tardif que le MGS-complet, plat sur toutes ses trajectoires, ne produit jamais.
L’isolateur le plus net de P7 : les deux bras mealpy — formule Kennedy-2003 littérale et formule MGS portée — ont des trajectoires identiques graine par graine (32/22/20/19, 31/26/25/25, …). La formule ne discrimine pas sur ce landscape ; l’écart P7 (30,5 contre 25,5) est donc un écart de moteur, pas de formule. Et le coût par step reste l’axe systématique : médiane 2,38x, cinq paires au-delà de 2x. Les sept autres paires ne commettent que le point final — leur forme ne se déduit pas, elle se mesurerait.
// === Deux vues : médianes par paire, rapport de coût par paire ===// SVG inline zéro-dépendance (rend GitHub/nbviewer/offline) — canon SvgChartHelper.cs (#6927, #6942).// Décimales en point invariant (virgule = séparateur de coordonnées SVG -> casse Chromium en fr-FR).#load "../../Probas/Infer/SvgChartHelper.cs"var cats = stats.Select(s => $"P{s.Num}").ToArray();var medsMgs = stats.Select(s => s.MedM).ToArray();var medsMealpy = stats.Select(s => s.MedP).ToArray();var ratiosChart = stats.Select(s => s.MseP/ s.MseM).ToArray();display(SvgChartHelper.GroupedBar("Médiane de conflits par paire (4 graines, budget égal, bras canoniques)", cats,new[]{ medsMgs, medsMealpy },new[]{"MGS","mealpy"}, width:760, height:380));display(SvgChartHelper.GroupedBar("Rapport de coût ms/éval mealpy/MGS (log) — >1 = mealpy plus coûteux", cats,new[]{ ratiosChart },new[]{"ratio"}, width:760, height:340, logY:true));
Diagnostic : noyau ou portage ?
Le tableau croisé sépare ce que la question directrice confondait : l’écart de coût est systématique, l’écart de qualité est spécifique.
Coût par évaluation — systématique (noyau). Huit paires sur neuf paient mealpy plus cher par évaluation (médiane 2,38x, jusqu’à 7,86x en P8). C’est la frontière d’exécution Python-contre-C#, mesurée isolément dans chaque paire par le test « fitness seule » (rapport 5,13x à 5,33x selon les paires) : l’ordre de grandeur du rapport de banc s’explique par la runtime, pas par les algorithmes. L’unique inversion (P1, 0,82x) est elle-même un artefact d’instrumentation du banc fondateur, documenté dans la paire.
Qualité — spécifique (portage/algorithme). Aucun moteur ne domine : mealpy devant 6/9, MGS 3/9, avec des amplitudes qui vont dans les deux sens (−6,5 à +17). Les bras de décomposition isolent les causes là où ils existent : en P7, deux formules distinctes sur le même moteur mealpy convergent à l’identique (25,5 partout) alors que le moteur MGS reste à 30,5 — l’écart BBPSO est moteur ; en P9, le bras ablaté (52,0) bat le bras complet (54,0) sur le moteur MGS — la diversité RefSet ne paie pas sur ce paysage, l’écart ScatterSearch est interne à la stratégie, pas au noyau.
Enfin, le contexte d’assainissement ferme la porte aux explications faciles : toutes les mesures récoltées ici sont post-correction (seeding décoratif #12071/#12082/#12084, garde bloquante DE #12464, bornes fantômes #12466/#12538, constantes PSO canoniques byte-exactes #12032) et post-bump du submodule — les écarts mesurés sont ceux du noyau assaini, pas les résidus des défauts corrigés.
Réponse à la question directrice, limites, suite
L’écart de convergence MGS vs mealpy est systématique pour le coût, spécifique pour la qualité. Chaque évaluation mealpy coûte en médiane 2,4x plus cher (frontière de runtime, isolée par les tests fitness-seule des paires) ; la qualité de convergence, elle, se joue algorithme par algorithme — mealpy gagne 6 des 9 paires (jusqu’à −17 conflits de médiane sur l’EO original), MGS les 3 autres (SA, WOA, FBI, jusqu’à −6,5). Les bras de décomposition tranchent deux cas : P7 montre un écart de moteur à formule constante, P9 un écart de stratégie à moteur constant. Le noyau assaini n’a pas de défaut systématique résiduel à corriger : il a un profil — rapide par évaluation, inégal en convergence selon l’algorithme.
Limites, mesurées. Les trajectoires aux checkpoints ne sont journalisées que pour 2 paires sur 9 (la colonne « forme » est partielle par construction) ; un seul paysage (Sudoku 36 gènes R1, 45 indices fixes) ; quatre graines ; un seul budget. Les exercices ×4 budget des paires suggèrent que certains écarts se referment à budget étendu — hors périmètre du tableau croisé, qui mesure à budget égal ; la boucle corrective #13778 l’a depuis mesuré pour la paire SA : l’écart ne se referme pas, il s’inverse (voir l’épilogue).
Suite. Tracker #12607 item 5 livré ; il reste l’item 6 (fermeture de l’Epic #12373) — arbitrage coordinateur.
Épilogue — la boucle corrective #13778 mesure l’exercice ×4 de la paire SA
Cette synthèse refermait la question directrice de l’Epic #12373 ; l’EPIC #13778 (« MGS vs mealpy : convertir les benchmarks en correctifs MGS puis re-mesurer ») a depuis fait passer la paire SA — un des trois gains MGS (−4 à budget égal) — par la boucle corrective complète : baseline figée, diagnostic causal instrumenté, correctif atomique opt-in, re-mesure appariée. Ce que la boucle a mesuré (trace complète dans le commentaire livré sur l’issue ; correctif : MetaGeneticSharp#59) :
Instrument. Dispersion inter-individus et meilleur courant aux checkpoints (4 graines, G160/G640). Après la mort de la température (T_k = 0,95^k devient gloutonne vers G150), l’acceptation en marche latérale sur les plateaux entretient une dispersion qui atteint transitoirement 21,7 pour des gênes bornés [1, 10).
Hypothèse. Le pas σ = StepFraction·|aléatoire − courant| étant relatif à la population, cette dispersion se rétro-alimente ; coupler le pas au même schedule géométrique que la température (homothétie x’ = courant + α^(kγ)·(proposé − courant)) devrait raffiner la proposition quand l’acceptation amont se fige. Correctif StepCoolingExponent opt-in strict (null par défaut, voie historique inchangée), qui ne tire aucun aléa : chaque graine rejoue la même suite RNG que la baseline.
Verdict : NO IMPROVEMENT, monotone en γ. γ = 0,25 / 0,5 / 1 → médiane G160 figée à 32,0 / 36,5 / 38,5 (baseline : 27,0), progression G160→G640 nulle sur les 4 graines, dispersion finale 0,0000 : le couplage collapse la population et reproduit le gel du « walker » mealpy. La prémisse réfutée est la partie informative — la dérive latérale entretenue n’est pas un défaut : c’est la recherche parallèle.
L’exercice 2 de MGS-24, mesuré. MGS-SA passe de 27,0 (G160) à 13,0 (G640 ; médiane des graines 0/1/7/42 : 14, 11, 15, 12) quand mealpy passe de 31,0 (8 000 epochs) à 27,0 (32 000) : l’écart de −4 devient −14 à budget ×4. Il ne se referme pas — il s’inverse en faveur de MGS. La réponse causale à la question de l’exercice (« se referme-t-il ? ») est donc mesurée ; l’exercice reste ouvert aux étudiants.
Ce résultat renforce le diagnostic ci-dessus : l’écart SA est spécifique à l’algorithme, et l’hypothèse d’un défaut noyau remédiable par couplage pas-température est falsifiée — sur ce paysage, la dispersion entretenue est le mécanisme qui paie à budget étendu.
Exercice 1 — la médiane à la main
Le tableau ci-dessus donne la médiane des conflits de la paire 8 (GA) pour les deux moteurs. Depuis les quatre graines MGS récoltées dans ce notebook (variable paires), re-dérivez la médiane MGS de P8 sans utiliser Mediane2 : triez les quatre valeurs, moyennez les deux centrales, et comparez à la colonne du tableau.
Indice : une médiane de nombre pair d’observations n’est jamais une observation — c’est leur point milieu.
// EXERCICE 1 : médiane manuelle de la paire 8, côté MGS.// Etape 1 : isoler les conflits des 4 graines du bras MGS de la paire 8 dans paires.// Etape 2 : les trier, moyenner les deux centrales.// Etape 3 : comparer au chiffre du tableau croisé et afficher l'écart.Console.WriteLine("Exercice a completer");
Exercice a completer
Exercice 2 — l’étendue comme signal de variance
Deux moteurs peuvent partager une médiane proche et raconter des histoires très différentes si l’étendue (min–max) sur 4 graines est large. Repérez dans le tableau la paire dont l’étendue mealpy est la plus large, et celle dont l’étendue MGS est la plus large.
Indice : une étendue large avec une médiane basse signale un moteur qui gagne parfois grand et perd parfois grand — exactement le profil qu’un budget ×4 (exercices des paires) vient tester.
// EXERCICE 2 : étendue maximale de chaque côté.// Etape 1 : parcourir paires, calculer max-min des conflits des bras canoniques.// Etape 2 : identifier la paire à l'étendue mealpy max, puis MGS max.// Etape 3 : afficher les deux paires et leurs étendues.Console.WriteLine("Exercice a completer");
Exercice a completer
Exercice 3 — classer une forme d’écart
Supposez une dixième paire dont le témoin journalise les checkpoints 25/50/75/100 % = 40/31/31/31 côté MGS et 45/40/40/38 côté mealpy, avec le même budget. Classez son écart : précipitation précoce, stagnation tardive, ou coût par step ? Quel verdict qualité au 100 % ?
Indice : comparez la trajectoire — qui descend tôt, qui stagne — avant de regarder le point final.
// EXERCICE 3 : classification d'une trajectoire fictive.// Etape 1 : écrire la règle : précoce = écart creusé avant 50% ; tardive = écart figé après 50%.// Etape 2 : l'appliquer aux checkpoints donnés dans l'énoncé.// Etape 3 : conclure en une phrase + verdict qualité 100%.Console.WriteLine("Exercice a completer");