@anonympins/fingerprint 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +136 -22
  2. package/fingerprint.js +502 -230
  3. package/library.js +0 -824
  4. package/package.json +7 -3
package/library.js CHANGED
@@ -13,749 +13,6 @@
13
13
 
14
14
  import { Worker } from "worker_threads";
15
15
  import os from "os";
16
- /**
17
- * @namespace Dichotomy
18
- * @description Un ensemble d'outils de recherche puissants pour les tableaux triés.
19
- */
20
- const Dichotomy = {
21
- /**
22
- * Comparateur par défaut pour les types primitifs (nombres, chaînes).
23
- * @private
24
- */
25
- _defaultComparator(a, b) {
26
- if (a < b) return -1;
27
- if (a > b) return 1;
28
- return 0;
29
- },
30
-
31
- /**
32
- * L'algorithme de base de la recherche dichotomique.
33
- * Trouve l'index d'une valeur cible dans un tableau trié.
34
- * Si plusieurs occurrences existent, l'index retourné n'est pas garanti (peut être n'importe laquelle des occurrences).
35
- * @template T
36
- * @param {Array<T>} arr - Le tableau trié dans lequel chercher.
37
- * @param {any} target - La valeur à trouver.
38
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
39
- * @returns {number} L'index de la cible si elle est trouvée, sinon -1.
40
- */
41
- search(arr, target, comparator = this._defaultComparator) {
42
- let low = 0;
43
- let high = arr.length - 1;
44
-
45
- while (low <= high) {
46
- // Utiliser Math.floor pour éviter les problèmes avec les nombres flottants
47
- // et pour s'assurer que mid est un entier.
48
- const mid = Math.floor(low + (high - low) / 2);
49
- const comparison = comparator(arr[mid], target);
50
-
51
- if (comparison === 0) {
52
- return mid; // Cible trouvée
53
- } else if (comparison < 0) {
54
- low = mid + 1; // La cible est dans la moitié droite
55
- } else {
56
- high = mid - 1; // La cible est dans la moitié gauche
57
- }
58
- }
59
-
60
- return -1; // Cible non trouvée
61
- },
62
-
63
- /**
64
- * Trouve l'index de la PREMIÈRE occurrence d'une valeur cible.
65
- * Utile quand le tableau contient des doublons.
66
- * @template T
67
- * @param {Array<T>} arr - Le tableau trié.
68
- * @param {any} target - La valeur à trouver.
69
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
70
- * @returns {number} L'index de la première occurrence, sinon -1.
71
- */
72
- findFirst(arr, target, comparator = this._defaultComparator) {
73
- let low = 0;
74
- let high = arr.length - 1;
75
- let result = -1;
76
-
77
- while (low <= high) {
78
- const mid = Math.floor(low + (high - low) / 2);
79
- const comparison = comparator(arr[mid], target);
80
-
81
- if (comparison === 0) {
82
- result = mid;
83
- high = mid - 1; // Continuer à chercher à gauche
84
- } else if (comparison < 0) {
85
- low = mid + 1;
86
- } else {
87
- high = mid - 1; // On continue de chercher à gauche
88
- }
89
- }
90
-
91
- return result;
92
- },
93
-
94
- /**
95
- * Trouve l'index de la DERNIÈRE occurrence d'une valeur cible.
96
- * Utile quand le tableau contient des doublons.
97
- * @template T
98
- * @param {Array<T>} arr - Le tableau trié.
99
- * @param {any} target - La valeur à trouver.
100
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
101
- * @returns {number} L'index de la dernière occurrence, sinon -1.
102
- */
103
- findLast(arr, target, comparator = this._defaultComparator) {
104
- let low = 0;
105
- let high = arr.length - 1;
106
- let result = -1;
107
-
108
- while (low <= high) {
109
- const mid = Math.floor(low + (high - low) / 2);
110
- const comparison = comparator(arr[mid], target);
111
-
112
- if (comparison === 0) {
113
- result = mid;
114
- low = mid + 1; // Continuer à chercher à droite
115
- } else if (comparison < 0) {
116
- low = mid + 1; // On continue de chercher à droite
117
- } else {
118
- high = mid - 1;
119
- }
120
- }
121
-
122
- return result;
123
- },
124
-
125
- /**
126
- * Compte le nombre total d'occurrences d'une valeur cible.
127
- * Combine findFirst et findLast pour une efficacité maximale.
128
- * @template T
129
- * @param {Array<T>} arr - Le tableau trié.
130
- * @param {any} target - La valeur à compter.
131
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
132
- * @returns {number} Le nombre d'occurrences de la cible.
133
- */
134
- countOccurrences(arr, target, comparator = this._defaultComparator) {
135
- const firstIndex = this.findFirst(arr, target, comparator);
136
-
137
- if (firstIndex === -1) {
138
- return 0; // Si la première n'existe pas, il n'y en a aucune.
139
- }
140
-
141
- // On peut passer `firstIndex` comme point de départ à `findLast` pour optimiser, mais restons simple.
142
- const lastIndex = this.findLast(arr, target, comparator);
143
- return lastIndex - firstIndex + 1;
144
- },
145
-
146
- /**
147
- * Trouve l'index où la cible devrait être insérée pour maintenir l'ordre du tableau.
148
- * C'est l'équivalent de la fonction `lower_bound` en C++.
149
- * @template T
150
- * @param {Array<T>} arr - Le tableau trié.
151
- * @param {any} target - La valeur à insérer.
152
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
153
- * @returns {number} L'index d'insertion approprié.
154
- */
155
- findInsertionPoint(arr, target, comparator = this._defaultComparator) {
156
- let low = 0;
157
- let high = arr.length; // Note: high est la longueur du tableau, pas length - 1
158
-
159
- while (low < high) {
160
- const mid = Math.floor(low + (high - low) / 2);
161
- if (comparator(arr[mid], target) < 0) {
162
- low = mid + 1;
163
- } else {
164
- high = mid;
165
- }
166
- }
167
-
168
- return low; // ou high, ils sont égaux à la fin de la boucle
169
- },
170
-
171
- /**
172
- * Vérifie si une valeur cible est présente dans le tableau.
173
- * Plus sémantique que `search(arr, target) !== -1`.
174
- * @template T
175
- * @param {Array<T>} arr - Le tableau trié.
176
- * @param {any} target - La valeur à vérifier.
177
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
178
- * @returns {boolean} `true` si la cible est présente, sinon `false`.
179
- */
180
- contains(arr, target, comparator = this._defaultComparator) {
181
- return this.search(arr, target, comparator) !== -1;
182
- },
183
-
184
- // --- NOUVEAUX ALGORITHMES PUISSANTS ---
185
-
186
- /**
187
- * Trouve tous les éléments dans un intervalle [minTarget, maxTarget].
188
- * @template T
189
- * @param {Array<T>} arr - Le tableau trié.
190
- * @param {any} minTarget - La borne inférieure de l'intervalle (inclusive).
191
- * @param {any} maxTarget - La borne supérieure de l'intervalle (inclusive).
192
- * @param {Comparator<T>} [comparator] - Fonction de comparaison.
193
- * @returns {Array<T>} Un sous-tableau contenant les éléments de l'intervalle.
194
- */
195
- findRange(arr, minTarget, maxTarget, comparator = this._defaultComparator) {
196
- // Trouve l'index du premier élément >= minTarget
197
- const startIndex = this.findInsertionPoint(arr, minTarget, comparator);
198
-
199
- // Trouve l'index du premier élément > maxTarget
200
- let endIndex = this.findInsertionPoint(arr, maxTarget, comparator);
201
-
202
- // Il faut aussi trouver le dernier élément ÉGAL à maxTarget
203
- const lastElement = this.findLast(arr, maxTarget, comparator);
204
- if (lastElement !== -1) {
205
- endIndex = lastElement + 1;
206
- }
207
-
208
- return arr.slice(startIndex, endIndex);
209
- },
210
-
211
- /**
212
- * Trouve l'index de l'élément le plus proche de la cible dans le tableau.
213
- * @template T
214
- * @param {Array<T>} arr - Le tableau trié.
215
- * @param {any} target - La valeur numérique cible.
216
- * @param {function(T, any): number} distanceMetric - Une fonction qui retourne la "distance" (un nombre) entre un élément et la cible.
217
- * @returns {number} L'index de l'élément le plus proche, ou -1 si le tableau est vide.
218
- */
219
- findClosest(arr, target, distanceMetric) {
220
- if (arr.length === 0) {
221
- return -1;
222
- }
223
-
224
- let low = 0;
225
- let high = arr.length - 1;
226
-
227
- while (low <= high) {
228
- const mid = Math.floor(low + (high - low) / 2);
229
- const midValue = arr[mid];
230
-
231
- if (distanceMetric(midValue, target) === 0) {
232
- return mid;
233
- } else if (distanceMetric(midValue, target) < 0) {
234
- low = mid + 1;
235
- } else {
236
- high = mid - 1;
237
- }
238
- }
239
-
240
- // À la fin de la boucle, `low` et `high` encadrent la position où `target` serait insérée.
241
- // L'élément le plus proche est soit à `high`, soit à `low`.
242
- if (high < 0) high = 0; // S'assurer que high est un index valide
243
- if (low >= arr.length) low = arr.length - 1; // S'assurer que low est un index valide
244
- const diffHigh = Math.abs(distanceMetric(arr[high], target));
245
- const diffLow = Math.abs(distanceMetric(arr[low], target));
246
-
247
- return diffLow <= diffHigh ? low : high;
248
- },
249
-
250
- /**
251
- * Trouve la "frontière" dans un tableau où une condition (prédicat) passe de false à true.
252
- * C'est une généralisation de findInsertionPoint et findFirst.
253
- * @template T
254
- * @param {Array<T>} arr - Le tableau où chaque élément peut être évalué par le prédicat.
255
- * @param {function(T): boolean} predicate - Une fonction qui retourne true ou false pour un élément.
256
- * Le tableau doit être "trié" selon ce prédicat (tous les false avant tous les true).
257
- * @returns {number} L'index du premier élément pour lequel le prédicat est true. Retourne arr.length si non trouvé.
258
- */
259
- findBoundary(arr, predicate) {
260
- let low = 0;
261
- let high = arr.length;
262
- while (low < high) {
263
- const mid = Math.floor(low + (high - low) / 2);
264
- if (predicate(arr[mid])) {
265
- high = mid; // On a trouvé un 'true', la frontière est peut-être avant
266
- } else {
267
- low = mid + 1; // C'est 'false', la frontière est forcément après
268
- }
269
- }
270
- return low;
271
- },
272
-
273
- /**
274
- * Effectue une recherche dichotomique sur une fonction pour trouver la plus petite valeur d'entrée
275
- * qui satisfait une condition. (Recherche sur la réponse).
276
- * @param {number} low - La borne inférieure de l'espace de recherche.
277
- * @param {number} high - La borne supérieure de l'espace de recherche.
278
- * @param {function(number): boolean} predicate - La fonction de test. Doit être monotone (false...false, true...true).
279
- * @param {number} [precision=1e-9] - La précision requise pour la réponse (pour les nombres flottants).
280
- * @returns {number} La plus petite valeur (approximative) qui rend le prédicat 'true'.
281
- */
282
- searchOnAnswer(low, high, predicate, precision = 1e-9) {
283
- // Si on travaille avec des entiers, la boucle s'arrête quand low == high
284
- const isIntegerSearch =
285
- Number.isInteger(low) && Number.isInteger(high) && precision === 1;
286
-
287
- while (high - low > precision) {
288
- const mid = isIntegerSearch
289
- ? Math.floor(low + (high - low) / 2)
290
- : low + (high - low) / 2;
291
- if (predicate(mid)) {
292
- high = mid; // Cette réponse est possible, essayons plus petit
293
- } else {
294
- low = mid; // Cette réponse n'est pas possible, il faut viser plus haut
295
- }
296
- }
297
- // Pour les entiers, 'high' est la première bonne réponse. Pour les flottants, c'est une approximation.
298
- return high;
299
- },
300
-
301
- /**
302
- * Trouve la valeur d'entrée qui maximise le résultat d'une fonction unimodale (avec un seul pic).
303
- * C'est l'outil adapté pour les problèmes d'optimisation non-monotones.
304
- * @param {number} low - La borne inférieure de l'espace de recherche.
305
- * @param {number} high - La borne supérieure de l'espace de recherche.
306
- * @param {function(number): number} evaluator - Une fonction qui prend une valeur et retourne son "score" ou "altitude".
307
- * @param {number} [precision=1e-9] - La précision requise pour la réponse.
308
- * @returns {number} La valeur (approximative) qui maximise le résultat de l'évaluateur.
309
- */
310
- findPeak(low, high, evaluator, precision = 1e-9) {
311
- while (high - low > precision) {
312
- // On divise l'intervalle restant en trois.
313
- const mid1 = low + (high - low) / 3;
314
- const mid2 = high - (high - low) / 3;
315
-
316
- const eval1 = evaluator(mid1);
317
- const eval2 = evaluator(mid2);
318
-
319
- if (eval1 < eval2) {
320
- // Le pic est dans la partie droite (incluant mid2).
321
- // On peut éliminer le premier tiers.
322
- low = mid1;
323
- } else {
324
- // Le pic est dans la partie gauche (incluant mid1).
325
- // On peut éliminer le troisième tiers.
326
- high = mid2;
327
- }
328
- }
329
-
330
- // low et high sont maintenant très proches. Le pic est entre les deux.
331
- // On retourne le milieu de l'intervalle final pour une meilleure approximation.
332
- return (low + high) / 2;
333
- },
334
- };
335
-
336
- /**
337
- * @namespace Dichotomy.Operators
338
- * @description Une bibliothèque de "fabriques de prédicats" pour des problèmes complexes,
339
- * à utiliser avec `Dichotomy.searchOnAnswer`.
340
- * Ces opérateurs sont conçus pour des problèmes où la réponse peut être trouvée en testant
341
- * une seule variable (monodimensionnel).
342
- * ---
343
- * @namespace Optimization
344
- * @description Une bibliothèque d'algorithmes d'optimisation pour des problèmes complexes (multimodaux, multidimensionnels).
345
- */
346
- Dichotomy.Operators = {
347
- /**
348
- * Crée un prédicat pour trouver le niveau de financement de base le plus élevé possible.
349
- * @param {object} config - L'objet de configuration.
350
- * @param {Array<number>} config.baseNeeds - Tableau des besoins de base de chaque entité.
351
- * @param {number} config.totalBudget - Le budget total disponible.
352
- * @returns {function(number): boolean} Un prédicat qui prend un `guaranteedLevel` et retourne `true` si le budget est suffisant.
353
- */
354
- createFairResourceAllocator({ baseNeeds, totalBudget }) {
355
- return function isFeasible(guaranteedLevel) {
356
- let requiredBudget = 0;
357
- for (const need of baseNeeds) {
358
- // Chaque entité reçoit AU MOINS le niveau garanti, ou son besoin de base si celui-ci est plus élevé.
359
- requiredBudget += Math.max(need, guaranteedLevel);
360
- }
361
- return requiredBudget <= totalBudget;
362
- };
363
- },
364
-
365
- /**
366
- * Crée un prédicat pour déterminer le taux de vaccination nécessaire pour l'immunité collective.
367
- * @param {object} config - L'objet de configuration.
368
- * @param {number} config.r0 - Le taux de reproduction de base du virus.
369
- * @returns {function(number): boolean} Un prédicat qui prend un `vaccinationRate` (entre 0 et 1) et retourne `true` si l'immunité est atteinte.
370
- */
371
- createHerdImmunityValidator({ r0 }) {
372
- // La formule de base est R_effectif = R0 * (1 - taux_immunité)
373
- // On veut R_effectif < 1
374
- return function achievesHerdImmunity(immunityRate) {
375
- const rEffective = r0 * (1 - immunityRate);
376
- return rEffective < 1;
377
- };
378
- },
379
-
380
- /**
381
- * Crée un prédicat pour trouver le bonus de score minimal pour atteindre un objectif d'équité.
382
- * @param {object} config - L'objet de configuration.
383
- * @param {Array<number>} config.scoresGroupA - Les scores du groupe de référence.
384
- * @param {Array<number>} config.scoresGroupB - Les scores du groupe désavantagé.
385
- * @param {number} config.maxAllowedDisparity - L'écart de score moyen maximal toléré (ex: 0.05 pour 5%).
386
- * @returns {function(number): boolean} Un prédicat qui prend un `bonus` et retourne `true` si l'équité est atteinte.
387
- */
388
- createFairnessThresholdValidator({
389
- scoresGroupA,
390
- scoresGroupB,
391
- maxAllowedDisparity,
392
- }) {
393
- const avgA = scoresGroupA.reduce((a, b) => a + b, 0) / scoresGroupA.length;
394
-
395
- return function isFairEnough(bonus) {
396
- const avgB_corrected =
397
- scoresGroupB.reduce((a, b) => a + b, 0) / scoresGroupB.length + bonus;
398
- const newDisparity = Math.abs(avgA - avgB_corrected) / avgA;
399
- return newDisparity <= maxAllowedDisparity;
400
- };
401
- },
402
-
403
- /**
404
- * Crée un prédicat pour résoudre le "problème du chargeur de paquets" (Packer Problem).
405
- * Le prédicat généré vérifie s'il est possible de transporter une liste d'objets
406
- * en un nombre de voyages donné, avec une certaine capacité par voyage.
407
- *
408
- * @param {object} config - L'objet de configuration.
409
- * @param {Array<number>} config.itemSizes - Un tableau des "tailles" de chaque objet (poids, volume, etc.).
410
- * @param {number} config.maxTrips - Le nombre maximum de "conteneurs" ou de voyages disponibles.
411
- * @returns {function(number): boolean} Un prédicat qui prend une `capacity` et retourne `true` si l'opération est possible.
412
- */
413
- createShippingValidator({ itemSizes, maxTrips }) {
414
- // On pré-calcule le plus gros objet pour une optimisation.
415
- const maxItemSize = Math.max(0, ...itemSizes);
416
-
417
- return function isSufficient(capacity) {
418
- if (capacity < maxItemSize) {
419
- return false; // Impossible si un objet est plus gros que la capacité.
420
- }
421
-
422
- let trips = 1;
423
- let currentLoad = 0;
424
- for (const size of itemSizes) {
425
- if (currentLoad + size <= capacity) {
426
- currentLoad += size;
427
- } else {
428
- trips++;
429
- currentLoad = size;
430
- }
431
- }
432
- return trips <= maxTrips;
433
- };
434
- },
435
-
436
- // On pourrait ajouter d'autres opérateurs ici :
437
- // - createLoadBalancingValidator(...)
438
- // - createRateLimiterValidator(...)
439
- // - etc.
440
-
441
- /**
442
- * Crée un prédicat pour modéliser l'impact d'une taxe carbone.
443
- * @param {object} config - L'objet de configuration.
444
- * @param {function(number): number} config.emissionModel - Une fonction qui prend un prix de taxe et retourne les émissions totales (ex: en millions de tonnes).
445
- * @param {number} config.targetEmissions - Le niveau d'émission à ne pas dépasser.
446
- * @returns {function(number): boolean} Un prédicat qui prend un `taxPrice` et retourne `true` si l'objectif est atteint.
447
- */
448
- createCarbonTaxValidator({ emissionModel, targetEmissions }) {
449
- return function isEffective(taxPrice) {
450
- const currentEmissions = emissionModel(taxPrice);
451
- return currentEmissions <= targetEmissions;
452
- };
453
- },
454
-
455
- /**
456
- * Crée un prédicat pour évaluer l'impact d'une augmentation du salaire minimum.
457
- * @param {object} config - L'objet de configuration.
458
- * @param {function(number): number} config.jobLossModel - Une fonction qui prend un salaire horaire et retourne le % de perte d'emploi estimé.
459
- * @param {number} config.maxAcceptableJobLoss - Le seuil de perte d'emploi à ne pas dépasser (ex: 0.01 pour 1%).
460
- * @returns {function(number): boolean} Un prédicat qui prend un `hourlyWage` et retourne `true` si l'impact est jugé acceptable.
461
- */
462
- createMinimumWageValidator({ jobLossModel, maxAcceptableJobLoss }) {
463
- // Attention : ici, un salaire plus élevé est "moins bon" pour le prédicat.
464
- // La fonction `searchOnAnswer` cherche la première valeur qui retourne `true`.
465
- // On doit donc inverser la logique. On cherche le premier salaire qui est "inacceptable".
466
- return function isUnacceptable(hourlyWage) {
467
- const estimatedJobLoss = jobLossModel(hourlyWage);
468
- return estimatedJobLoss > maxAcceptableJobLoss;
469
- };
470
- },
471
-
472
- /**
473
- * Crée un prédicat pour un système de quota d'admission universitaire.
474
- * @param {object} config - L'objet de configuration.
475
- * @param {Array<number>} config.highSchoolSizes - Un tableau avec le nombre de diplômés par lycée.
476
- * @param {number} config.universityCapacity - Le nombre total de places disponibles.
477
- * @returns {function(number): boolean} Un prédicat qui prend un `quotaPercentage` (0 à 1) et retourne `true` si la capacité n'est pas dépassée.
478
- */
479
- createAdmissionQuotaValidator({ highSchoolSizes, universityCapacity }) {
480
- return function isWithinCapacity(quotaPercentage) {
481
- let admittedStudents = 0;
482
- for (const size of highSchoolSizes) {
483
- admittedStudents += Math.ceil(size * quotaPercentage);
484
- }
485
- return admittedStudents <= universityCapacity;
486
- };
487
- },
488
-
489
- /**
490
- * Crée un prédicat pour trouver un quota de récolte durable.
491
- * @param {object} config - L'objet de configuration.
492
- * @param {function(number): number} config.populationModel - Fonction qui prend le quota de l'année N et retourne la population de l'année N+1.
493
- * @param {number} config.initialPopulation - La population actuelle.
494
- * @returns {function(number): boolean} Un prédicat qui prend un `harvestQuota` et retourne `true` si la population reste stable ou augmente.
495
- */
496
- createSustainableHarvestValidator({ populationModel, initialPopulation }) {
497
- return function isSustainable(harvestQuota) {
498
- const nextYearPopulation = populationModel(harvestQuota);
499
- return nextYearPopulation >= initialPopulation;
500
- };
501
- },
502
-
503
- /**
504
- * Crée un prédicat pour une politique de libération anticipée.
505
- * @param {object} config - L'objet de configuration.
506
- * @param {Array<{sentenceLength: number, timeServed: number, riskScore: number}>} config.inmates - Données sur les détenus.
507
- * @param {number} config.targetPopulation - La population carcérale cible.
508
- * @param {number} config.maxAverageRisk - Le score de risque moyen acceptable pour le groupe libéré.
509
- * @param {number} config.currentPopulation - La population carcérale actuelle.
510
- * @returns {function(number): boolean} Un prédicat qui prend un `minSentenceServedPercentage` et retourne `true` si les conditions sont remplies.
511
- */
512
- createEarlyReleaseValidator({
513
- inmates,
514
- targetPopulation,
515
- maxAverageRisk,
516
- currentPopulation,
517
- }) {
518
- return function isViable(minSentenceServedPercentage) {
519
- const eligibleInmates = inmates.filter(
520
- (p) => p.timeServed / p.sentenceLength >= minSentenceServedPercentage,
521
- );
522
-
523
- if (eligibleInmates.length === 0) {
524
- // Si personne n'est éligible, la population ne baisse pas.
525
- return currentPopulation <= targetPopulation;
526
- }
527
-
528
- const finalPopulation = currentPopulation - eligibleInmates.length;
529
- if (finalPopulation > targetPopulation) {
530
- return false; // Pas assez de libérations pour atteindre l'objectif.
531
- }
532
-
533
- const totalRisk = eligibleInmates.reduce(
534
- (sum, p) => sum + p.riskScore,
535
- 0,
536
- );
537
- const averageRisk = totalRisk / eligibleInmates.length;
538
-
539
- return averageRisk <= maxAverageRisk;
540
- };
541
- },
542
-
543
- /**
544
- * Crée un prédicat pour équilibrer la modération de contenu.
545
- * @param {object} config - L'objet de configuration.
546
- * @param {function(number): {workloadReduction: number, falsePositiveRate: number}} config.moderationModel - Modèle qui simule l'impact d'un seuil.
547
- * @param {number} config.minWorkloadReduction - L'objectif de réduction de charge de travail (ex: 0.9).
548
- * @param {number} config.maxFalsePositiveRate - Le taux de faux positifs à ne pas dépasser (ex: 0.001).
549
- * @returns {function(number): boolean} Un prédicat qui prend un `toxicityThreshold` et retourne `true` si les objectifs sont atteints.
550
- */
551
- createContentModerationValidator({
552
- moderationModel,
553
- minWorkloadReduction,
554
- maxFalsePositiveRate,
555
- }) {
556
- return function isBalanced(toxicityThreshold) {
557
- const { workloadReduction, falsePositiveRate } =
558
- moderationModel(toxicityThreshold);
559
- return (
560
- workloadReduction >= minWorkloadReduction &&
561
- falsePositiveRate <= maxFalsePositiveRate
562
- );
563
- };
564
- },
565
-
566
- /**
567
- * Crée un évaluateur pour trouver le prix qui maximise le revenu.
568
- * @param {object} config - L'objet de configuration.
569
- * @param {function(number): number} config.demandModel - Fonction qui prend un prix et retourne le nombre d'unités vendues.
570
- * @returns {function(number): number} Un évaluateur qui prend un `price` et retourne le revenu total.
571
- */
572
- createOptimalPricingModel({ demandModel }) {
573
- return function revenueEvaluator(price) {
574
- if (price < 0) return 0;
575
- const unitsSold = demandModel(price);
576
- return price * unitsSold;
577
- };
578
- },
579
-
580
- /**
581
- * Crée un évaluateur pour trouver le taux de commission qui maximise le revenu de la plateforme.
582
- * @param {object} config - L'objet de configuration.
583
- * @param {number} config.totalAdvertiserCredits - Le total des crédits disponibles chez les annonceurs.
584
- * @param {number} config.totalWebsites - Le nombre total de sites monétisés.
585
- * @returns {function(number): number} Un évaluateur qui prend un `commissionRate` et retourne le revenu estimé.
586
- */
587
- createPlatformRevenueEvaluator({ totalAdvertiserCredits, totalWebsites }) {
588
- // Modèle de la demande (Annonceurs)
589
- // Plus la commission est basse, plus ils sont prêts à acheter de clics.
590
- const advertiserDemandModel = (commissionRate) => {
591
- // Le nombre de clics "demandés" diminue à mesure que la commission augmente.
592
- // La demande est aussi proportionnelle au total des crédits disponibles.
593
- // --- SOLUTION : Ajout d'une demande de base pour éviter un taux de 0% ---
594
- // Représente l'intérêt potentiel même sans crédits existants.
595
- const baseDemand = (totalAdvertiserCredits || 100) * 10; // On simule 100 crédits de base.
596
- // La demande diminue avec la racine carrée de la commission, ce qui la rend moins sensible au début.
597
- return baseDemand * (1 - Math.pow(commissionRate, 0.5));
598
- };
599
-
600
- // Modèle de l'offre (Webmasters)
601
- // Plus leur rémunération est haute (commission basse), plus ils offrent d'espaces.
602
- const webmasterSupplyModel = (commissionRate) => {
603
- // L'offre de clics augmente quand la commission baisse (et donc que leur gain augmente).
604
- const baseSupply = totalWebsites * 500; // Ex: 500 clics potentiels par site
605
- // L'offre est un peu moins sensible à la commission que la demande.
606
- return baseSupply * (1 - commissionRate * 0.8);
607
- };
608
-
609
- return function platformRevenueEvaluator(commissionRate) {
610
- if (commissionRate < 0 || commissionRate > 1) return 0;
611
- const demand = advertiserDemandModel(commissionRate);
612
- const supply = webmasterSupplyModel(commissionRate);
613
- const clicks = Math.min(demand, supply);
614
- return clicks * commissionRate; // Le revenu est le nombre de clics * la commission
615
- };
616
- },
617
-
618
- /**
619
- * Crée un évaluateur N-Dimensionnel pour le rendement d'une culture en fonction de multiples facteurs.
620
- * @param {object} config - L'objet de configuration.
621
- * @param {Array<{name: string, optimalAmount: number, weight: number, sensitivity: number}>} config.factors - Chaque facteur a une quantité optimale, un poids, et une sensibilité.
622
- * @param {number} config.baseYield - Le rendement de base sans aucun apport.
623
- * @returns {function(Array<number>): number} Un évaluateur qui prend un tableau de quantités (`amounts`) et retourne le rendement total. Le but est de MAXIMISER ce rendement.
624
- */
625
- createMultiFactorCropYieldModel({ factors, baseYield }) {
626
- return function multiFactorYieldEvaluator(amounts) {
627
- if (amounts.length !== factors.length) {
628
- throw new Error(
629
- "Le nombre de quantités doit correspondre au nombre de facteurs.",
630
- );
631
- }
632
-
633
- let totalYield = baseYield;
634
- for (let i = 0; i < factors.length; i++) {
635
- const factor = factors[i];
636
- const amount = amounts[i];
637
- if (amount < 0) return -Infinity; // Pénalité forte pour les valeurs impossibles
638
-
639
- const deviation = amount - factor.optimalAmount;
640
- // La sensibilité contrôle la "largeur" de la parabole. Une sensibilité élevée rend le rendement plus sensible aux écarts.
641
- totalYield +=
642
- factor.weight -
643
- (factor.sensitivity * deviation * deviation) /
644
- (factor.optimalAmount + 1e-6);
645
- }
646
- return totalYield;
647
- };
648
- },
649
-
650
- /**
651
- * Crée un évaluateur pour le rendement d'une culture en fonction de la quantité d'engrais.
652
- * @param {object} config - L'objet de configuration.
653
- * @param {number} config.optimalAmount - La quantité d'engrais qui produit le rendement maximal.
654
- * @param {number} config.maxYield - Le rendement maximal possible.
655
- * @returns {function(number): number} Un évaluateur qui prend une `fertilizerAmount` et retourne le rendement.
656
- */
657
- createCropYieldModel({ optimalAmount, maxYield }) {
658
- // Modèle quadratique simple : le rendement diminue symétriquement autour de l'optimum.
659
- return function yieldEvaluator(fertilizerAmount) {
660
- if (fertilizerAmount < 0) return 0;
661
- const deviation = fertilizerAmount - optimalAmount;
662
- // La formule est une parabole inversée.
663
- const yieldValue = maxYield - (deviation * deviation) / optimalAmount;
664
- return Math.max(0, yieldValue); // Le rendement ne peut pas être négatif.
665
- };
666
- },
667
-
668
- /**
669
- * Crée un évaluateur pour la satisfaction utilisateur d'une durée d'animation.
670
- * @param {object} config - L'objet de configuration.
671
- * @param {number} config.idealDuration - La durée perçue comme parfaite (ex: 250ms).
672
- * @returns {function(number): number} Un évaluateur qui prend une `duration` et retourne un score de satisfaction.
673
- */
674
- createAnimationTimingModel({ idealDuration }) {
675
- // Modèle basé sur une fonction de Gauss. Le score est maximal à la durée idéale et chute rapidement.
676
- return function satisfactionEvaluator(duration) {
677
- if (duration <= 0) return 0;
678
- const deviation = duration - idealDuration;
679
- const sigma = idealDuration / 2; // L'écart-type contrôle la "tolérance" autour de l'idéal.
680
- return Math.exp(-(deviation * deviation) / (2 * sigma * sigma));
681
- };
682
- },
683
-
684
- /**
685
- * Crée un évaluateur pour le problème de la brachistochrone (toboggan le plus rapide).
686
- * @param {object} config - L'objet de configuration.
687
- * @param {{x: number, y: number}} config.startPoint - Le point de départ.
688
- * @param {{x: number, y: number}} config.endPoint - Le point d'arrivée.
689
- * @param {number} config.numPoints - Le nombre de points intermédiaires pour discrétiser la courbe.
690
- * @returns {function(Array<number>): number} Un évaluateur qui prend un tableau de hauteurs `y` et retourne le temps de descente.
691
- */
692
- createBrachistochroneEvaluator({ startPoint, endPoint, numPoints }) {
693
- const g = 9.81; // Accélération gravitationnelle
694
- const segmentLengthX = endPoint.x / (numPoints + 1);
695
- const epsilon = 1e-6; // Pour éviter la division par zéro au départ
696
-
697
- return function evaluateSlideTime(slideShape) {
698
- const points = [
699
- startPoint,
700
- ...slideShape.map((y, i) => ({ x: (i + 1) * segmentLengthX, y })),
701
- endPoint,
702
- ];
703
-
704
- let totalTime = 0;
705
- for (let i = 0; i < points.length - 1; i++) {
706
- const p1 = points[i];
707
- const p2 = points[i + 1];
708
-
709
- const deltaX = p2.x - p1.x;
710
- const deltaY = p2.y - p1.y;
711
- const segmentDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
712
-
713
- // Vitesse basée sur la conservation de l'énergie: v = sqrt(2*g*h)
714
- const v1 = Math.sqrt(2 * g * (startPoint.y - p1.y + epsilon));
715
- const v2 = Math.sqrt(2 * g * (startPoint.y - p2.y + epsilon));
716
- const avgVelocity = (v1 + v2) / 2;
717
-
718
- if (avgVelocity > 0) {
719
- totalTime += segmentDistance / avgVelocity;
720
- }
721
- }
722
- return totalTime;
723
- };
724
- },
725
-
726
- /**
727
- * Crée une fonction qui calcule le gradient pour le problème de la brachistochrone.
728
- * @param {object} config - L'objet de configuration.
729
- * @param {{x: number, y: number}} config.startPoint - Le point de départ.
730
- * @param {{x: number, y: number}} config.endPoint - Le point d'arrivée.
731
- * @param {number} config.numPoints - Le nombre de points intermédiaires.
732
- * @returns {function(Array<number>): Array<number>} Une fonction qui retourne le gradient du temps de descente par rapport aux hauteurs `y`.
733
- */
734
- createBrachistochroneGradient({ startPoint, endPoint, numPoints }) {
735
- const evaluator = this.createBrachistochroneEvaluator({
736
- startPoint,
737
- endPoint,
738
- numPoints,
739
- });
740
- const h = 1e-5; // Petit pas pour la différenciation numérique
741
-
742
- return function calculateGradient(slideShape) {
743
- const gradient = [];
744
- const baseTime = evaluator(slideShape);
745
-
746
- for (let i = 0; i < slideShape.length; i++) {
747
- const newShape = [...slideShape];
748
- newShape[i] += h;
749
- const newTime = evaluator(newShape);
750
-
751
- // Dérivée partielle par différence finie
752
- const derivative = (newTime - baseTime) / h;
753
- gradient.push(derivative);
754
- }
755
- return gradient;
756
- };
757
- },
758
- };
759
16
 
760
17
  const Optimization = {
761
18
  // eslint-disable-line no-unused-vars
@@ -1463,87 +720,6 @@ Optimization.cmaes = function(fitnessFunction, initialSolution, initialStepSize,
1463
720
  return { solution: bestSolution, fitness: bestFitness };
1464
721
  };
1465
722
 
1466
- /**
1467
- * @class BinarySearchTree
1468
- * @description Implémentation d'un Arbre Binaire de Recherche.
1469
- * Chaque noeud a une valeur, et des sous-arbres gauche/droit.
1470
- */
1471
- Dichotomy.BinarySearchTree = class {
1472
- constructor(comparator = Dichotomy._defaultComparator) {
1473
- this.root = null;
1474
- this.comparator = comparator;
1475
- }
1476
-
1477
- /**
1478
- * Insère une nouvelle valeur dans l'arbre.
1479
- * @param {*} value
1480
- */
1481
- insert(value) {
1482
- const newNode = { value, left: null, right: null };
1483
- if (!this.root) {
1484
- this.root = newNode;
1485
- return;
1486
- }
1487
-
1488
- let current = this.root;
1489
- while (true) {
1490
- const comparison = this.comparator(value, current.value);
1491
- if (comparison < 0) {
1492
- // Aller à gauche
1493
- if (!current.left) {
1494
- current.left = newNode;
1495
- return;
1496
- }
1497
- current = current.left;
1498
- } else {
1499
- // Aller à droite (ou si égal, pour permettre les doublons)
1500
- if (!current.right) {
1501
- current.right = newNode;
1502
- return;
1503
- }
1504
- current = current.right;
1505
- }
1506
- }
1507
- }
1508
-
1509
- /**
1510
- * Vérifie si une valeur est présente dans l'arbre.
1511
- * @param {*} value
1512
- * @returns {boolean}
1513
- */
1514
- contains(value) {
1515
- let current = this.root;
1516
- while (current) {
1517
- const comparison = this.comparator(value, current.value);
1518
- if (comparison === 0) {
1519
- return true;
1520
- } else if (comparison < 0) {
1521
- current = current.left;
1522
- } else {
1523
- current = current.right;
1524
- }
1525
- }
1526
- return false;
1527
- }
1528
-
1529
- /**
1530
- * Effectue un parcours In-Order (gauche, racine, droite) qui retourne les éléments triés.
1531
- * @returns {Array<*>}
1532
- */
1533
- inOrderTraversal() {
1534
- const result = [];
1535
- function traverse(node) {
1536
- if (node) {
1537
- traverse(node.left);
1538
- result.push(node.value);
1539
- traverse(node.right);
1540
- }
1541
- }
1542
- traverse(this.root);
1543
- return result;
1544
- }
1545
- };
1546
-
1547
723
  /**
1548
724
  * @namespace Optimization.Operators
1549
725
  * @description Une bibliothèque de "fabriques d'évaluateurs" pour des problèmes d'optimisation complexes,