Distribution aléatoire

Bonjour/Bonsoir,

Je cherche à distribuer aléatoirement un nombre de points définis dans plusieurs colonnes. Je m'explique:

image

Je souhaiterais distribuer les points de la colonne "Total" dans les 6 colonnes suivantes qui correspondent à des caractéristiques. Ma seule contrainte c'est les colonnes "Max" et "Min", la distribution doit respecter ces valeurs min et max.

Ma solution était: dans un premier temps distribuer les valeurs min dans chaques colonnes, puis de distribuer le reste des points aléatoirement, pour ça je pensais d'abord choisir l'ordre des colonnes aléatoirement pour chaque ligne (Ex: colonne 1 Intel, colonne 2 strentgh, etc.) pour éviter des redondances. Ensuite aléatoirement distribuer les points restants par colonne une par une (Ex: si(reste > val max - val min; alea(1; val max - val min); alea(1; reste)) (d'où le fait de changer l'ordre des colonnes à chaque fois pour éviter que ce soit toujours les mêmes colonnes qui passent en dernier).

Pour faire tout ça je pensais utiliser la fonction LET() que j'ai découvert récemment et que je trouve très pratique pour ce genre de choses mais j'ai plus de mal avec les fonction de tris comme SORT(), SCAN() etc. (dans l'idée j'aimerais mettre la formule la première colonne et qu'elle remplisse les 6 colonnes) donc je ne suis pas parvenu à faire quelque chose de fonctionnel, je ne sais pas si je m'y prend mal et si je prend le problème dans le mauvais sens ou s'il y a juste d'autre fonction que je ne connais pas qui pourrait faire le taf parfaitement.

Voilà le lien vers le fichier test: https://docs.google.com/spreadsheets/d/15SdoDmxV-yACNlh7lywKHwkywP40rwZI-LsPk8HiE5k/edit?usp=sharing

Merci d'avance pour vos réponses !

bonjour toutes et tous et bon week-end

@tester

1) créer un bouton ...Dessin.... Assigner le Script ==> repartitionToutesLignes

repartitionToutesLignes

par : IA (version gratuite)

un début, il bug parfois sur Max. 100

  function repartitionToutesLignes() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const startRow = 3; // début des données
  const lastRow = sheet.getLastRow(); // dernière ligne du tableau
  const startCol = 3; // colonne C (NOM1)
  const endCol = 8;   // colonne H (NOM6)
  const nCols = endCol - startCol + 1;

  // Efface d'abord les anciennes répartitions
  sheet.getRange(startRow, startCol, lastRow - startRow + 1, nCols).clearContent();

  for (let row = startRow; row <= lastRow; row++) {
    const total = sheet.getRange(row, 2).getValue(); // Colonne B (Total)
    const max = sheet.getRange(row, 9).getValue();   // Colonne I (Max)
    const min = sheet.getRange(row, 10).getValue();  // Colonne J (Min)

    // Vérifie les entrées valides
    if (typeof total !== 'number' || isNaN(total) || total <= 0) continue;
    if (typeof max !== 'number' || typeof min !== 'number' || max < min) continue;

    // Fonction interne pour créer une répartition
    const genererRepartition = () => {
      let parts = Array(nCols).fill(0);
      let remaining = total;

      for (let j = 0; j < nCols; j++) {
        const resteCols = nCols - j;
        const maxPossible = Math.min(max, remaining - (resteCols - 1) * min);
        const minPossible = min;

        // si bornes invalides, attribue minimum
        if (maxPossible < minPossible) {
          parts[j] = min;
          remaining -= min;
          continue;
        }

        const value = (j === nCols - 1)
          ? remaining
          : Math.floor(Math.random() * (maxPossible - minPossible + 1)) + minPossible;

        parts[j] = value;
        remaining -= value;
      }

      // Ajustement pour garantir la somme exacte
      const somme = parts.reduce((a, b) => a + b, 0);
      const ecart = total - somme;
      if (ecart !== 0) {
        const idx = Math.floor(Math.random() * nCols);
        parts[idx] += ecart;
      }

      return parts;
    };

    // Première répartition
    let parts = genererRepartition();

    // Vérification de validité
    const estValide = (arr) => arr.every(v => v >= min && v <= max) &&
      arr.reduce((a, b) => a + b, 0) === total;

    // Si la première tentative échoue, refaire une deuxième répartition
    if (!estValide(parts)) {
      parts = genererRepartition();
      // Si encore invalide, force les valeurs dans les bornes et ajuste le dernier
      if (!estValide(parts)) {
        parts = parts.map(v => Math.max(min, Math.min(v, max)));
        const sommeCorrigee = parts.reduce((a, b) => a + b, 0);
        const ecart = total - sommeCorrigee;
        const idx = Math.floor(Math.random() * nCols);
        parts[idx] += ecart;
      }
    }

    // Écrit le résultat dans la feuille
    sheet.getRange(row, startCol, 1, nCols).setValues([parts]);
  }

  SpreadsheetApp.getActive().toast("✅ Répartition aléatoire terminée (avec double vérification) !");
}

Bonsoir, Merci pour votre réponse, c'est vrai que je n'ai pas précisé mais je souhaiterais trouvé une solution utilisant uniquement les fonctions déjà présente dans sheets de base (donc sans passer par apps script ou autre).

Salut,

Avec des formules c'est surement possible mais il va nécessiter des 10aines de calculs intermédiaire ce qui nécessiterai plusieurs colonnes dédiées uniquement à ces calculs, le + simple est de créer une formule personnalisée, par exemple :

/**
 * Répartit un total entier sur 6 cellules aléatoirement dans une plage [min, max].
 * Usage dans Sheets : =DISPATCHER(total; min; max)
 *
 * @param {number} total - Le total à répartir
 * @param {number} min - Valeur minimale autorisée pour chaque cellule
 * @param {number} max - Valeur maximale autorisée pour chaque cellule
 * @return {number[]} - Tableau de 6 entiers aléatoires respectant les contraintes
 * @customfunction
 */
function DISPATCHER(total, min, max) {
  const n = 6;
  if (total < min * n || total > max * n) {
    throw new Error("Impossible de dispatcher : total incompatible avec min/max");
  }
  let parts = Array(n).fill(min);
  let remaining = total - min * n;
  const marge = max - min;
  while (remaining > 0) {
    for (let i = 0; i < n; i++) {
      if (remaining <= 0) break;
      const possible = Math.min(marge - (parts[i] - min), remaining);
      if (possible > 0) {
        // ajouter un entier aléatoire
        const add = Math.floor(Math.random() * (possible + 1));
        parts[i] += add;
        remaining -= add;
      }
    }
  }
  const diff = total - parts.reduce((a, b) => a + b, 0);
  parts[parts.length - 1] += diff;
  if (parts.some(v => v < min || v > max)) {
    throw new Error("Impossible de respecter les bornes min/max avec ce total");
  }
  return [parts];
}

Ce qui donne

image

Bonjour, merci pour votre réponse, c'est à peu près l'idée que j'avais si je devais passer par un script (utilisé une boucle et l'arranger pour avoir plus ou moins de controle sur l'aléatoire). Par contre j'ai vu que dans le doc test que j'ai partagé dans mon premier message le tableau avait été rempli mais il contient des erreurs, je ne sais pas si cela vient de votre formule ou si une autre personne est passé et à fait des tests:

image

Je n'avais pas agit sur votre fichier test, je viens de créer un onglet et d'appliquer la formule personnalisée si vous souhaitez voir ce que ça donne, cela me parait fonctionnel :

image

Bonjour à tous,

Alternativement, on peut travailler en formules uniquement. Ci-après la formule nommée que j'ai ajoutée à votre classeur d'exemple. Notez qu'à chaque recalcul du classeur les valeurs sont réévaluées (utilisation de RANDBETWEEN qui est volatile) !

=LET( baseArr; SEQUENCE(1; 6; _min; 0); _temp; REDUCE( HSTACK(tot; 0); SEQUENCE(COUNT(baseArr)); LAMBDA(a; i; LET( _low; _min; _up; _max; _nbLefts; COUNT(baseArr) - i; _Srem; INDEX(a; i; 1); _maxI; MIN(_up; _Srem - _nbLefts * _low); _minI; MAX(_low; _Srem - _nbLefts * _up); _xi; IF(_nbLefts > 0; RANDBETWEEN(_minI; _maxI); _maxI); VSTACK(a; HSTACK(_Srem - _xi; _xi)) ) ) ); _res1; TOROW(CHOOSECOLS(_temp; 2)); _res2; CHOOSECOLS(_res1; SEQUENCE(COUNT(_res1) - 1; 1; 2)); _res2 )

La formule prend 3 paramètres : le total tot, le min _min et le max _max.

EDIT : voir https://forum.excel-pratique.com/s/goto/1256855 pour formule améliorée.

Sinon de mon côté j'ai persévéré pendant une partie du we et ce matin et je suis parvenu à une solution avec uniquement les fonction de sheets. Je la partage si jamais ça peu intérréssé (c'est pas la plus joli mais c'est plutôt robuste d'apres mes tests et adaptable assez facilement suivant les données):

=LET(
tot; G2;                                                        /*Total à distribué*/
max;RECHERCHEV(E2;Data!$E$24:$F$31;2;FAUX)/5 ;                     /*Valeur max à pas dépasser*/
min;max*0,6;                                                    /*Valeur min souhaité*/
chance;3;                                                        /*Premier aléa, permet de définir si on va modifier une valeur ou non*/
aleastat;max/5;                                                /*Deuxième aléa, permet de définir le montant max de points à ajouter à une valeur*/
tiragealea;ALEA.ENTRE.BORNES(1;6);                             /*Troisième aléa, utilisé pour répartir les colonnes */
reste; tot - (min*6);                                          /*Reste des points à attribuer*/

val_a_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste>0;ALEA.ENTRE.BORNES(1;reste)+min;0+min)));
reste_a; reste - val_a_a +min;
val_a_b; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste_a>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste_a>0;ALEA.ENTRE.BORNES(1;reste_a)+min;0+min)));
reste_b; reste_a - val_a_b +min; 
val_a_c; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste_b>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste_b>0;ALEA.ENTRE.BORNES(1;reste_b)+min;0+min)));
reste_c; reste_b - val_a_c +min;  
val_a_d; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste_c>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste_c>0;ALEA.ENTRE.BORNES(1;reste_c)+min;0+min)));
reste_d; reste_c - val_a_d +min; 
val_a_e; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste_d>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste_d>0;ALEA.ENTRE.BORNES(1;reste_d)+min;0+min)));
reste_e; reste_d - val_a_e +min; 
val_a_f; SI(ALEA.ENTRE.BORNES(1;chance)=1;min; SI(reste_e>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste_e>0;ALEA.ENTRE.BORNES(1;reste_e)+min;0+min)));
reste_f; reste_e - val_a_f +min; 

val_b_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_a; SI(reste_f>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_a;SI(reste_f>0;ALEA.ENTRE.BORNES(1;reste_f)+val_a_a;0+val_a_a)));
reste_g; reste_f - val_b_a +val_a_a;
val_b_b; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_b; SI(reste_g>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_b;SI(reste_g>0;ALEA.ENTRE.BORNES(1;reste_g)+val_a_b;0+val_a_b)));
reste_h; reste_g - val_b_b +val_a_b; 
val_b_c; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_c; SI(reste_h>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_c;SI(reste_h>0;ALEA.ENTRE.BORNES(1;reste_h)+val_a_c;0+val_a_c)));
reste_i; reste_h - val_b_c +val_a_c;  
val_b_d; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_d; SI(reste_i>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_d;SI(reste_i>0;ALEA.ENTRE.BORNES(1;reste_i)+val_a_d;0+val_a_d)));
reste_j; reste_i - val_b_d +val_a_d; 
val_b_e; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_e; SI(reste_j>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_e;SI(reste_j>0;ALEA.ENTRE.BORNES(1;reste_j)+val_a_e;0+val_a_e)));
reste_k; reste_j - val_b_e +val_a_e; 
val_b_f; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_f; SI(reste_k>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_f;SI(reste_k>0;ALEA.ENTRE.BORNES(1;reste_k)+val_a_f;0+val_a_f)));
reste_l; reste_k - val_b_f +val_a_f;

val_c_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_a; SI(reste_l>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_a;SI(reste_l>0;ALEA.ENTRE.BORNES(1;reste_l)+val_b_a;0+val_b_a)));
reste_m; reste_l - val_c_a +val_b_a+SI(val_c_a>max;val_c_a-max;0);
val_c_b; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_b; SI(reste_m>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_b;SI(reste_m>0;ALEA.ENTRE.BORNES(1;reste_m)+val_b_b;0+val_b_b)));
reste_n; reste_m - val_c_b +val_b_b+SI(val_c_b>max;val_c_b-max;0);
val_c_c; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_c; SI(reste_n>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_c;SI(reste_n>0;ALEA.ENTRE.BORNES(1;reste_n)+val_b_c;0+val_b_c)));
reste_o; reste_n - val_c_c +val_b_c+SI(val_c_c>max;val_c_c-max;0);
val_c_d; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_d; SI(reste_o>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_d;SI(reste_o>0;ALEA.ENTRE.BORNES(1;reste_o)+val_b_d;0+val_b_d)));
reste_p; reste_o - val_c_d +val_b_d+SI(val_c_d>max;val_c_d-max;0);
val_c_e; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_e; SI(reste_p>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_e;SI(reste_p>0;ALEA.ENTRE.BORNES(1;reste_p)+val_b_e;0+val_b_e)));
reste_q; reste_p - val_c_e +val_b_e+SI(val_c_e>max;val_c_e-max;0);
val_c_f; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_f; SI(reste_q>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_f;SI(reste_q>0;ALEA.ENTRE.BORNES(1;reste_q)+val_b_f;0+val_b_f)));
reste_r; reste_q - val_c_f +val_b_f+SI(val_c_f>max;val_c_f-max;0);

val_d_a; SI(val_c_a>max;max; SI(reste_r >0; SI(reste_r > max-val_c_a; max; val_c_a + reste_r);val_c_a)); 
reste_s; reste_r - val_d_a +SI(val_c_a>max;max;val_c_a);
val_d_b; SI(val_c_b>max;max;SI(reste_s >0; SI(reste_s > max-val_c_b; max; val_c_b + reste_s);val_c_b)); 
reste_t; reste_s - val_d_b +SI(val_c_b>max;max;val_c_b);
val_d_c; SI(val_c_c>max;max;SI(reste_t >0; SI(reste_t > max-val_c_c; max; val_c_c + reste_t);val_c_c)); 
reste_u; reste_t - val_d_c +SI(val_c_c>max;max;val_c_c);
val_d_d; SI(val_c_d>max;max;SI(reste_u >0; SI(reste_u > max-val_c_d; max; val_c_d + reste_u);val_c_d)); 
reste_v; reste_u - val_d_d +SI(val_c_d>max;max;val_c_d);
val_d_e; SI(val_c_e>max;max;SI(reste_v >0; SI(reste_v > max-val_c_e; max; val_c_e + reste_v);val_c_e)); 
reste_w; reste_v - val_d_e +SI(val_c_e>max;max;val_c_e);
val_d_f; SI(val_c_f>max;max;SI(reste_w >0; SI(reste_w > max-val_c_f; max; val_c_f + reste_w);val_c_f)); 

str; IFS(tiragealea=1;val_d_a;tiragealea=2;val_d_b;tiragealea=3;val_d_c;tiragealea=4;val_d_d;tiragealea=5;val_d_e;tiragealea=6;val_d_f);
dex; IFS(tiragealea=6;val_d_a;tiragealea=1;val_d_b;tiragealea=2;val_d_c;tiragealea=3;val_d_d;tiragealea=4;val_d_e;tiragealea=5;val_d_f);
con; IFS(tiragealea=5;val_d_a;tiragealea=6;val_d_b;tiragealea=1;val_d_c;tiragealea=2;val_d_d;tiragealea=3;val_d_e;tiragealea=4;val_d_f);
int; IFS(tiragealea=4;val_d_a;tiragealea=5;val_d_b;tiragealea=6;val_d_c;tiragealea=1;val_d_d;tiragealea=2;val_d_e;tiragealea=3;val_d_f);
wis; IFS(tiragealea=3;val_d_a;tiragealea=4;val_d_b;tiragealea=5;val_d_c;tiragealea=6;val_d_d;tiragealea=1;val_d_e;tiragealea=2;val_d_f);
cha; IFS(tiragealea=2;val_d_a;tiragealea=3;val_d_b;tiragealea=4;val_d_c;tiragealea=5;val_d_d;tiragealea=6;val_d_e;tiragealea=1;val_d_f);

TRANSPOSE({str;dex;con;int;wis;cha}))

Et pour revenir plus en détail sur les différents blocs:
Le premier bloc d'initialisation:

val_a_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;min;                        SI(reste>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min;SI(reste>0;ALEA.ENTRE.BORNES(1;reste)+min;0+min)));
reste_a; reste - val_a_a +min;

val_a_a;                                                     /*Première valeur (1 valeur par colonne), qui deviendra "val_b_a" puis "val_c_a" etc.*/
SI(ALEA.ENTRE.BORNES(1;chance)=1;min   /*Premier test, 2 chance sur 3 de modifier la valeur sinon on applique juste le "min" pour l'initialisation*/
SI(reste>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+min /*Si mon reste est plus grand que la valeur max que je souhaite attribué je fais un aléa classique pour modifier ma valeur + ajout du "min" pour l'initialisation */ 
SI(reste>0;ALEA.ENTRE.BORNES(1;reste)+min;0+min) /*Si mon reste est plus petit mais quand même supérieur à "0" aléa entre 1 et mon reste + ajout du "min" pour l'initialisation, Sinon juste ajout de min si mon reste est "0" */ 
reste_a; reste - val_a_a +min; /*Mise à jour du reste après chaque action*/

Le deuxième bloc (répétable autant de fois que l'on veut):

val_b_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_a_a; SI(reste_f>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_a_a;SI(reste_f>0;ALEA.ENTRE.BORNES(1;reste_f)+val_a_a;0+val_a_a)));
reste_g; reste_f - val_b_a +val_a_a;

/*globalement la même chose mais sans l'ajout du min pour l'initialisation*/ 
/* /!\ Attention au manque de sécurité ici que je n'ai pas mis car ne me concerne pas ici mais en fonciton des valeurs "min" "max" et "aleastat" le reste peut être erroné (négatif ou plus à jour)*/

La solution (que j'utilise après): reste_g; reste_f - val_b_a +val_a_a + SI(val_c_a>max;val_c_a-max;0) (permet de mettre ajour correctement le reste)

Troisième bloc le même que le deuxième pour moi mais avec la sécurité:

val_c_a; SI(ALEA.ENTRE.BORNES(1;chance)=1;val_b_a; SI(reste_l>aleastat;ALEA.ENTRE.BORNES(1;aleastat)+val_b_a;SI(reste_l>0;ALEA.ENTRE.BORNES(1;reste_l)+val_b_a;0+val_b_a)));
reste_m; reste_l - val_c_a +val_b_a+SI(val_c_a>max;val_c_a-max;0);

Quatrième bloc pour répartir le reste (afin d'éviter de répéter trop fois les étapes d'avant j'ai décidé qu'a partir de ce moment là j'allais juste répartir mon reste):

val_d_a; SI(val_c_a>max;max; SI(reste_r >0; SI(reste_r > max-val_c_a; max; val_c_a + reste_r);val_c_a)); 
reste_s; reste_r - val_d_a +SI(val_c_a>max;max;val_c_a);

SI(val_c_a>max;max; SI(reste_r >0; /*On vérifie que ma valeur n'a pas atteint le max puis que le reste n'est pas à 0*/
SI(reste_r > max-val_c_a; max; val_c_a + reste_r) /*On vérifie si mon reste n'est pas trop grand et que ma valeur ne va pas dépasser le max autoriser, si c'est le cas on max la valeur sinon on ajoute le reste*/

Et le dernier bloc qui sert uniquement à distribuer mes 6 valeurs dans les colonnes aléatoirement

str; IFS(tiragealea=1;val_d_a;tiragealea=2;val_d_b;tiragealea=3;val_d_c;tiragealea=4;val_d_d;tiragealea=5;val_d_e;tiragealea=6;val_d_f);
/*En fonction de tiragealea str prend une des 6 valeurs, et on répète pour chacune de nos 6 valeurs*/

Et pour finir j'utilise un transpose pour distribuer ça proprement dans les cellules.

Voilà un aperçu du résulat:

image

Les points sont bel et bien répartit aléatoirement dans les 6 colonnes, aucune colonne n'est "privilégié" dans le calcul et les valeur min et max son respecté.
Comme dit plus haut c'est pas très joli mais ça a le mérite de fonctionné et d'être assez solide (surtout quand on met bien les sécurité qu'il faut).
Merci pour l'aide et les propositions apportées ! Bonne journée !

Bonjour Saboh, serait-il possible d'avoir plus de détails sur le fonctionnement de votre fonction ?

Je suis loin d'avoir un grande maitrise sur sheets et je n'arrive pas a bien comprendre certaines utilisation de fonctions.

Bonjour Saboh, serait-il possible d'avoir plus de détails sur le fonctionnement de votre fonction ?

Je suis loin d'avoir un grande maitrise sur sheets et je n'arrive pas a bien comprendre certaines utilisation de fonctions.

Bonjour Julos,

Avant de voir les fonctions il faut définir une méthode de résolution. Voici celle que j'ai implémentée :

Méthode de résolution

Problème : on cherche à remplir une liste de 6 valeurs avec des nombres compris entre min et max, de manière à ce que la somme de ces nombre soit égale à un total prédéfini.

Stratégie de résolution :

  • On va parcourir la liste case par case, et pour chacune calculer un score selon des bornes précises :

La borne min sera le max entre [le min prédéfini] et [total_courant - la somme maximal des résultats restant à calculer].

La borne max sera le min entre [la borne max] et [total_courant – la somme minimale des résultats restant à calculer].

  • On remplit le tableau successivement en recalculant ces bornes au fur et à mesure, et en prenant une valeur random entre les 2 bornes.
  • C'est terminé, on a un tableau qui correspond au critère, car plus on avance et plus les 2 bornes se ressèrent.
  • AJOUT (dans votre classeur la formule DISTRIBALEA2) : cependant cette méthode a un biais : comme on remplit de gauche à droite, même si l'on prend des valeurs aléatoires, il y a un risque que les colonnes de droite, traitées en dernier, soient "moins pleines" que celles de gauche. Donc pour corriger le tir on mélange le tableau obtenu de manière aléatoire.

EXEMPLE : total = 481, min = 60, max = 100.

Case 1 :

borne min = MAX(60; 481 – 100*5) = 60

borne max = MIN(100; 481 – 60*5) = 100

valeur aléatoire choisie = 96

Case 2 :

borne min = MAX(60; 481 – 96 – 100*4) = 60

borne max = MIN(100; 481 – 96 – 60*4) = 100

… On choisit : 96, 67, 81, 96 avec ces bornes

Case 5 :

(total courant = 481-96-67-81-96=141)

borne min = MAX(60; 141 – 100*1) = 60

borne max = MIN(100; 141 – 60*1) = 81

valeur aléatoire choisie = 80

Case 6 :

(total courant = 481-96-67-81-96-80=61)

borne min = MAX(60; 61 – 100*0) = 61

borne max = MIN(100; 61 – 60*0) = 61

valeur aléatoire choisie = 61 (obligé)

On obtient la liste 96, 67, 81, 96, 80, 61. On la mélange et on a une distribution aléatoire (ça évite que en moyenne, sur de gros volumes, Charisma = dernière colonne se retrouve plus bas que Strength = première colonne).

Implémentation dans GSheets

Comment implémenter cela maintenant dans GSheets ? Et bien on va utiliser REDUCE qui permet de parcourir un tableau et d'y effectuer, à chaque cellule, un calcul. Je vais faire un tableau à 2 colonnes : en premier les _Srem = "remaining sum" = le total courant) et en 2e les valeurs aléatoires choisies). C'est ce que fait la formule ci-après.

Honnêtement, ce n'est pas très compliqué à comprendre si vous maitrisez déjà LET+LAMBDA+REDUCE. Si ce n'est pas le cas, je vous invite à vous former sur ces sujets dans un premier temps. Sur Google Sheets il n'y a peut-être pas beaucoup de tutoriels, mais sur Excel vous en trouverez davantage et le fonctionnement est identique. Je vous renvoie vers Fonction Excel : REDUCE (et même si c'est destiné à un public plus "programmeur" : [O365] Passer du VBA vers les nouvelles fonctions dynamiques).

Formule révisée

(mélange aléatoire a posteriori) :

=LET(
    baseArr; SEQUENCE(1; 6; _min; 0);
    _temp; REDUCE(
        HSTACK(tot; 0);
        SEQUENCE(COUNT(baseArr));
        LAMBDA(a; i;
            LET(
                _nbLefts; COUNT(baseArr) - i;
                _Srem; INDEX(a; i; 1);
                _maxI; MIN(_max; _Srem - _nbLefts * _min);
                _minI; MAX(_min; _Srem - _nbLefts * _max);
                _xi; RANDBETWEEN(_minI; _maxI);
                VSTACK(a; HSTACK(_Srem - _xi; _xi))
            )
        )
    );
    _res1; CHOOSECOLS(_temp; 2);
    _res2; CHOOSEROWS(_res1; SEQUENCE(COUNT(_res1) - 1; 1; 2));
    _res3; SORT(HSTACK(_res2; RANDARRAY(6)); 2);
    TOROW(CHOOSECOLS(_res3; 1))
)

Super, merci beacoup pour les détails je vais me pencher sur ça et aller voir plus en details LAMBDA() et REDUCE() ! Merci tout le monde pour l'aide !

Je vous en prie, content d'aider. N'oubliez pas de marquer les différentes solutions (fonctionnelles) qui vous ont été proposées pour clôturer le fil.

Bonne journée.

Rechercher des sujets similaires à "distribution aleatoire"