Aller au contenu

É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 et V par un unique symbole I,
  • C et L par un unique symbole X,
  • M et D par un unique symbole C.

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\).

La fonction pour convertir

Écrire la fonction romain qui prend en argument un nombre entier strictement positif valeur et renvoie son écriture en chiffres romains.

Exemples
🐍 Console Python
>>> romain(4)
'IV'
>>> romain(5)
'V'
>>> romain(6)
'VI'
>>> romain(5042)
'MMMMMXLII'

###
VALEURS = [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-nl# Testsbksl-nlassert romain(4) == "IV"bksl-nlassert romain(5) == "V"bksl-nlassert romain(6) == "VI"bksl-nlassert romain(5042) == "MMMMMXLII"bksl-nlbksl-nl# Tests supplémentairesbksl-nldef romainpy-undcorr(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-nlfor valeur in range(1, 5py-und001):bksl-nl attendu = romainpy-undcorr(valeur)bksl-nl assert romain(valeur) == attendu, f"Erreur en écrivant {valeur}"bksl-nlbksl-nl 5/5
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(3042) == "MMMXLII"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


Un algorithme glouton

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.

📋 Explications
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

Remarque

Vous avez peut-être utilisé un dictionnaire VALEURS= {1000: "M, 900:"CM", 500: "D", 400:"CD" ...}
🤔 Cela a fonctionné ...
Attention, depuis plusieurs versions, dans python les dictionnaires sont parcourus dans l'ordre dans lequel ils sont "écrits" ou saisis. Si vous travaillez sur une version plus ancienne de python, ce n'est pas le cas. Un dictionnaire n'est pas ordonné : l'ordre dans lequel sont écrits les couples (clé, valeur) n'a aucune importance. Vous ne pourrez pas utiliser cette méthode avec d'autres langages. Il faut utiliser pour un algorithme glouton une structure ordonnée.

Méthode arithmétique

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.
🐍 Script Python
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

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\).


Astuce (1)

Penser à un algorithme glouton.

Astuce (2)

On pourra utiliser la liste VALEURS = [..., (4, "IV"), ...] après l'avoir complétée.

Astuce (3)

On pourra utiliser la liste VALEURS = [(1000, "M"), (900, "CM"), (500, "D"), ...] après l'avoir complétée.