Vous utilisez un navigateur obsolète, veuillez le mettre à jour.
En poursuivant votre navigation sur le site, vous acceptez l’utilisation de cookies par OpenClassrooms pour vous proposer des services et offres adaptées à vos centres d’intérêts. Notre politique de cookies.
Accepter
Une question, on doit aussi faire un convertisseur chiffre arabe -> chiffre romain ou pas?
(Au fait, On pourrait-utiliser ca en crypto en poussant un peu plus )
Une question, on doit aussi faire un convertisseur chiffre arabe -> chiffre romain ou pas?
(Au fait, On pourrait-utiliser ca en crypto en poussant un peu plus )
C'est pas dit je crois (et convertir romain -> arabe en gérant les erreurs, c'est déjà ça ).
Comme vous pouvez vous en douter, votre programme devra "traduire" un nombre entier en chiffre romain (valide) et afficher le résultat dans la console.
<édit> Juste une petite question sur le niveau 2.
Si le nombre romain entré est XDD, par exemple, il viole la règle 1 (pas en ordre décroissant) et la règle 5 (trop long). Dans ce cas, le programme doit-il afficher que le nombre XDD viole la règle 1 et la règle 5? Ou s'il préciser qu'il viole la règle 5 est suffisant?
Je pense que tu dis ce tout ce qui ne va pas (genre : Votre nombre XDD ne convient pas, car votre écriture est trop longue, et que les symboles doivent être données par ordre décroissant. Voici la correction : MX.
Votre nombre est : 1010. )
Sinon, si le programme a une erreur, doit-on quand même donner le résultat (ou, s'il y en a deux ?), et une correction ?
Voilà
PS : petit hs mais bon... bravo pour ton poste de validateur
Indiquer l'erreur la plus grave (comme le fait le compilateur C++).
La règle numéro 1 étant plus importante que la 2 qui est elle-même plus importante que la 3 etc.
Après, tu peux donner un résultat mais suivant l'erreur, on ne peut pas savoir ce que cela voulait réelement dire. Donc non.
Et, s'il est possible, on donne une correction?
Comme ceci?
XXXX
Chiffre invalide. Il viole la regle 5.
On pourrait l'ecrire de maniere plus courte.
Le resultat serait : XL
Objet invalide
Ou alors, comme ceci?
XXXX
Chiffre invalide. Il viole la regle 5.
On pourrait l'ecrire de maniere plus courte.
Le resultat serait : XL
Nombre romains => XL
Nombre arabe ==> 40
a- Ton Exemple est certes Minimal, mais aussi totalement inComplet.
b- Ce n'est pas le bon endroit pour poser des questions
c- Le but est de manipuler le C++, donc par extension std::string, et non les chaines C zéro-terminées.
d- Une fonction qui attend un argument, on l'appelle en lui refilant un paramètre.
Après une longue absence, voici la suite de l'aventure. Cet exercice est volontairement plus difficile que le précédent.
Exercice du mois d'avril 2009
Nom : Polynômes Sujet : Classes, surcharge d'opérateurs, algorithme
Lorsque l'on fait du calcul numérique (simulation de choses par un ordinateur), on a souvent besoin de représenter des objets mathématiques. Le cas des fractions a déjà été traité, il est temps de s'attaquer au cas des polynômes.
Dans le cadre de cet exercice, on considèrera la définition suivante d'un polynôme.
Un polynome est une somme de puissances où chaque terme est pondéré par un coefficient.
Les coefficients <math>\(a_1, a_2, \dots, a_n\)</math> ainsi que <math>\(x\)</math> sont des nombres réels. Le nombre entier <math>\(n\)</math> est appelé le degré du polynôme.
Les expressions suivantes sont des polynomes:
<math>\(1 + 3x +x^2\)</math> de degrè 2
<math>\(x^2 + 8.5 x^{13}\)</math> de degré 13
<math>\(-34 x^4\)</math> de degré 4
<math>\(4\)</math> de degré 0
Les expressions suivantes ne sont pas des polynomes:
L'évaluation d'un polynome consiste à calculer sa valeur pour un nombre <math>\(x\)</math> donné. Par exemple si <math>\(P(x) = x^2 - 2x + 12\)</math>, l'évaluation du polynôme en <math>\(x=3\)</math> donne:
<math>\(P(3) = 3^2 - 2\cdot3 + 12 = 15\)</math>
On dit que deux polynômes sont égaux si tous leurs coefficients sont égaux un par un.
On définit des opérations sur les polynomes de la manière suivante:
L'addition et la soustraction se font en additionant et soustrayant terme par terme. Par exemple: <math>\((x^2 - 2x + 12) + (1 + 3x + x^2) = 2x^2 + x + 13\)</math> <math>\((x^2 - 2x + 12) - (1 + 3x + x^2) = 5x + 11\)</math>
La multiplication s'effectue en utilisant les règles usuelles de l'algèbre. <math>\((x^2 - 2x + 12) \cdot (1 + 3x + x^2) = x^4 + x^3 + 7x^2 + 34x + 12\)</math>
Ce qui peut devenir très vite fastidieux si le degré du polynôme est grand. (Voilà une bonne raison d'implémenter une telle classe.
Le cas de la division est plus complexe. Comme pour les entiers, il s'agit d'une division avec reste.
Cela se fait en utilisant une division Euclidienne.
Si d'autres explications sont nécessaires, merci de me le dire et je développerai.
L'exercice
Le but de cet exercice est de réaliser une classe Polynome qui permettera de représenter la notion de polynome mathématique.
Niveau 1
Votre classe devra fournir les services suivants:
1) Construction par défaut créant un polynome de degré 0 avec 0 comme seul coefficient.
2) Construction à partir d'un nombre réel (polynôme de degré 0)
3) Surcharge des opérateurs arithmétiques usuels (+,-,*). Pensez également à implémenter les versions +=,-= et *= des opérateurs. Vous pouvez également réfléchir à la question des opérateurs du type "Polynome + Reel" ou "Reel + Polynome".
4) Surcharger l'opérateur << pour l'affichage dans un flux.
Votre Polynome devra toujours s'afficher en ne montrant pas les termes dont les coefficients sont 0. Sauf si le Polynome est le Polynome de degré 0 avec seulement 0 comme coefficient.
6) Surcharger les opérateurs de comparaison (==,!=).
7) Proposer une fonction "evaluer" qui permet d'évaluer le Polynome en un point x donné.
8) Proposer un moyen de connaître le degré du Polynome.
9) Proposer un moyen de connaître et de modifier chacun des coefficients du Polynome.
Niveau 2
Ce niveau consiste à ajouter la division euclidienne de polynômes. Pour cela, il vous faudra implémenter les opérateurs /,%,/= et %=.
Niveau 3
Pour le niveau 3, je vous propose de généraliser la notion de polynôme à n'importe quel type et pas seulement aux réels. Pour cela, quoi de vieux que d'utiliser les templates ?
Votre classe devra répondre au critère du niveau 2 tout en étant implémentée de sorte qu'elle soit utilisable avec n'improte quel type d'objet (mathématique).
Elle devra en particulier fonctionner avec les types suivants: int, Fraction, BigInt ou encore la classe complex de la SL.
Comment commencer ?
Si vous ne savez pas commencer, lisez les différents messages secrets.
Les coefficients forment une liste de nombres. Pourquoi ne pas les représenter sous forme de tableau ?
Commencez par implémenter l'opérateur << afin de vérifier ce qui se passe.
La division (et modulo) est le plus difficile, faites le reste en premier.
Pour toute information, je vous laisse revoir vos cours de maths ou Wikipedia par exemple. Vous pouvez aussi poser vos questions dans ce thread si la donnée n'est pas assez claire.
Vous avez jusqu'au 30 avril pour soumettre vos réponses à Réponse_Exercices.
Il y a juste un truc qui m'intrigue pour les polynômes, il s'agit de la comparaison de deux polynômes.
Pour comparer deux flottants, je crois qu'il est impossible d'utiliser == et !=, il faut se donner une erreur.
Mais comment généraliser ça à un type template ?
J'ai absolument rien pigé. Suite de puissances, ok, ça peut aller, mais pondérées pas un coefficient... eh ben je laisse tomber cet exo, trop dur pour moi (c'est quel niveau ça ? première, un truc comme ça non ?)
Il y a juste un truc qui m'intrigue pour les polynômes, il s'agit de la comparaison de deux polynômes.
Pour comparer deux flottants, je crois qu'il est impossible d'utiliser == et !=, il faut se donner une erreur.
Mais comment généraliser ça à un type template ?
Théoriquement, la classe Polynome ne fera qu'utiliser l'opérateur == surchargé de la classe utilisée comme type. Le problème est donc reporté vers la classe utilisée comme type.
Pour les types POD (double, float, ...), on peut toujours spécialiser la fonction.
Citation : Germanof
J'ai absolument rien pigé. Suite de puissances, ok, ça peut aller, mais pondérées pas un coefficient... eh ben je laisse tomber cet exo, trop dur pour moi (c'est quel niveau ça ? première, un truc comme ça non ?)
C'est plus difficile que les autres oui. J'ai aucune idée de quand on apprend ce qu'est un polynôme en France, mais à part la division, il n'y a pas grand chos ede compliqué si tu regardes les exemples de Wikipédia pour t'aider.
Les polynomes dans R[X] ou dans C[X], c'est bac +1, mais l'exo est accessible à un élève de première ou terminale, sauf p-e pour la division de polynome.
lmghs >> je confirme. En 1er S, on voit les fonctions polynomiales de degré 2 à coefficients réels avec des solutions dans R.
En Ts, les solutions sont abordées dans C mais les coeffs restent réels. C'est en sup que les polynomes sont vraiment abordés avec de vrais définitions, l'arithmétique des polynomes, tous les trucs fun
Pour moi, Lyon, les polynômes je les ai vu en 1ère dans R, puis en terminale pour C.
On n'a abordé la division qu'en BAC + 1 (1er semestre)
Sinon, sympa l'exercice
*
Nan, en 1er, tu as vu en 1er des fonctions polynomiale de degré 2 à coefficients réels à valeur dans R.
Ce n'est pas un polynome de degré N dans R[X] ou dans C[X]
Regarde en détail ici ce qu'on fait avec des polynomes.
Je sais de quoi je parle... Je viens de vérifier, j'ai vu des polynôme à degrée N (d'une manière générale, mais plus détaillé pour les 2 premiers degrés) dans C en terminale
Je sais de quoi je parle... Je viens de vérifier, j'ai vu des polynôme à degrée N (d'une manière générale, mais plus détaillé pour les 2 premiers degrés) dans C en terminale
A moins que t'aies 50 ans ou plus , ça m'étonnerait qu'il ya ait quoi que ce soit de bien consistant... T'as quoi dans ton cours de Term sur les polynomes de degré quelconque ? La définition d'une fonction polynomiale ? Doit pas y avoir grand chose de plus, t'arriveras pas à me faire croire que t'as fait de l'arithmétique dans <math>\(\mathbb{C}[X]\)</math> en terminale...
Sujet très intéressant, je pense que je vais participer pour une fois !
Je sais pas trop où tu vas chercher tes idées Nanoc mais chapeau, c'est pas toujours évident de trouver des truc sympa à faire.
Pour ce qui est des études, je suis d'acc on définit rapidement ce qu'est un polynômes en 1er mais on n'étudie vraiment que ce de degré 2.
Bonjour tout le monde ! Il est temps que je dévoile après beaucoup de retard, une solution pour l'exercice du mois de février. Vous avez été 22 à m'envoyer une solution. Parmi celles reçues, plusieurs n'effectuaient pas les conversions de manières correctes et pour les autres, c'était bien trop complexe même si cela fonctionnait.
Voici une solution possible.
Partie "Papier-Crayon"
Comme c'est principalement un exercice d'algorithmique, il est important de commencer par réféchir avec une feuille de papier avant de commencer à taper quoi que ce soit sur son clavier.
Des nombres arabes vers les nombres romains...
Il y a plusieurs approches possibles à ce problème. J'en ai choisi une qui me paraît simple à comprendre. On effectue le tout en 2 étapes.
Premièrement on convertit "bêtement" le nombre arabe en une chaîne de symboles romains sans tenir compte des règles de soustraction ni des symboles 500, 50 et 5. Cela donne par exemple:
3842 -> MMMCCCCCCCCXXXXII
On passe ensuite dans une étape de simplification qui va remplacer les longues chaines par des soustractions et/ou remplacer 5 symboles consécutifs par le symbole correspondant. Par exemple:
CCCCCCC -> DCC
XXXX -> XL
IIIIIIIII -> IX
Notre exemple initial devient donc:
3842 -> MMMCCCCCCCCXXXXII -> MMMDCCCXLII
De cette manière, on a toujours la solution correcte et la plus courte.
...et le contraire
Le sens opposé est un petit peu plus compliqué si l'on veut tenir compte du fait que l'utilisateur peut faire des erreurs.
La première chose à vérifier et que l'utilisateur n'a pas rentré de symboles non-autorisés.
XXCCDM -> Ok (pour le moment)
CCCA -> Erreur
Ensuite, on va parcourir la chaine de droite à gauche et comparer chaque "symbole" avec celui qui est à sa droite (si il y en a un). Le caractère courant est représenté en rouge.
MMCXII
Si le symbole a une valeur plus élevée ou égale à son voisin de droite et que la valeur n'est pas plus petite que tout les symboles placés à droite, alors c'est OK. (Rappellez-vous que les symboles doivent être placé dans l'ordre décroissant)
MMCXII -> X est plus grand que I -> Ok CCM -> C est égal à C MAIS est plus petit que M -> Ordre non-décroissant
MMXM -> X est plus petit que M -> voir la suite
Si par contre le symbole a une valeur inférieur, c'est soit que l'utilisateur a fait une erreur ou alors on a une soustraction.
On vérifie alors que le symbole est un des 6 cas de soustraction autorisé (IV, IX, XL, XC, CD, CM)
MMXM -> XM -> Erreur
MMCM -> CM -> Ok
Et on passe au caractère suivant.
Choix des structures de données
Pour contenir un nombre romain, quoi de mieux qu'une simple chaîne de caractères (std::string) ?
Pour contenir les valeurs de chaques symboles, j'ai choisi d'utiliser un tableau d'entier.
Il est temps de passer à la programmation, ce qui revient à traduire ce qu'on a écrit ci-dessus en C++. J'ai choisi de jouer avec les exceptions pour signaler les erreurs.
Des nombres arabes vers les nombres romains...
La première partie est simple et je pense ne pas avoir besoin de l'expliquer.
string arabToRoman(int arabe)
{
//On commence par tester si le nombre est valide
if (arabe < 1 || arabe > 4999)
throw string("Le nombre doit être compris entre 1 et 4999");
string romain; //La chaine que l'on va renvoyer
int i; //Compteur
//On compte le nombre de 'M' à écrire
for (i = 0; i<arabe/1000; ++i)
romain += "M";
//On supprime les milliers du nombre. A partir de la, arabe est <1000
arabe -= i*1000;
//On compte le nombre de 'C' à écrire
for (i = 0; i<arabe/100; ++i)
romain += "C";
//On supprime les centaines du nombre. A partir de la, arabe est <100
arabe -= i*100;
//On compte le nombre de 'X' à écrire
for (i = 0; i<arabe/10; ++i)
romain += "X";
//On supprime les dizaines du nombre. A partir de la, arabe est <10
arabe -= i*10;
//On compte le nombre de 'I' à écrire
for (i = 0; i<arabe; ++i)
romain += "I";
A partir de là, il reste à simplifier. Pour ce faire, il va falloir compter le nombre de "C" qui se suivent et remplacer par la chaîne correspondante. Puis recommencer avec "X" et "I".
Pour s'aider à la tâche, on utilise les fonctions membres de la classe string.
La fonction rfind cherche la dernière occurence d'un symbole et la fonction find renvoit la première occurence. La différence entre les deux nous donne le nombre de symboles.
La fonction replace remplace une partie de la chaîne de longueur passée en argument par une autre chaîne.
//Simplification ---------------------------------------------------------
//On remplace les "C" par ce qu'il faut pour simplifier
switch (romain.rfind('C') - romain.find('C') + 1)
{
case 9:
romain.replace(romain.find('C'), 9, "CM");
break;
case 8:
romain.replace(romain.find('C'), 8, "DCCC");
break;
case 7:
romain.replace(romain.find('C'), 7, "DCC");
break;
case 6:
romain.replace(romain.find('C'), 6, "DC");
break;
case 5:
romain.replace(romain.find('C'), 5, "D");
break;
case 4:
romain.replace(romain.find('C'), 4, "CD");
break;
default:
break;
}
//On remplace les "X" par ce qu'il faut pour simplifier
switch (romain.rfind('X') - romain.find('X') + 1)
{
case 9:
romain.replace(romain.find('X'), 9, "XC");
break;
case 8:
romain.replace(romain.find('X'), 8, "LXXX");
break;
case 7:
romain.replace(romain.find('X'), 7, "LXX");
break;
case 6:
romain.replace(romain.find('X'), 6, "LX");
break;
case 5:
romain.replace(romain.find('X'), 5, "L");
break;
case 4:
romain.replace(romain.find('X'), 4, "XL");
break;
default:
break;
}
//On remplace les "I" par ce qu'il faut pour simplifier
switch (romain.rfind('I') - romain.find('I') + 1)
{
case 9:
romain.replace(romain.find('I'), 9, "IX");
break;
case 8:
romain.replace(romain.find('I'), 8, "VIII");
break;
case 7:
romain.replace(romain.find('I'), 7, "VII");
break;
case 6:
romain.replace(romain.find('I'), 6, "VI");
break;
case 5:
romain.replace(romain.find('I'), 5, "V");
break;
case 4:
romain.replace(romain.find('I'), 4, "IV");
break;
default:
break;
}
//Et on renvoit le resultat
return romain;
}
Vous remarquerez que les deux morceaux de code présentés sont très répétitifs et que la seule différence entre les différents cas est le symbole et la valeur de ce dernier. On peut donc raccourcir le code précédent en utilisant les constantes que nous avons définies précédemment.
string arabToRoman(int arabe)
{
//On commence par tester si le nombre est valide
if (arabe < 1 || arabe > 4999)
throw string("Le nombre doit être compris entre 1 et 4999");
//La chaine que l'on va renvoyer
string romain;
for (int j=0; j<NOMBRES_SYMBOLES; j+=2)
{
int i=0;
//On compte le nombre de symboles d'un type donné à écrire
for (i = 0; i<arabe/VALEURS_SYMBOLES[j]; ++i)
romain += SYMBOLES[j];
//On supprime ce qu'on a ajouté du nombre arabe
arabe -= i*VALEURS_SYMBOLES[j];
}
//Simplifcation---------------------------------------------------
for (int j=2; j<NOMBRES_SYMBOLES; j+=2)
{
//On crée une flux sur une chaine de caractère pour pouvoir écrire des nombres dedans.
ostringstream flux;
//On remplace les symboles repetes par les chaines raccourcies
switch (romain.rfind(SYMBOLES[j]) - romain.find(SYMBOLES[j]) + 1)
{
case 9:
flux << SYMBOLES[j] << SYMBOLES[j-2];
romain.replace(romain.find(SYMBOLES[j]), 9, flux.str());
break;
case 8:
flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 8, flux.str());
break;
case 7:
flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 7, flux.str());
break;
case 6:
flux << SYMBOLES[j-1] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 6, flux.str());
break;
case 5:
flux << SYMBOLES[j-1];
romain.replace(romain.find(SYMBOLES[j]), 5, flux.str());
break;
case 4:
flux << SYMBOLES[j] << SYMBOLES[j-1];
romain.replace(romain.find(SYMBOLES[j]), 4, flux.str());
break;
default:
break;
}
}
//On renvoit la chaine
return romain;
}
L'avantage de cette fonction plus courte c'est qu'elle est aussi plus générique. Si les romains décident un jour d'ajouter plus de symboles à leurs nombres (On sait jamais hein !) et bien on aura pas besoin de toucher la fonction mais juste les tableaux définis avant.
...et le contraire
La fonction inverse n'est pas plus compliquée à écrire une fois que l'on a posé l'algorithme sur papier. La seule astuce et d'utiliser la fonction de conversion arabe->romain pour vérifier que le nombre romain que l'utilisateur a entré est bien la solution la plus courte.
int romanToArab(const string& roman)
{
//On verifie que la chaine ne contient que des symboles autorises
if (roman.find_first_not_of(SYMBOLES) != string::npos)
throw string("Symbole incorrect dans le nombre (Règle 0)");
int arabe = 0;
int derniereValeur = 0;
int plusGrandeValeur = 0;
//On parcourt la chaine a l'envers
for (int pos = roman.size()-1; pos>=0; --pos)
{
//On cherche la valeur du symbole actuel
const int valeur = VALEURS_SYMBOLES[SYMBOLES.find(roman[pos])];
//Si le symbole actuel est plus grand que celui a sa droite on additione le symbole
if (valeur >= derniereValeur)
{
//Si le symbole est plus petit que un de ceux à sa droite, c'est une erreur
if(valeur < plusGrandeValeur)
throw string("Ordre des symboles incorrects (Règle 1)");
//Sinon, c'est bon. On additionne donc
arabe += valeur;
}
//Sinon on est dans le cas d'une "soustraction"
else
{
//On verifie que la soustraction est valable
if (derniereValeur != 5*valeur && derniereValeur != 10*valeur)
throw string("Soustraction invalide (Règle 3)");
//Si c'est bon on retranche
arabe -= valeur;
}
derniereValeur = valeur;
if(valeur > plusGrandeValeur)
plusGrandeValeur = valeur;
}
//On verifie que le nombre que l'utilisateur a entre n'était en fait pas trop grand
if (arabe < 1 || arabe > 4999)
throw string("Le nombre doit être compris entre 1 et 4999 (Règle 4)");
//On vérifie si c'était bien la version la plus courte
if (roman != arabToRoman(arabe))
{
ostringstream flux;
flux << "Le nombre doit être écrit dans sa forme la plus courte (Règle 5)\nLa version correcte est: " <<arabToRoman(arabe);
flux << ". Ce qui donne " << arabe << " en chiffres arabes.";
throw flux.str();
}
return arabe;
}
N'hésitez pas à demander des détails, si vous en avez besoin. Voici finalement le code source complet avec un main() pour tester.
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
const string SYMBOLES = "MDCLXVI";
const int NOMBRES_SYMBOLES = 7;
const int VALEURS_SYMBOLES[NOMBRES_SYMBOLES] = {1000, 500, 100, 50, 10, 5, 1};
string arabToRoman(int arabe)
{
//On commence par tester si le nombre est valide
if (arabe < 1 || arabe > 4999)
throw string("Le nombre doit être compris entre 1 et 4999");
//La chaine que l'on va renvoyer
string romain;
for (int j=0; j<NOMBRES_SYMBOLES; j+=2)
{
int i=0;
//On compte le nombre de symboles d'un type donné à écrire
for (i = 0; i<arabe/VALEURS_SYMBOLES[j]; ++i)
romain += SYMBOLES[j];
//On supprime ce qu'on a ajouté du nombre arabe
arabe -= i*VALEURS_SYMBOLES[j];
}
//Simplifcation---------------------------------------------------
for (int j=2; j<NOMBRES_SYMBOLES; j+=2)
{
//On crée une flux sur une chaine de caractère pour pouvoir écrire des nombres dedans.
ostringstream flux;
//On remplace les symboles repetes par les chaines raccourcies
switch (romain.rfind(SYMBOLES[j]) - romain.find(SYMBOLES[j]) + 1)
{
case 9:
flux << SYMBOLES[j] << SYMBOLES[j-2];
romain.replace(romain.find(SYMBOLES[j]), 9, flux.str());
break;
case 8:
flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 8, flux.str());
break;
case 7:
flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 7, flux.str());
break;
case 6:
flux << SYMBOLES[j-1] << SYMBOLES[j];
romain.replace(romain.find(SYMBOLES[j]), 6, flux.str());
break;
case 5:
flux << SYMBOLES[j-1];
romain.replace(romain.find(SYMBOLES[j]), 5, flux.str());
break;
case 4:
flux << SYMBOLES[j] << SYMBOLES[j-1];
romain.replace(romain.find(SYMBOLES[j]), 4, flux.str());
break;
default:
break;
}
}
//On renvoit la chaine
return romain;
}
int romanToArab(const string& roman)
{
//On verifie que la chaine ne contient que des symboles autorises
if (roman.find_first_not_of(SYMBOLES) != string::npos)
throw string("Symbole incorrect dans le nombre (Règle 0)");
int arabe = 0;
int derniereValeur = 0;
int plusGrandeValeur = 0;
//On parcourt la chaine a l'envers
for (int pos = roman.size()-1; pos>=0; --pos)
{
//On cherche la valeur du symbole actuel
const int valeur = VALEURS_SYMBOLES[SYMBOLES.find(roman[pos])];
//Si le symbole actuel est plus grand que celui a sa droite on additione le symbole
if (valeur >= derniereValeur)
{
//Si le symbole est plus petit que un de ceux à sa droite, c'est une erreur
if(valeur < plusGrandeValeur)
throw string("Ordre des symboles incorrects (Règle 1)");
//Sinon, c'est bon. On additionne donc
arabe += valeur;
}
//Sinon on est dans le cas d'une "soustraction"
else
{
//On verifie que la soustraction est valable
if (derniereValeur != 5*valeur && derniereValeur != 10*valeur)
throw string("Soustraction invalide (Règle 3)");
//Si c'est bon on retranche
arabe -= valeur;
}
derniereValeur = valeur;
if(valeur > plusGrandeValeur)
plusGrandeValeur = valeur;
}
//On verifie que le nombre que l'utilisateur a entre n'était en fait pas trop grand
if (arabe < 1 || arabe > 4999)
throw string("Le nombre doit être compris entre 1 et 4999 (Règle 4)");
//On vérifie si c'était bien la version la plus courte
if (roman != arabToRoman(arabe))
{
ostringstream flux;
flux << "Le nombre doit être écrit dans sa forme la plus courte (Règle 5)\nLa version correcte est: " <<arabToRoman(arabe);
flux << ". Ce qui donne " << arabe << " en chiffres arabes.";
throw flux.str();
}
return arabe;
}
int main()
{
try
{
cout << "Conversion de 3593: " << flush;
cout << arabToRoman(3593) << endl;
cout << "Conversion de MMDCCM: " << flush;
cout << romanToArab("MMDCCM") << endl;
}
catch (string& e)
{
cerr << "\nERREUR: " << e << endl;
}
return 0;
}
Posez des questions si vous avez besoin de plus de détails !
Pour aller plus loin....
(beaucoup plus loin)
Dans un post précédent, j'avais lancer une petite pique sans penser que quelqu'un prendrait cela au sérieux. C'était compter sans Hunlyxxod qui a relevé le défi.
Il a réalisé cet exercice entièrement en métaprogrammation. Bravo à lui.
Voici son code:
#include <iostream>
/*****************************************************************************/
/* Template utilitaire
/*****************************************************************************/
// Classe représentant un entier
template <int _Number>
struct Integer
{
enum { Value = _Number };
};
// Classe représentant une liste
template <class _Data, class _Next>
struct List
{
typedef _Data Data;
typedef _Next Next;
};
// Converti une liste de caractères en chaîne de caractères
template <class _List>
struct String
{
String(): m_data(_List::Data::Value) {};
const char m_data;
String<typename _List::Next> m_next;
private:
String<_List> & operator =(const String<_List> &);
};
template <>
struct String<void>
{
String(): m_data('\0') {};
const char m_data;
private:
String<void> & operator =(const String<void> &);
};
// Macros pour faciliter l'écriture de chaînes de caractères
#define EXPAND(args) args
#define METASTRINGNULL void
#define METASTRING1(c) List<Integer<c>, METASTRINGNULL>
#define METASTRING2(c, ...) List<Integer<c>, EXPAND(METASTRING1(__VA_ARGS__))>
#define METASTRING3(c, ...) List<Integer<c>, EXPAND(METASTRING2(__VA_ARGS__))>
#define METASTRING4(c, ...) List<Integer<c>, EXPAND(METASTRING3(__VA_ARGS__))>
#define METASTRING5(c, ...) List<Integer<c>, EXPAND(METASTRING4(__VA_ARGS__))>
#define METASTRING6(c, ...) List<Integer<c>, EXPAND(METASTRING5(__VA_ARGS__))>
#define METASTRING7(c, ...) List<Integer<c>, EXPAND(METASTRING6(__VA_ARGS__))>
#define METASTRING8(c, ...) List<Integer<c>, EXPAND(METASTRING7(__VA_ARGS__))>
#define METASTRING9(c, ...) List<Integer<c>, EXPAND(METASTRING8(__VA_ARGS__))>
#define METASTRING10(c, ...) List<Integer<c>, EXPAND(METASTRING9(__VA_ARGS__))>
#define METASTRING11(c, ...) List<Integer<c>, EXPAND(METASTRING10(__VA_ARGS__))>
#define METASTRING12(c, ...) List<Integer<c>, EXPAND(METASTRING11(__VA_ARGS__))>
#define METASTRING13(c, ...) List<Integer<c>, EXPAND(METASTRING12(__VA_ARGS__))>
#define METASTRING14(c, ...) List<Integer<c>, EXPAND(METASTRING13(__VA_ARGS__))>
#define METASTRING15(c, ...) List<Integer<c>, EXPAND(METASTRING14(__VA_ARGS__))>
#define METASTRING16(c, ...) List<Integer<c>, EXPAND(METASTRING15(__VA_ARGS__))>
/*****************************************************************************/
/* Définitions pour les chiffres romains
/*****************************************************************************/
// Structure contenant les informations d'un symbole romain
template <char _Letter, unsigned _Number, class _Subtract = void >
struct RomanSymbol
{
typedef Integer<_Letter> Letter;
typedef Integer<_Number> Number;
typedef _Subtract Subtract;
};
// Symboles romains utilisés
typedef RomanSymbol<'I', 1> I;
typedef RomanSymbol<'V', 5, I> V;
typedef RomanSymbol<'X', 10, I> X;
typedef RomanSymbol<'L', 50, X> L;
typedef RomanSymbol<'C', 100, X> C;
typedef RomanSymbol<'D', 500, C> D;
typedef RomanSymbol<'M', 1000, C> M;
// Liste contenant tous les symbols romains utilisés
typedef List<M, List<D, List<C, List<L, List<X, List<V, List<I, void> > > > > > > RomanSymbols;
/*****************************************************************************/
/* Conversion arabes -> romains
/*****************************************************************************/
// ConvertToRoman:
// Convertit _Number en _Symbols
template <int _Number, class _Symbols>
struct ConvertToRoman;
// ConvertToRomanSubtract:
// Essayes d'insérer une soustraction dans la conversion de _Number en _Symbols
template <int _Number, class _Symbols>
struct ConvertToRomanSubtract;
// ConvertToRomanSubtract_Test<true>
// Insérer une soustraction est possible
template <bool _Test, int _Number, class _Symbols>
struct ConvertToRomanSubtract_Test
{
typedef
List<
typename _Symbols::Data::Subtract::Letter,
List<
typename _Symbols::Data::Letter,
typename ConvertToRoman<
_Number - _Symbols::Data::Number::Value + _Symbols::Data::Subtract::Number::Value,
typename _Symbols::Next>::Result > >
Result;
};
// ConvertToRomanSubtract_Test<false>
// Pas possible de placer une soustraction, converti le nombre avec les symboles suivants
template <int _Number, class _Symbols>
struct ConvertToRomanSubtract_Test<false, _Number, _Symbols>
{
typedef typename ConvertToRoman<_Number, typename _Symbols::Next>::Result Result;
};
template <int _Number, class _Symbols>
struct ConvertToRomanSubtract
{
typedef
typename ConvertToRomanSubtract_Test<
(_Number >= _Symbols::Data::Number::Value - _Symbols::Data::Subtract::Number::Value),
_Number,
_Symbols>::Result
Result;
};
// ConvertToRoman_Test<true>
// Le premier symbole est insérable en début de la chaîne de caractères,
// on l'insère et on converti le reste du nombre
template <bool _Test, int _Number, class _Symbols>
struct ConvertToRoman_Test
{
typedef
List<
typename _Symbols::Data::Letter,
typename ConvertToRoman<_Number - _Symbols::Data::Number::Value, _Symbols>::Result
> Result;
};
// ConvertToRoman_Test<false>
// Le premier symbole n'est pas insérable,
// on essaye avec une soustraction
template <int _Number, class _Symbols>
struct ConvertToRoman_Test<false, _Number, _Symbols>
{
typedef
typename ConvertToRomanSubtract<_Number, _Symbols>::Result
Result;
};
template <int _Number, class _Symbols>
struct ConvertToRoman
{
typedef
typename ConvertToRoman_Test<
(_Number >= _Symbols::Data::Number::Value),
_Number,
_Symbols >::Result
Result;
};
// Condition de fin:
// Le nombre est 0, il n'y a plus rien à convertir
template <class _Symbols>
struct ConvertToRoman<0, _Symbols>
{
typedef void Result;
};
/*****************************************************************************/
/* Conversion romains -> arabes
/*****************************************************************************/
template <char _Symbol>
struct SymbolNotValid;
// ConvertToArabic:
// Convertit la liste de symboles _String en nombre
template <class _String, class _Symbols>
struct ConvertToArabic;
// ConvertToArabicSubtract:
// Teste si les premiers symboles de _String sont une soustraction
template <class _String, class _Symbols>
struct ConvertToArabicSubtract;
// ConvertToArabicSubtract_Test<true>
// Les deux premiers symboles sont une soustraction,
// on converti les symboles suivants et on l'ajoute à la soustraction
template <bool _Test, class _String, class _Symbols>
struct ConvertToArabicSubtract_Test
{
enum {
Value = _Symbols::Data::Number::Value - _Symbols::Data::Subtract::Number::Value +
ConvertToArabic<typename _String::Next::Next, typename _Symbols::Next>::Value
};
};
// ConvertToArabicSubtract_Test<false>
// Les deux premiers symboles ne sont pas une soustraction
template <class _String, class _Symbols>
struct ConvertToArabicSubtract_Test<false, _String, _Symbols>
{
enum {
Value = ConvertToArabic<_String, typename _Symbols::Next>::Value
};
};
template <class _String, class _Symbols>
struct ConvertToArabicSubtract
{
enum {
Value = ConvertToArabicSubtract_Test<
static_cast<int>(_String::Data::Value) == static_cast<int>(_Symbols::Data::Subtract::Letter::Value) &&
static_cast<int>(_String::Next::Data::Value) == static_cast<int>(_Symbols::Data::Letter::Value),
_String,
_Symbols>::Value
};
};
// La liste ne contient qu'un symbole, ça ne peut être une soustraction
template <class _Char, class _Symbols>
struct ConvertToArabicSubtract<List<_Char, void>, _Symbols>
{
enum {
Value = ConvertToArabic<List<_Char, void>, typename _Symbols::Next>::Value
};
};
// Le symbole à tester n'accepte pas de soustraction, on passe le test
template <class _String, char _Letter, unsigned _Number, class _Next>
struct ConvertToArabicSubtract<_String, List<RomanSymbol<_Letter, _Number, void>, _Next> >
{
enum {
Value = ConvertToArabic<_String, _Next>::Value
};
};
// Si on a passé tous les symboles sans succès, on signale une erreur
template <class _String>
struct ConvertToArabicSubtract<_String, void>
{
enum { Error = sizeof(SymbolNotValid<_String::Data::Value>) };
};
// ConvertToArabic_Test<true>
// On a trouvé le premier symbole de _String, on fait la conversion,
// et on passe au suivant
template <bool _Test, class _String, class _Symbols>
struct ConvertToArabic_Test
{
enum {
Value = _Symbols::Data::Number::Value + ConvertToArabic<typename _String::Next, _Symbols>::Value
};
};
// ConvertToArabic_Test<false>
// Le premier symbole de _String n'est pas le premier symbole de _Symbols,
// on vérifie que ce ne soit pas une soustraction
template <class _String, class _Symbols>
struct ConvertToArabic_Test<false, _String, _Symbols>
{
enum {
Value = ConvertToArabicSubtract<_String, _Symbols>::Value
};
};
template <class _String, class _Symbols>
struct ConvertToArabic
{
enum {
Value = ConvertToArabic_Test<static_cast<int>(_String::Data::Value) == static_cast<int>(_Symbols::Data::Letter::Value), _String, _Symbols>::Value
};
};
// On a parcouru toutes la liste de symbole avec succès
template <class _Symbols>
struct ConvertToArabic<void, _Symbols>
{
enum { Value = 0 };
};
// Un symbole de la liste n'a pas pu être trouvé, on signale une erreur
template <class _String>
struct ConvertToArabic<_String, void>
{
enum { Error = sizeof(SymbolNotValid<_String::Data::Value>) };
};
/*****************************************************************************/
/* Programme de test
/*****************************************************************************/
#define ARABIC_NUMBER 2938
#define ROMAN_NUMBER METASTRING8('M', 'C', 'M', 'L', 'X', 'I', 'X', 'V')
String<ConvertToRoman<ARABIC_NUMBER, RomanSymbols>::Result> ArabicString;
String<ROMAN_NUMBER> RomanString;
int main()
{
std::cout << ARABIC_NUMBER << " equivaut a " << &ArabicString.m_data << std::endl;
std::cout << &RomanString.m_data << " equivaut a " << ConvertToArabic<ROMAN_NUMBER, RomanSymbols>::Value << std::endl;
return 0;
}
Voilà. Amusez-vous bien avec l'exercice sur les polynômes. Les corections manquantes arrivent tout bientôt...
En première, on en sait déjà assez pour savoir faire tout l'exercice : multiplication, addition..., factorisation, unicité d'écriture d'un polynôme... Certes pas poussé ni formellement démontré et énoncé...
Les binômes, les trinômes, et parfois les polynômes de degré 3 sont étudiés (quadrinômes ?), surtout dans l'optique de résolution d'équations.
Cependant, le discours formaté de soutenir qu'on n'apprend plus rien à l'école de nos jours quand on l'a entendu dire, qu'on ne la connait que par certains répétent n'est pas entiérement vrai.