C++ calcul entier entre 1 et 7
Calculez immédiatement comment ramener une valeur vers un entier compris entre 1 et 7 en C++, comparez les approches modulo, rejet et tirage uniforme, puis visualisez la distribution obtenue avec un graphique interactif.
Calculateur interactif
Guide expert : comment faire un calcul entier entre 1 et 7 en C++
Le besoin de produire ou de ramener une valeur à un entier compris entre 1 et 7 apparaît très souvent en développement C++. On le rencontre dans les jeux de dés, les systèmes de rotation hebdomadaire, la planification sur les jours de la semaine, le partitionnement d’entrées en sept catégories, ou encore dans certains algorithmes de hachage et d’échantillonnage. Pourtant, la bonne méthode dépend du problème exact. Voulez-vous transformer un entier quelconque en une classe de 1 à 7 ? Voulez-vous tirer une valeur aléatoire uniformément répartie entre 1 et 7 ? Ou simplement valider qu’une valeur saisie appartient déjà à cet intervalle ?
La distinction est essentielle. Beaucoup de développeurs écrivent spontanément rand() % 7 + 1. Cette écriture fonctionne visuellement, mais elle peut introduire un biais statistique si la taille de l’espace de sortie du générateur n’est pas un multiple exact de 7. À petite échelle, ce biais paraît négligeable. À grande échelle, ou dans un contexte sensible comme une simulation, un moteur de jeu, un test statistique ou un mécanisme de sélection, il devient un vrai sujet de qualité. C’est pour cela qu’en C++ moderne on recommande plutôt l’usage de std::uniform_int_distribution.
1. Cas simple : ramener n’importe quel entier dans l’intervalle [1, 7]
Si votre objectif n’est pas de faire de l’aléatoire, mais simplement de convertir une valeur quelconque vers l’ensemble {1, 2, 3, 4, 5, 6, 7}, alors la solution la plus robuste est le modulo sûr. En C++, le piège classique vient des entiers négatifs, car le résultat de % garde le signe du dividende. Pour cette raison, on préfère la formule suivante :
Pourquoi cette formule fonctionne-t-elle ? Le premier n % 7 donne un reste compris entre -6 et 6. En ajoutant 7, on remet les restes négatifs dans une zone positive. En reprenant le modulo 7, on obtient cette fois un entier entre 0 et 6. Enfin, en ajoutant 1, on décale vers 1 à 7. C’est une technique fiable pour mapper un identifiant, un index ou une clé vers sept classes.
- Avantage : très rapide, déterministe, idéal pour des besoins métier.
- Limite : ce n’est pas une méthode de génération aléatoire équitable si la source ne l’est pas déjà.
- Cas d’usage : rotation de tâches, partition logique, regroupement cyclique.
2. Cas aléatoire : pourquoi rand() % 7 + 1 peut biaiser les résultats
Le problème vient du fait que rand() renvoie une valeur entre 0 et RAND_MAX. Cela représente donc RAND_MAX + 1 valeurs possibles. Si ce total n’est pas divisible par 7, certaines sorties reçoivent une valeur source de plus que les autres après l’opération % 7. Ce phénomène est appelé modulo bias.
Voici des statistiques exactes, basées sur de vraies tailles d’espace de sortie. Elles montrent comment une simple opération modulo peut répartir les résultats de manière légèrement inégale.
| Taille de l’espace source N | N mod 7 | Répartition par x % 7 |
Impact mesurable |
|---|---|---|---|
| 15 | 1 | 1 classe reçoit 3 valeurs, 6 classes reçoivent 2 valeurs | La classe favorisée a 50 % de cas en plus que les autres |
| 32 768 | 1 | 1 classe reçoit 4 682 valeurs, 6 classes reçoivent 4 681 valeurs | Biais faible mais réel, typique d’un RAND_MAX à 32 767 |
| 2 147 483 648 | 2 | 2 classes reçoivent 306 783 379 valeurs, 5 classes reçoivent 306 783 378 valeurs | Biais extrêmement faible mais mathématiquement présent |
Sur un petit programme amateur, cela ne casse pas tout. Mais sur des millions de tirages, le déséquilibre s’accumule. En pratique, si vous avez besoin d’un vrai tirage uniforme entre 1 et 7, il faut éliminer les valeurs excédentaires plutôt que de les rabattre brutalement avec un modulo.
3. La méthode correcte pour éviter le biais : le rejet
La méthode par rejet consiste à n’accepter que la plus grande portion de l’espace source divisible par 7. Si votre générateur produit des valeurs de 0 à RAND_MAX, vous calculez une limite d’acceptation :
Avec cette approche, chaque entier de 1 à 7 est obtenu exactement le même nombre de fois parmi les valeurs acceptées. Les valeurs au-delà de la limite sont simplement ignorées et l’on relance. C’est la technique historique qui corrige proprement le biais modulo.
| Espace source N | Valeurs acceptées | Valeurs rejetées | Taux d’acceptation réel |
|---|---|---|---|
| 15 | 14 | 1 | 93,33 % |
| 32 768 | 32 767 | 1 | 99,9969 % |
| 2 147 483 648 | 2 147 483 646 | 2 | 99,9999999 % |
On voit immédiatement qu’avec de grands espaces de sortie, le coût du rejet est minime. C’est pourquoi cette technique est réputée très efficace. Elle garantit l’équité sans impact significatif sur les performances dans la grande majorité des cas.
4. La solution recommandée en C++ moderne
Depuis C++11, la meilleure pratique consiste généralement à utiliser les outils de la bibliothèque standard :
Cette écriture est préférable pour plusieurs raisons. D’abord, elle exprime clairement l’intention : vous voulez un entier uniforme dans un intervalle fermé. Ensuite, elle évite de réinventer un mécanisme de correction de biais. Enfin, elle s’intègre naturellement dans un code moderne, testable et lisible.
std::random_devicefournit une graine initiale.std::mt19937est un moteur pseudo-aléatoire performant et largement utilisé.std::uniform_int_distributiontransforme la sortie du moteur en distribution uniforme sur [1, 7].
Il faut néanmoins bien comprendre que le moteur et la distribution jouent deux rôles distincts. Le moteur produit une séquence de nombres pseudo-aléatoires. La distribution convertit cette séquence vers la forme statistique souhaitée. Beaucoup d’erreurs en C++ viennent du fait qu’on mélange ces deux concepts.
5. Quand utiliser chaque approche ?
Règle simple : utilisez le modulo sûr pour normaliser une donnée métier, utilisez la distribution uniforme pour tirer un nombre aléatoire, et réservez la technique par rejet aux cas où vous manipulez vous-même un générateur brut et devez garantir l’absence de biais.
- Vous avez un identifiant client, un numéro de lot, un index de boucle : utilisez le modulo sûr.
- Vous simulez un dé à 7 faces : utilisez
std::uniform_int_distribution<int>(1,7). - Vous devez expliquer ou implémenter manuellement le traitement d’un générateur brut : utilisez le rejet.
- Vous validez une entrée utilisateur : vérifiez simplement
n >= 1 && n <= 7.
6. Erreurs fréquentes à éviter
La première erreur consiste à oublier les valeurs négatives. n % 7 + 1 ne suffit pas si n peut être négatif. La deuxième erreur consiste à croire que rand() % 7 + 1 est parfaitement uniforme. La troisième est de recréer le moteur pseudo-aléatoire à chaque appel, ce qui détruit souvent la qualité des tirages. Enfin, certains développeurs utilisent une même graine fixe sans le vouloir et obtiennent donc toujours la même séquence.
Une bonne implémentation C++ doit être explicite, stable et adaptée au besoin réel. Si votre code relève d’une logique métier, recherchez la prévisibilité. Si votre code relève d’une logique statistique ou ludique, recherchez l’uniformité. Ce ne sont pas les mêmes objectifs.
7. Références utiles et sources d’autorité
Pour approfondir le sujet de la qualité du hasard, de la génération pseudo-aléatoire et des exigences de robustesse, vous pouvez consulter des ressources institutionnelles et académiques comme NIST Random Bit Generation Documentation, la page de cours de programmation de Stanford CS106B ou encore les archives de cours de Princeton COS126. Même si ces ressources ne donnent pas toutes exactement le même exemple sur l’intervalle 1 à 7, elles sont très utiles pour comprendre les bases de la génération aléatoire correcte, des distributions et de la qualité des implémentations.
8. Conclusion pratique
Pour un calcul entier entre 1 et 7 en C++, il n’existe pas une seule réponse universelle, mais trois bonnes réponses selon le contexte. Si vous mappez une valeur arbitraire vers un ensemble de sept états, choisissez le modulo sûr. Si vous partez d’un générateur brut et devez conserver une parfaite équité, choisissez le rejet. Si vous écrivez du C++ moderne, utilisez la bibliothèque standard avec std::mt19937 et std::uniform_int_distribution. Cette hiérarchie de choix vous évitera la plupart des erreurs rencontrées en production.
Le calculateur ci-dessus vous aide justement à visualiser ces différences. En entrant une valeur source, un RAND_MAX et un nombre d’essais, vous pouvez voir immédiatement la valeur calculée, la formule utilisée et la manière dont les fréquences se répartissent entre 1 et 7. C’est un excellent moyen de comprendre la théorie en la confrontant à des résultats concrets.