Écrire un nombre en chiffres romains⚓︎
On souhaite dans cet exercice écrire des nombres entiers strictement positifs en chiffres romains.
Cette notation a évolué au fil des siècles. On se propose ici d'utiliser la méthode fixée au Moyen-Âge en se limitant aux entiers inférieurs à \(5\,000\).
1. Symboles utilisés
Les symboles valides sont les suivants :
Valeur | Symbole |
---|---|
\(1\) | I |
\(5\) | V |
\(10\) | X |
\(50\) | L |
\(100\) | C |
\(500\) | D |
\(1\,000\) | M |
Ainsi, MMXII
(\(2012\)) est une écriture valide, AM
non.
2. Ordre d'écriture
La représentation d'un nombre en chiffres romains se lit de gauche à droite. Dans le cas de base, on y rencontre des symboles de valeurs décroissantes.
Ainsi, XVI
est l'écriture valide du nombre \(16\), VIX
non.
La règle n°5 autorise toutefois certaines entorses à cette règle.
3. Notation additive
Dans le cas de base, les valeurs des symboles sont additionnées.
Ainsi LXXIII
représente \(50+10+10+1+1+1=73\).
4. Répétitions
Le symbole M
peut être répété autant de fois que nécessaire.
Tous les autres symboles ne peuvent pas être répétés plus de 3 fois (inclus).
Ainsi, MMMMVI
(\(4\,006\)) est une écriture valide, MMMMIIIIII
non.
Cette règle a été fixée au Moyen-Âge. L'écriture MMMMIIIIII
était valide dans l'Antiquité !
5. Notation soustractive
Afin d'éviter les répétitions interdites, on s'autorise des entorses à la règle n°2.
On peut ainsi faire précéder les symboles :
X
etV
par un unique symboleI
,C
etL
par un unique symboleX
,M
etD
par un unique symboleC
.
Dans ces cas, la valeur qui précède est soustraite à la valeur précédée.
On a donc les correspondances suivantes :
Symboles | Valeurs |
---|---|
IV |
\(4\) |
IX |
\(9\) |
XL |
\(40\) |
... | ... |
Ainsi, MCMXLI
représente \(1\,000+900+40+1=1\,941\).
Écrire la fonction romain
qui prend en argument un nombre entier strictement positif valeur
et renvoie son écriture en chiffres romains.
Exemples
>>> romain(4)
'IV'
>>> romain(5)
'V'
>>> romain(6)
'VI'
>>> romain(4042)
'MMMMXLII'
def romain(valeur):bksl-nl ...bksl-nlbksl-nlbksl-nl# Testsbksl-nlassert romain(4) == "IV"bksl-nlassert romain(5) == "V"bksl-nlassert romain(6) == "VI"bksl-nlassert romain(4042) == "MMMMXLII"bksl-nlbksl-nlVALEURS = [bksl-nl (1000, "M"),bksl-nl (900, "CM"),bksl-nl (500, "D"),bksl-nl (400, "CD"),bksl-nl (100, "C"),bksl-nl (90, "XC"),bksl-nl (50, "L"),bksl-nl (40, "XL"),bksl-nl (10, "X"),bksl-nl (9, "IX"),bksl-nl (5, "V"),bksl-nl (4, "IV"),bksl-nl (1, "I"),bksl-nl]bksl-nlbksl-nlbksl-nldef romain(valeur):bksl-nl resultat = ""bksl-nl for entier, symbole in VALEURS:bksl-nl while valeur >= entier:bksl-nl valeur -= entierbksl-nl resultat += symbolebksl-nl return resultatbksl-nlbksl-nl
A
On procède de façon analogue au « Rendu de monnaie » : au lieu de rendre en priorité les « pièces » les plus grandes, ici, on écrit en priorité les « valeurs » les plus grandes.
Créer une chaîne de caractère vide et l'affecter à la variable résultat
Pour chaque nombre utilisable :
Tant que la valeur à convertir est supérieure ou égale à ce nombre :
Concaténer l'écriture romaine de ce nombre au résultat
Soustraire ce nombre à la valeur à convertir
On peut aussi procéder en utilisant des divisions euclidiennes afin de ne pas écrire de boucle while
. On rappelle en effet que :
1748 // 1000
est évalué à1
, c'est le quotient de la division euclidienne de \(1\,748\) par \(1\,000\) ;1748 % 1000
est évalué à748
, c'est le reste de cette même division.
def romain(valeur):
resultat = ""
for entier, symbole in VALEURS:
quotient = valeur // entier
valeur = valeur % entier
resultat += symbole * quotient # concaténation de caractères
return resultat
Il est aussi possible de travailler avec un dictionnaire {valeur: symbole}
. Cette approche pose néanmoins la question de l'ordre du parcours d'un dictionnaire. Il est en effet indispensable de parcourir les couples (valeur, symbole)
dans l'ordre décroissant des valeurs.
Depuis Python 3.7, les parcours des dictionnaires sont effectués dans l'ordre d'insertion des clés. On peut donc procéder ainsi :
DICO_VALEURS = {
1000: "M", 900: "CM", 500: "D", 400: "CD",
100: "C", 90: "XC", 50: "L", 40: "XL",
10: "X", 9: "IX", 5: "V", 4: "IV",
1: "I"
}
def romain(valeur):
resultat = ""
for entier, symbole in DICO_VALEURS:
while valeur >= entier:
valeur -= entier
resultat += DICO_VALEURS[entier]
return resultat
Il faut toutefois garder à l'esprit que cette approche dépend de la version de Python utilisée : elle n'est pas généralisable ni à l'ensemble des versions de Python ni à tous les langages de programmation.
Et après ?
Les entiers supérieurs ou égaux à \(5\,000\) s'écrivent en utilisant une barre horizontale indiquant qu'il faut multiplier le facteur concerné par \(1\,000\).
Par exemple \(\overline{\text{VI}}\text{CCCVIII}\) représente \(6\,308\) car le \(\overline{\text{VI}}\) est multiplié par \(1\,000\).
Z
Astuce (1)
Penser à un algorithme glouton.
Astuce (2)
On pourra utiliser la liste VALEURS = [(1000, "M"), (900, "CM"), (500, "D"), ...]
après l'avoir complétée.